Struts Bridge

Introduction

This document will explore how a Struts application can be modified to work as a JSR168 compliant portlet. It will hopefully serve as a useful reference guide for those planning to install the Struts Bridge into their existing Struts applications. As an example we'll use the iBATIS version of Petstore application, the Petstore is a commonly implemented demonstration of a fictitious e-commerce site. The iBATIS version of Petstore is built with the Struts framework and incidentally also provides an example of the iBATIS DAO framework and SQL Maps framework.

Struts Bridge: Background

The Struts Bridge is one of several software bridges from the Apache Portals Bridge project (at the time of writing the project also includes bridges for JSF, PHP, Velocity and Perl). The idea behind the Struts Bridge is that you can take an existing Struts application and make a couple of very minor modifications after which it will continue to work as a standalone Struts application but also work as a portlet.

The Portals Bridge project was initially developed within the Apache Jetspeed 2 portal platform project. Portals Bridge has now become a separate project; it was designed to run in any JSR168 compliant portal and contains nothing that should be specific to Jetspeed 2 portal.

JPetstore Example

iBATIS JPetstore is a relatively small demonstration Struts application. A portlet version of this application is distributed as an example with the Apache Jetspeed 2 portal platform. Attached to this page are two versions of the Jetspeed 2 portlet version of the JPetstore WAR file; one version is complied for use with J2SE 1.4.2 and one for J2SE 1.5. You will also find two zip files, one for each J2SE version, containing some source code and compiled versions of the libraries necessary to convert a Struts application into a portlet.

Changes that were necessary to convert the original iBATIS JPetstore into a portlet version

The iBATIS JPetstore portlet distributed with Jetspeed 2 is subtly different to the original iBATIS JPetstore example in a number of ways.

Struts upgrade, database configuration and general tidy up

The Struts Bridge code exists in two different versions, one for Struts 1.2.4 and one for Struts 1.2.7. The original JPetstore used Struts 1.1 and so it was necessary to update the Struts libraries.

In order to be easier to deploy and get the example up and running, the Jetspeed 2 JPetstore has been configured to use HSQLDB by default and includes the necessary HSQLDB library and database files.

Database configuration

As the JPetstore demo provides an embedded HSQLDB database.You will need to change the url property for your setup.

WEB-INF/classes/properties/database.properties

driver=org.hsqldb.jdbcDriver
url=jdbc:hsqldb:C:/Tomcat-5.5.9/webapps/jpetstore/WEB-INF/db/jpetstore
username=sa
password=

OR

You can delete the WEB-INF/classes/properties/database.properties file and the LocalHsqldbConfigurator class which implements ServletContextListener

will automatically recreate this file while initializing the context the next time you restart tomcat .
 

The Struts bridge relies on the fact that all URLs used in the application are created via Struts HTML tags and this includes image file references. It also expects every page to be accessed via a Struts action. These are essentially Struts best practices but in order for this application to work as a portlet it becomes essential that these practices are followed. So in the Jetspeed2 version of JPetstore there has been a general tidy up of the JSP pages to ensure that these practices are followed.

Portlet related changes

Necessary Portlet Modifications

  • Various minor changes to JSP files
  • WEB-INF/web.xml
  • WEB-INF/struts-config.xml

Necessary Portlet Related Additions

  • WEB-INF/portlet.xml
  • WEB-INF/struts-portlet-config.xml
  • WEB-INF/lib/portals-bridges-common-0.4-SNAPSHOT.jar
  • WEB-INF/lib/portals-bridges-struts-1.2.4-0.4-SNAPSHOT.jar
  • WEB-INF/lib/uPortalContextProvider.jar

The Jetspeed2 JPetstore includes additional configuration files and libraries that are necessary specifically to make it work as a portlet. Most importantly it include versions of portals-bridges-common and portals-bridges-struts jar files.

Modify web.xml to use PortletServletinstead of ActionServlet

The key to converting a Struts application to a portlet is that in web.xml the servlet used is modified so that instead of using org.apache.struts.action.ActionServlet it uses org.apache.portals.bridges.struts.PortletServlet. In the portlet version for the JPetstore web.xml we have:
 

<servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>org.apache.portals.bridges.struts.PortletServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
</servlet>

Modify JSP pages to use a portlet aware Struts HTML taglib

JSP pages are modified to used a portlet aware version of the Struts HTML taglib. In the JSP pages references to:

<%@ taglib uri="struts-html" prefix="html" %>

are replaced with:

<%@ taglib uri="http://portals.apache.org/bridges/struts/tags-portlet-html" prefix="html" %>

Modify struts-config.xml to override the default RequestProcessor

In struts-config.xml a new portlet aware controller is introduced. In WEB-INF/struts-config.xml add a <controller> element just above the <message-resources> element to override the RequestProcessor

.
 

<controller pagePattern="$M$P" inputForward="false" processorClass="org.apache.portals.bridges.struts.PortletRequestProcessor"/>

Create a struts-portlet-config.xml file in the WEB-INF directory

A new configuration file called struts-portlet-config.xml has been added. The purpose of this file is to help the Struts portlet identify which URLs are portal actions and which are portal views, it also identifies which objects should be copied from a portal action to the subsequent portal rendering request. For JPetstore this file looks something like this:

