Monday, August 27, 2007

Launching and debugging Portlets from Eclipse with Jetty and Pluto

Last time I showed how you can combine Jetty, Pluto and JWebUnit (or any other web testing framework for that matter) to easily write integration tests for your portlets. As a short follow up, I'll show you how to use the same setup to create a simple "Launcher" to get your portlets up and running quickly from Eclipse (but it can easily be modified to work in your favorite IDE).

The first thing to do is to add the usual dependencies to your pom.xml file (assuming you're using Maven 2 to build your portlet project. If not, add the dependencies to your classpath manually):

Update: The maven-jetty-pluto-embedded artifact is available in the Maven 2 repositories.



<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-2.1</artifactId>
<version>6.1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.bekk.boss</groupId>
<artifactId>maven-jetty-pluto-embedded</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>


(if you want to know what the maven-jetty-pluto-embedded artifact is, read this). Note that if you have any other dependencies that either directly or indirectly has a dependency to a different version of the servlet api that Jetty 6.1.5 is using (like commons-logging), you need to explicitly exclude this dependency, adding the following snippet inside the dependency:


<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>


If you have not already done so, run mvn eclipse:eclipse to add the dependencies to your Eclipse project. Next, create a new class called JettyPlutoLauncher (I put it in my src/test/java folder). We'll add a main method to this class, doing the exact same as the setUp() method of our base integration test described in the previous entry:


import org.apache.pluto.core.PortletServlet;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.jetty.webapp.WebAppContext;

public class JettyPlutoLauncher {
public static void main(String[] args) throws Exception {
System.setProperty("org.apache.pluto.embedded.portletId", "portletId");
Server server = new Server(8080);
WebAppContext webapp = new WebAppContext("src/main/webapp", "/contextRoot");
webapp.setDefaultsDescriptor("/WEB-INF/jetty-pluto-web-default.xml");
ServletHolder portletServlet = new ServletHolder(new PortletServlet());
portletServlet.setInitParameter("portlet-name", "portletId");
portletServlet.setInitOrder(1);
webapp.addServlet(portletServlet, "/PlutoInvoker/portletId");
server.addHandler(webapp);
server.start();
}
}


Replace all occurrences of portletId with the actual id of the portlet to run, and contextRoot with the context root you want for your application.

That's it. Now you can simply select your class and do "Run As - Java Application" to run, or "Debug As - Java Application" to do step by step debugging your portlet!

Saturday, August 18, 2007

Testing Portlets with Jetty, Pluto and JWebUnit

After my last two entries, I've gotten some questions about using pluto embedded in jetty to create automated integration tests for JSR 168 portlets. Using the maven-jetty-plugin for running the portlets is great for fast, iterative development. But it can't be used to run automated integration tests. Remembering an excellent article from Johannes Brodwall's blog about integration testing with Jetty and JWebUnit, I wanted to extend his approach to use the embedded jetty-pluto setup I have created. This turned out to be to be quite easy.

To illustrate the solution, let's start from scratch, creating a simple portlet displaying a personalized "Hello World" message (how original...). Note that this is only a very simple example, outlining how it is possible to set up the infrastructure for simple web integration testing of portlets.

The first step is to create a blank portlet project, using Maven 2 and the portlet archetype:


mvn archetype:create
-DgroupId=com.mycompany.app
-DartifactId=my-portlet
-DarchetypeArtifactId=maven-archetype-portlet


(Type this on one line)

I'll use Eclipse for this example, so I'll set up an Eclipse project for the portlet by typing mvn eclipse:eclipse. Now you can go ahead importing the project into Eclipse as an existing project.

Unfortunately, it appears that the portlet arhcetype contains a bug, so make sure you update the portlet-class element in the portlet.xml file with the correct portlet class:

<portlet-class>com.mycompany.app.MyPortlet</portlet-class>


Then we need some dependencies to Jetty, JWebUnit and the maven-jetty-pluto-embedded artifact, so add these to the dependencies section of the pom.xml file of the portlet project:


Update: The maven-jetty-pluto-embedded artifact is available in the Maven 2 repositories.



<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-2.1</artifactId>
<version>6.1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.jwebunit</groupId>
<artifactId>jwebunit-htmlunit-plugin</artifactId>
<version>1.4.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.bekk.boss</groupId>
<artifactId>maven-jetty-pluto-embedded</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>


(The JWebUnit dependency has an indirect dependency on commons-logging, which for some weird reason has a dependency on the servlet-api!? This version of the servlet-api causes problems for the version of jetty we're running, so we have to exclude it)

Run mvn eclipse:eclipse again to download the new dependencies. If you want to know what the maven-jetty-pluto-embedded artifact is, you should read my previous entries).

Open the MyPortlet portlet that was generated for us, and remove everything but the method signature for the doView() method, like this:


public class MyPortlet extends GenericPortlet {

public void doView( RenderRequest request, RenderResponse response ) {
}

}


Now we'll start preparing the test, and this is where the fun begins. We'll have to configure Jetty in our test to use the embedded jetty pluto setup from the previous articles. Create a new source folder, src/test/java, and create a new blank WebTestCase:

package com.mycompany.app;

import net.sourceforge.jwebunit.junit.WebTestCase;

public class MyPortletIntegrationTest extends WebTestCase {

}


We'll have to create a setUp() metohd that initializes the Jetty server, and configures it with the pluto driver:


protected Server server;

public void setUp() throws Exception {
System.setProperty("org.apache.pluto.embedded.portletId", "my-portlet");
server = new Server(8080);
WebAppContext webapp = new WebAppContext("src/main/webapp", "/test");
webapp.setDefaultsDescriptor("/WEB-INF/jetty-pluto-web-default.xml");
ServletHolder portletServlet = new ServletHolder(new PortletServlet());
portletServlet.setInitParameter("portlet-name", "my-portlet");
portletServlet.setInitOrder(1);
webapp.addServlet(portletServlet, "/PlutoInvoker/my-portlet");
server.addHandler(webapp);
server.start();
getTestContext().setBaseUrl("http://localhost:8080/test");
}

public void tearDown() throws Exception {
server.stop();
}


So what does all this mean? Basically, we have just copied the setup from the configuration of the maven-jetty-plugin that we used earlier, and configured the Jetty server programatically in our integration test. In addition, we've added and configured an instance of the pluto PortletServlet so we don't have to modify our web.xml file (which normally would be something our build takes care of). We also added a tearDown() method to gracefully shut down the server.

Now it's time to write an actual test. We'll start very, very simple, by just testing if the portlet displays a simple message when it is displayed normally in the view mode:


public void testIndexPageDisplaysMessage() throws Exception {
// Start at the pluto driver entry point
beginAt("/pluto/index.jsp");
assertTextPresent("Hello World from MyPortlet!");
}


Running the test, it will of course fail since we have not implemented anything in the portlet yet. So let's create an index.jsp file in the /WEB-INF folder of the portlet:


<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet"%>

Hello World from MyPortlet!


In the doView() method of the portlet, we'll just dispatch to this jsp:


public void doView( RenderRequest request, RenderResponse response ) throws IOException, PortletException {
getPortletContext().getRequestDispatcher("/WEB-INF/index.jsp").include(request, response);
}


Now, run the test again. This time, it should show a green bar! That was quick and simple! Let's try to complicate the case a little bit by adding and working with a form. The form should be a form with two text fields, one named firstName and a second named lastName. When submitting the form, the user should be displayed a personalized "Hello World" message:


public void testPersonalizedHelloWorldMessage() throws Exception {
// Start at the pluto driver entry point
beginAt("/pluto/index.jsp");
// Expect a form with name "helloWorldForm" present on the page
assertFormPresent("helloWorldForm");
// This form should have two text fields. We'll populate these with data
setWorkingForm("helloWorldForm");
setTextField("firstName", "Donald");
setTextField("lastName", "Duck");
submit();
assertTextPresent("Quack quack, Donald Duck!");
}


We populate the form with some data, submit it, and verify the result. Running the test now, you'll see it fails, as expected. So let's implement the expected functionality. Normally, I would use a MVC framework with portlet support, such as Struts 2 or Spring MVC, but since I'm only using the core APIs in this example, we'll have to use the portlet as a very basic controller. The portlet will handle the form submits and the logic for including the correct jsp. Let's start by adding the form to the index.jsp page:


<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet"%>

Hello World from MyPortlet!

<form name="helloWorldForm" action="<portlet:actionURL/>" method="POST">
<b>First name</b>: <input type="text" name="firstName"/><br/>
<b>Last name</b>: <input type="text" name="lastName"/><br/>
<input type="submit"/>
</form>


Then we'll implement the processAction() method and process the incoming form submit:


public void processAction(ActionRequest actionRequest, ActionResponse actionResponse) throws PortletException, IOException {
String firstName = actionRequest.getParameter("firstName");
String lastName = actionRequest.getParameter("lastName");
actionResponse.setRenderParameter("firstName", firstName);
actionResponse.setRenderParameter("lastName", lastName);
actionResponse.setRenderParameter("action", "viewResult");
}


Then we'll add some "routing logic" to the doView() method so it will dispatch us to the correct view:


public void doView( RenderRequest request, RenderResponse response ) throws IOException, PortletException {
String action = request.getParameter("action");
if("viewResult".equals(action)) {
getPortletContext().getRequestDispatcher("/WEB-INF/hello.jsp").include(request, response);
}
else {
getPortletContext().getRequestDispatcher("/WEB-INF/index.jsp").include(request, response);
}
}


And finally, the /WEB-INF/hello.jsp file that generates the output:


<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet"%>
<portlet:defineObjects/>

Quack quack, <%=renderRequest.getParameter("firstName") %> <%=renderRequest.getParameter("lastName") %>!


Run the test again, and it should become green!

Next, let's assume that the portlet should do something useful in the edit portlet mode. But how can we simulate a click on the edit button? After some looking though the source of the generated pluto html, I found the XPath expressions that identifies each of the portlet mode and window state buttons. With this information, let's write a test that switches the portlet to edit mode, and assert that we get to the correct page:


public void testSwitchToEditMode() throws Exception {
beginAt("/pluto/index.jsp");
switchEdit();
assertTextPresent("This is MyPortlet in edit mode!");
}

private void switchEdit() {
clickElementByXPath("//span[@class='edit']/..");
}


The switchEdit() method simulates a click on the edit mode control of the portlet, using the correct XPath expression.

Try to run the test, and it will turn red with a message stating that "Unable to locate element with XPath...". This is because pluto does not display the edit link for the portlet. Looking in the portlet.xml for the portlet, you can see that currently the only supported portlet mode is VIEW. So let's add EDIT to the supported modes as well:


<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
<portlet-mode>EDIT</portlet-mode>
</supports>


Running the test again, it will fail, but now with a different error message, stating that the "Expected text was not found". So it's time to implement the doEdit() method and the corresponding JSP. Create an edit.jsp file in the /WEB-INF folder, which just contains

This is MyPortlet in edit mode!


In the portlet, the doEdit() method should look like this:


public void doEdit(RenderRequest request, RenderResponse response) throws IOException, PortletException {
getPortletContext().getRequestDispatcher("/WEB-INF/edit.jsp").include(request, response);
}


This time when you run the test, it should turn green.

As you can see, writing integration tests for a portlet is pretty simple, using the right tools. But why this approach, instead of just running JWebUnit (or Selenium or <insert your favourite web testing framework here>) against a running portal server with the portlet installed? First of all, using this "in process" approach is quicker! It doesn't require you to start a big, heavy portal server, and no login is required. Asserting and verifying content is easier since the only portlet generating content is the one you're testing. And since it's in process, it's really simple to debug. Just run the JUnit test in debug mode, and you can debug the portlet as it responds to the request of the web testing framework.

As a final note, I have uploaded an abstract WebTestCase that you can download and use as a base for your integration tests. It has everything set up for starting Jetty with the pluto portal driver, and with helper methods for switching portlet modes and window states.

Sunday, August 05, 2007

maven-jetty-plugin and JSR168 portlets - Part 2

My last post outlined how I made the maven-jetty-plugin work with a maven 2 portlet project. As I mentioned, there were some issues with the approach if you're using Spring in your application. Also, there were some files you needed to include in your project. That's not too big of a problem solving using excludes in your pom so the files are not included in your build, but it's still a bit annoying having them around. And finally, there were no portlet controls, so changing modes and window states were impossible unless you manually created links in your portlet. So the solution had to improve...

The first obstacle to overtake was how to enable the controls for switching modes and window states. I thought this was going to be easy, but trial and (mostly) error should prove me wrong. It became clear to me that the approach of adding the pluto dependencies to the plugin instead of the application caused problems with loading a resource bundle that the pluto taglib required. I solved this temporarily by adding the ToolTips.properties file to the pluto-portal-driver-1.1.4.jar file. Good enough for now. This brought me one step further: The pluto:modeAnchor and pluto:windowStateAnchor tags now rendered correctly, but they required some stylesheets and javascript files that comes with the pluto web application to actually show the controls. This forced me to add a bunch of new files to my application, and it was really starting to pollute my project.

The next task was Spring support. Having Spring on the application- and plugin classpath will cause classloader problems (you'll get ClassCastExceptions since the Spring classes are loaded from two different classloaders). Another problem is that both your application and pluto requires the ContextLoaderListener and the contextConfigLocation context parameter to be set. Basically, to make this work with just maven-jetty-plugin configuration options would require me to split the configuration options into three separate files; a webdefaults descriptor, web.xml and an override descriptor. This is really starting to look bad...

Coming this far, I realized that the current solution wasn't going to be a very good. It would work, but it wasn't good enough. First of all, the classloading issues with the resource bundle and Spring classes convinced me that it was better adding the dependendices to the application instead of the plugin. I also needed to find a solution to the problem with the ContextLoaderListener. And last, I needed to get rid of all the stylesheets, images, javascripts, jsp files and configuration files from the application, into one single artifact that could be included as a dependency to the application. This means that I would have to serve all the required resources from one jar file.

The ContextLoaderListener problem was solved using a custom, jetty specific, context listener that use the internal jetty context classes to modify the context parameters and ensuring that only one context loader listener instance exists. I then added this listener to the webdefaults file, ensuring it is the very first listener that is executed. Quite brutal, but it works...

Identifying and collecting all the required stylesheets, JSP files, images etc into one jar file was easy. The hard part was how to make them available to the pluto driver, when they always were meant to be a part of the running web application... So all lookups/URLs to files like /pluto.css, /images/controls/edit.png, /WEB-INF/pluto-portal-driver-config.xml, and not to forget, the jsp that acts as the entry point to the portlet container (I've named it /pluto/index.jsp), would have to be served from this jar. Taking inspiration from the Struts 2 FilterDispatcher, serving static resources referenced from URLs to the web application was easy using a servlet filter. But how was I going to "intercept" the lookups to the resources in /WEB-INF? And how can one serve a JSP file from within a JAR file? Looking through the source of the RenderConfigServiceImpl (which loads the /WEB-INF/pluto-portal-driver-config.xml file) and the Jasper JspServlet, it appears that they both look up resources using ServletContext.getResource and ServletContext.getResourceAsStream. If I could intercept these calls and look up the resources from the classpath instead, the problem should be solved. So I wrote a wrapper servlet for the JspServlet, and a wrapper context listener for the PortalStartupListener (which in turn initializes the RenderConfigServiceImpl) that just delegates to the wrapped objects, passing them a wrapped instance of ServletContext that looks up the resources it does not find in /WEB-INF with the ClassLoader.getResource and ClassLoader.getResourceAsStream methods instead. Problem solved!

This left me with two resources; the pluto.tld file and the customized webdefaults descriptor. For the pluto.tld file, I just placed it in the META-INF folder of the jar file, and renamed it to taglib.tld. This is discovered by the servlet container automatically when it is referenced through the correct uri in the jsp files using it. One file left; the webdefaults descriptor. After a lot of trial and error (again...) (I even tried extending the maven-jetty-plugin to add a new configuration option, but this proved fruitless as I discovered that it is not possible to extend a maven 2 plugin...), I could not find a solution. So it remains unsolved until the maven-jetty-plugin is modified to look up a webdefaults descriptor by some other means than just a file path (I'll probably create a JIRA issue for this. Update: JIRA issue created). But still, I'm now left with only one jar file added to the dependency of the application, and one webdefaults file that I add to the application itself (and is easily excluded in the pom). I've also added the dependency to a profile, so it's only active when I specify this profile on the command line. It looks like this:


Update: The maven-jetty-pluto-embedded artifact is available in the Maven 2 repositories.



<profiles>
<profile>
<id>pluto-embedded</id>
<build>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<configuration>
<webXml>${project.build.directory}/pluto-resources/web.xml</webXml>
<webDefaultXml>src/main/webapp/WEB-INF/jetty-pluto-web-default.xml</webDefaultXml>
<systemProperties>
<systemProperty>
<name>org.apache.pluto.embedded.portletId</name>
<value>MyPortletAsDefinedInPortletXml</value>
</systemProperty>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.bekk.boss</groupId>
<artifactId>maven-jetty-pluto-embedded</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</profile>
</profiles>


Of course, you need the maven-pluto-plugin as well, as described in the previous post. Also, note the system property configuration specifying the id of the portlet to run.

I run this setup using the command mvn jetty:run -P pluto-embedded. Now I'm a lot more happy with the solution. Below is a screenshot how this looks running with the Struts 2 sample portlet application (click the image to enlarge):

maven jetty plugin running embedded with Pluto

You can download the jetty-pluto-web-default.xml file and try it yourself. Just put the xml file in your /WEB-INF folder. All feedback is appreciated.