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 formulaFirst 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.
ConclusionThis 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.