Saturday, October 06, 2007

Easy portlet development and reduced roundtrip with the updated Struts 2 portlet archetype

The entry has been updated to reflect the new 2.1.1-SNAPSHOT release

I have just deployed an updated version of the Struts 2 portlet archetype, which is based on Struts 2.1.1. The archetype also includes the jetty-pluto integration which should reduce the roundtrip from development to testing and running quite a lot.

To create a basic Struts 2 portlet with the archetype, run the command

mvn archetype:create
-DgroupId=<groupId>
-DartifactId=<artifactId>
-DarchetypeGroupId=org.apache.struts
-DarchetypeArtifactId=struts2-archetype-portlet
-DarchetypeVersion=2.1.1-SNAPSHOT
-DremoteRepositories=
http://people.apache.org/repo/m2-snapshot-repository

(all on one line)

Just replace <groupId> and <artifactId> with your real groupId and artifactId. Now you can just type mvn jetty:run -P pluto-embedded to run the portlet in pluto. The pluto-embedded profile does the magic of setting up the integration between the maven-jetty-plugin and pluto. To test it, just open a browser and point it to http://localhost:8080/<artifactId>/pluto/index.jsp

I have also included a class called JettyPlutoLauncher (you might recognize it from a previous entry) in the test sources of the generated project. It is simply a class with a main method that can be used to launch the portlet from your IDE. To use it with eclipse simply type mvn eclipse:eclipse -P pluto-embedded, import the project in Eclipse, and select to run the JettyPlutoLauncher class. Then open a browser to the same URL as described above.

This will hopefully get you up and running with Struts 2 portlet development pretty quick!

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.

Tuesday, July 31, 2007

maven-jetty-plugin and JSR168 portlets

Inspired by Don Brown's Developing a Portlet using Eclipse tutorial for Struts 2 and some information I found on the Pluto FAQ, I started playing around with embedding pluto in a Maven 2 project. The goal was to be able to use the maven-jetty-plugin to run and test my portlets with Jetty just by typing mvn jetty:run. That would definitively be a step up to increase developer productivity!

After googling a lot and collecting information from various sources, I managed to get things up and running! Here's the steps needed to get the maven-jetty-plugin to work with your Maven 2 portlet project.

The formula

First of all, this only works with Pluto 1.1.4, which is not currently released, but probably will be soon. The reason is that Pluto 1.1.3 does not work with JSP 2.1. So the first thing to do is to get the Pluto sources from SVN from the pluto-1.1.4 tag and build it according to the istructions. This will install the pluto 1.1.4 artifacts in your local maven repository. Alternatively, I guess you could just add the apache maven staging repository to your pom.xml (which does sound a bit easier when I think of it...)

The next step is to add the maven-pluto-plugin to your pom.xml. This is needed to add the web.xml entries for the wrapper servlets for the portlet.

<plugin>
<groupId>org.apache.pluto</groupId>
<artifactId>maven-pluto-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>assemble</goal>
</goals>
</execution>
</executions>
</plugin>


And of course, we need the jetty plugin as well:


<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
</plugin>


Now, we need to add the dependencies needed by the pluto portlet container. I chose to add these to the plugin classpath, but you could also add them to the project dependencies instead. So within the scope of the plugin tag for the jetty plugin, add a section of dependencies:


<dependencies>
<dependency>
<groupId>org.apache.pluto</groupId>
<artifactId>pluto-portal-driver</artifactId>
<version>1.1.4</version>
</dependency>
<dependency>
<groupId>org.apache.pluto</groupId>
<artifactId>pluto-portal-driver-impl</artifactId>
<version>1.1.4</version>
<dependency>
<dependency>
<groupId>org.apache.pluto</groupId>
<artifactId>pluto-container</artifactId>
<version>1.1.4</version>
</dependency>
<dependency>
<groupId>org.apache.pluto</groupId>
<artifactId>pluto-taglib</artifactId>
<version>1.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<<version>2.0.2</version>
</dependency>
</dependencies>


(That's right, there's a Spring dependency in there, which can cause some troubles if you're using Spring already, but let's skip that for now...)

The maven-pluto-plugin generates a web.xml file and puts it in the ${project.build.directory}/pluto-resources/ folder. So we need to customize the configuration of the plugin a bit. So add this to the maven-jetty-plugin definition in the pom.xml:


<configuration>
<webXml>${project.build.directory}/pluto-resources/web.xml</webXml>
</configuration>


Still, that's unfortunately not entirely enough for the jetty plugin. We also need to add a context-parameter, filter, filter-mapping(s) and some servlet context listeners to the web.xml. Now, I don't want to pollute my original web.xml with this stuff, so I considered a couple of options. Either, I could add an overrideweb.xml file, which is added to the web application configuration after the jetty:run goal is invoked, or I could create a new webdefault xml file, which is applied before. After trying both options, I decided to go for the second solution (I'm not going into that now, but it's the only way I could get it to work with Spring in a way I was happy with). Therefore, I extracted the webdefault.xml file from the jetty jar file, and added the required cofigurations:


<!-- Spring configuration file for the portlet container services -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/pluto-portal-driver-services-config.xml</param-value>
</context-param>

<!-- The Spring context loader listener and the startup listener for the dummy portal -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.apache.pluto.driver.PortalStartupListener</listener-class>
</listener>

<!-- The pluto driver that will intercept our JSP "portal" -->
<filter>
<filter-name>plutoPortalDriver</filter-name>
<filter-class>org.apache.pluto.driver.PortalDriverFilter</filter-class>
</filter>

<!-- The filter will intercept our /test-portal/index.jsp file and everything beneath it -->
<filter-mapping>
<filter-name>plutoPortalDriver</filter-name>
<url-pattern>/test-portal/index.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>plutoPortalDriver</filter-name>
<url-pattern>/test-portal/index.jsp/*</url-pattern>
</filter-mapping>

<!-- The container taglibs that invoke our portlet in the portlet container -->
<jsp-config>
<taglib>
<taglib-uri>http://portals.apache.org/pluto</taglib-uri>
<taglib-location>/WEB-INF/tld/pluto.tld</taglib-location>
</taglib>
</jsp-config>


For now, I just put this new webdefault xml file (I named it jetty-pluto-web-default.xml) in the /WEB-INF folder of the application, and added a new configuration option for the jetty plugin:


<configuration>
<webDefaultXml>src/main/webapp/WEB-INF/jetty-pluto-web-default.xml</webDefaultXml>
<webXml>target/pluto-resources/web.xml</webXml>
</configuration>


Almost done now! We need three files from pluto. These are configuration- and taglib files that initialize the portlet container, and instead of listing them here, just download the pluto-portal-driver-config.xml and the pluto-portal-driver-services-config.xml and put them in the WEB-INF folder of your application. Then create a tld folder in the same folder, and download the pluto.tld file to this folder.

The last steps is to create a "dummy portal" entry point. I created a folder named test-portal in the src/main/webapp folder, and created an index.jsp file that acts as the entry point to the portlet container. The index.jsp file looks like this:


<%@ taglib uri="http://portals.apache.org/pluto" prefix="pluto" %>
<pluto:portlet portletId="/struts2-portlet.StrutsPortlet">
<div class="portlet" id="struts2-portlet.StrutsPortlet">
<div class="header">
<h2 class="title"><pluto:title></pluto:title></h2>
</div>
<div class="body">
<pluto:render></pluto:render>
</div>
</div>
</pluto:portlet>


The portletId in the <pluto:portlet> tag is composed of the context root of the web application, and the name of the portlet as defined in the portlet.xml file.

Now, everything should be set up, and you should be able to view your portlet by pointing your browser to http://localhost:8080/<context-root>/test-portal/index.jsp (note that you need to point to the index.jsp file, it will not work just pointing it to /test-portal/ due to the filter mappings).

If you don't want to do all these steps manually, I have created an archived maven project based on the struts2-archetype-portlet, as described in Don's tutorial, and added all the required files and settings.

Conclusion

This might seem like an awful lot of work to get the jetty plugin to work with portlets, but I believe it's worth the effort. I have never been more productive developing portlets than after I got this up and running. And when you have been through this once, just add it to your set of templates/archetypes so you won't have to do it again.

And please, if you find any errors in this article, please drop me a comment so I can correct them.

Update: See part 2 to see how the solution has improved.

Update 2: Pluto 1.1.4 has been released and should be available from the regular maven repositories.