<?xml version="1.0" encoding="UTF-8"?>
<config>
  <render-context>
    <attribute name="errors"/>
    <attribute name="message"/>
  </render-context>
  <portlet-url-type>
    <action path="/shop/add"/>
    <action path="/shop/switch"/>
    <action path="/shop/remove"/>
    <action path="/shop/signoff"/>
    <action path="/shop/viewCategory"/>
    <action path="/shop/viewItem"/>
    <action path="/shop/viewProduct"/>
    <action path="/shop/viewCart"/>
    <action path="/shop/newOrder"/>
    <render path="/shop/newOrderForm"/>
    <action path="/shop/listOrders"/>
    <resource path="/images/"/>
  </portlet-url-type>
</config>

Create a portlet.xml file in the WEB-INF directory

A portlet.xml file has been added containing something like this:

<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" version="1.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
 http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd">
   <portlet>
    <portlet-name>JPetstorePortlet</portlet-name>
    <portlet-class>org.apache.portals.bridges.struts.StrutsPortlet</portlet-class>
    <init-param>
      <name>ServletContextProvider</name>
      <value>ca.mun.portal.bridges.PortalServletContextProvider</value>
    </init-param>
    <init-param>
      <name>ViewPage</name>
      <value>/index.shtml</value>
    </init-param>
    <init-param>
      <name>HelpPage</name>
      <value>/help.shtml</value>
    </init-param>
    <expiration-cache>-1</expiration-cache>
    <supports>
      <mime-type>text/html</mime-type>
      <portlet-mode>VIEW</portlet-mode>
      <portlet-mode>HELP</portlet-mode>
    </supports>
    <portlet-info>
      <title>JPetstore</title>
      <keywords>Struts</keywords>
    </portlet-info>
  </portlet>
</portlet-app>

Create an instance of ServletContainerProvider for the portal platform

You'll notice that in the above portlet.xml file there is a reference to a ServletContainerProvider. To get the struts bridge working, Satish Sekharan of Memorial University of Newfoundland wrote an implementation of the interface org.apache.portals.bridges.common.ServletContextProvider. This implementation supplies access to the Servlet context of uPortal/Pluto portlet container.
 

Please note that there are some unresolved bugs with the current version of uPortal.

https://clearinghouse.ja-sig.org/issues/browse/UP-1347
https://clearinghouse.ja-sig.org/issues/browse/UP-1348

I have provided fixes for all these bugs. JPetstore and other portlets using the Apache Struts Bridge would work as expected with the next release of uPortal 2.5.2

package ca.mun.portal.bridges;

import javax.portlet.GenericPortlet;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.apache.pluto.core.impl.PortletContextImpl;
import org.apache.portals.bridges.common.ServletContextProvider;

/**
 * PortalServletContextProvider supplies access to the Servlet context of uPortal Portlet.
 *
 * @author Satish Sekharan
 */
public class PortalServletContextProvider implements ServletContextProvider {

         public ServletContext getServletContext(GenericPortlet portlet)
         {
                 return ((PortletContextImpl) portlet.getPortletContext()).getServletContext();
         }

        public HttpServletRequest getHttpServletRequest(GenericPortlet portlet, PortletRequest request)
        {
                return (HttpServletRequest) ((HttpServletRequestWrapper) request).getRequest();
        }


        public HttpServletResponse getHttpServletResponse(GenericPortlet portlet, PortletResponse response) {
                return (HttpServletResponse) ((HttpServletResponseWrapper) response).getResponse();
        }
}

Deploy the WAR file as a portlet

If you deploy the Jetspeed2 JPetstore WAR file using Pluto or uPortal an additional servlet entry and mapping will be automatically added to the web.xml.

<servlet>
        <servlet-name>JPetstorePortlet</servlet-name>
        <display-name>JPetstorePortlet Wrapper</display-name>
        <description>Automated generated Portlet Wrapper</description>
        <servlet-class>org.apache.pluto.core.PortletServlet</servlet-class>
        <init-param>
            <param-name>portlet-class</param-name>
            <param-value>org.apache.portals.bridges.struts.StrutsPortlet</param-value>
        </init-param>
        <init-param>
            <param-name>portlet-guid</param-name>
            <param-value>jpetstore.JPetstorePortlet</param-value>
        </init-param>
</servlet>

<servlet-mapping>
        <servlet-name>JPetstorePortlet</servlet-name>
        <url-pattern>/JPetstorePortlet/*</url-pattern>
</servlet-mapping>

Building Portal bridge components from source

Here are some brief pointers on how to obtain and build the Portal bridges source code manually. All over Apache land projects are migrating from CVS to Subversion, it seems that this is happening at much faster rate than the accompanying documentation is being produced! So for people without Subversion savvy this is how to download the Portals bridges source code. First obtain yourself a Subversion client and install it.

If you are using a command line version of subversion you will now have a executable called svn somewhere on your system. Create a new directory and checkout the Portal bridges project code using a command like:

svn checkout http://svn.apache.org/repos/asf/portals/bridges/trunk

This will checkout the "HEAD" of the project. Now you need to make use of the usual suspects for a Java project build (Ant and Maven). To make the portals-bridges-common and portals-bridges-struts jar files you just need to change into the appropriate directory, run maven and let it do it's build magic. To clean and build all projects at once, you can run maven allClean allBuild in the root directory of the project

References

http://portals.apache.org/bridges/multiproject/portals-bridges-struts/features.html http://raibledesigns.com/wiki/Wiki.jsp?page=StrutsPortlet