+

Search Tips   |   Advanced Search

Changes to Struts application code


Learn about the main differences between a servlet-based Struts application and a Struts application created for the portal environment.


Comparison of servlets and portlets

There are two main differences between the portlet and servlet environment that affects a Struts application in WebSphere Portal.

  1. Action processing and rendering

    All servlet processing occurs during the service() method. The Struts rendering of the page is usually immediately preceded by action processing; they are essentially part of one step. The request and response object are passed to the service() method, which writes the resulting output to the response object. The servlet-based Struts RequestProcessor is designed to complete the Struts action processing and eventually forward, based on the URI, to a JSP or another Struts action.

    Portlet processing, however, is implemented in two phases, an event phase and a render phase. Action processing is performed prior to rendering the display view. Only the request object is passed to the portlet during the event phase. When a Struts application is migrated to the portlet environment, some of the information that was available during the event phase, namely the request parameters, is no longer available during the render phase. Additionally, since rendering methods, such as doView(), can be called when the portlet page is refreshed without a new event occurring for that portlet, all information required to render the page must be available every time that method is called.

    The struts action is not invoked when moving between modes (view, edit, and configure). Only the service method is called (doView, doEdit) when switching modes. To cause the struts action to fire, place a call to that action in these methods.

    The Struts Portlet Framework provides a RequestProcesson that creates an IViewCommand. IViewCommand encapsulates the information so that the command object can be rendered at a later time, during the rendering method. IViewCommand and IViewCommandFactory are discussed in Saving information for rendering the view.

  2. URI construction

    URIs are constructed differently for portlets than for servlets. The portlet creates the URI programmatically using the PortletResponse object. The Struts Portlet Framework has modified the tags in Struts so that they create portal links. The Struts link tags behave the same as they do in the servlet environment, but the URL is a portlet URL and the Struts URL is passed as a parameter on the URL.


Saving information for rendering the view

A command pattern can be used to encapsulate the rendering of the view, and the information required during this rendering (See Design Patterns: Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson, and Vlissides for more information about the command design pattern). The pattern is implemented using the IViewCommand interface.

During the action processing, information needed to render the view needs to be saved, including the path to the page to be displayed. Also, for a JSP in a Struts application, the associated ActionForm is normally needed in order to complete the page. So, any ActionForm attributes need to be saved along with the path to the page to render. The attributes that are saved with a command should be limited because, since the action processing and view rendering steps involve separate requests, the command is saved in session. Since the session information may need to be serialized in a high availability environment, it is important to minimize the amount of data stored in the session wherever possible. Thus, the ActionForm is typically needed, this is saved by default.

In doView() processing, nothing needs to be known about the IViewCommand except that it should be executed. The execute method of the IViewCommand receives the request and response objects as parameters. As part of the processing, the previously saved attributes are populated into the request object passed in on the execute method.

Also, during the render phase, an execution context object (ViewCommandExecutionContext) is passed in on the execute call, which provides additional objects to help with the actual execution. We can save additional information with the view command and provide additional objects and information in the command execution context.

For more information about IViewCommand, refer to the Javadoc documentation for the com.ibm.portal.struts.command package. The product Javadoc documentation is located at PORTAL_HOME/PortalServer/doc/Javadoc/api_docs/ directory. Also see Use the command factory.


Response object

During portlet action processing the response object is not available. The methods called during Struts processing expect both a request and response. To address the need for a response object, the Struts Portlet framework creates a temporary one. Other than for calling sendError(), you should not write to the response object. Instead the Action should return an ActionForward object and let the Struts RequestProcessor complete the doForward set.


sendError() processing

The typical Struts application has no need to access the response object during the Action processing. However, when an application checks and finds some state information invalid during action processing, it is not unusual for a Struts application to report the error using the response.sendError() method. The Struts Portlet Framework intercepts the calls to sendError() and saves the error information in the session. During the later view render phase, this error information is found, and the error information is displayed in lieu of displaying other content for the portlet.


Saving information in a bean

A typical Struts application does not write directly to the response object and instead forwards to a JSP. The JSPs should not need access to the original event information, request parameters, which initially led to the current rendering. Instead, action processing should store information in a bean from which the JSP extracts the information. As long as the bean is available during the rendering, the rendering can be repeated. Since beans, such as ActionForm, are usually stored in the original request attributes, the request attributes from the original request processing must be saved in the iViewCommand and made available when doView() is invoked.


Forwards and redirects

Although we can write portlets using Struts, there are some aspects of the servlet environment that we cannot do in a Struts portlet.

For example, Struts provides support for redirects and forwards to other servlets. These are provided because they are functions generally available to servlets. However, these capabilities are not available in portlets because WebSphere Portal does not support forwards or redirects.

The Struts Action object returns an ActionForward object, which contains a path to be either forwarded or redirected to. WebSphere Portal does not support forward operations because the response object has already been committed. If the path is not an action, include the page using the PortletContext.include() method. If the path is an action, then a forward to the Action is simulated. Forwards to other actions can be handled by recursively sending the new Action URI through the Struts base processing of an Action. Redirects are treated in the same way as a forward. This can be implemented using PortletApiUtils class. The following example demonstrates how to implement a forward to a Struts Action from an Action or a custom tag.

PortletApiUtils portletUtils = PortletApiUtils.getInstance();
if (portletUtils != null)
{
   portletUtils.forward( page, request );
}
else
{
   pageContext.forward( page, request );
}

The PortletApiUtils.getInstance() method is called to see if a PortletApiUtils instance is available. If a non null value is returned, then the execution is inside of WebSphere Portal. In this case, the portletApiUtils.foward() method is used instead of the PageContext.forward().

The issue with forwards affects tag handlers as well. In tag handlers, it is possible to access the PageContext object and invoke the forward method. An alternative method to the PageContext.forward() is available via the PortletApiUtils class that will provide a mechanism to emulate the forward functionality.

The tags in the PortalStrutsExample were modified to use the PortletApiUtil class instead of the PageContext.forward. The tags shipped with the Struts example are CheckLogonTag, LinkSubscriptionTag, and LinkUserTag. Here is a code sample of the change.

PortletApiUtils portletUtils = PortletApiUtils.getInstance();
if (portletUtils != null)
{
   // when run in a portlet use this execution path.
   Object pResponse = portletUtils.getPortletResponse( request );
   Object portletURI =
   portletUtils.createPortletURIWithStrutsURL(pResponse, url.toString() );
   // don't need to call response.encodeURL, // the portletURI.toString takes care on that.
   results.append(portletURI.toString() );
}
else
{
   // when run as a servlet, execute this path    results.append(response.encodeURL(url.toString()));
}

    

If a forward is required in a JSP, then the logic forward tag is the suggested solution. The forward tag has been modified to use the PortletApiUtils forward implementation, and is the preferred method for a forward from a JSP. The PortletApiUtils can be obtained in a JSP through java code, but obtaining the Struts ModuleConfig to prefix the path is problematic. The logic forward tag handles these issues.


Customize Commands

The section Comparison of servlets and portlets describes two phase processing of the portal server and the need for the IViewCommand objects created in the actionPerformed() phase. The Struts Portlet Framework ships several different commands that handle dynamic content, static content, XML data and error information from response.sendError(). This section describes some of the customizations that can be applied to a command, and also how to implement a command factory for creating new commands.


Add commands through the command factory

The Struts Portlet Framework uses the ViewCommandFactory object to create the IViewCommand objects for representing the view to render based on the given input information. The view command object typically contains the URL of the object (usually a JSP) creating the output for the portlet and other information needed for the rendering.

The command object needs to contain the information to render the output properly. For a JSP within a Struts application, this is typically a form bean. It is possible that a particular portlet needs additional information in order to do the rendering. In such a case, you would need to add code to cause the additional information to be added to the command. The likely approach would be to override the saveAttributes() method in the subclass of the IViewCommand class. If the given URI (or perhaps type of URI) was one that would require additional information for rendering, then a new type of WpsStrutsViewCommand would need to be created which contained the needed information. The saveAttributes() method of this subclass of IViewCommand could then save the additional objects to be placed in the request object as attributes when the command was executed. To cause an existing request attribute to be saved, the saveAttribute method can be used to provide the request object and name of the attribute.

The SPFLegacyPlugins.war and SPFStandardPlugins.war are examples of how to implement a custom ViewCommandFactory.


API enhancements

Ideally, portal-specific requirements should be minimized when writing Struts Applications. There are certain areas that need to be surfaced to the developer, like tag modifications and forwards. For those developing Struts applications specifically for the portlet environment, a class is provided to hide some of the servlet details that do not map well to execution in the portal server environment.


Access portlet objects

There will be situations when a Struts Action needs to access objects in the Portlet API.

For example, the Struts Action might need to access the PortletRequest object to get to PortletSettings or PortletPreferences, depending on the container. The PortletApiUtils should be used to obtain the PortletRequest object from the HttpServletRequest object.

PortletApiUtils portletUtils = PortletApiUtils.getUtilsInstance();
  if (portletUtils != null)  {

   PortletRequest portletRequest = (PortletRequest) portletUtils.getPortletRequest( request );
}
  

The Struts Action class also makes the ActionServlet class available. When the Struts application is authored with the Struts Portlet Framework, the ActionServlet class is actually the WPActionServlet. For more information see the Javadoc documentation for:


Sending a message

The Struts examples use the Property Broker for sending messages from one portlet to another.

The Struts Portlet Framework ships the SPFLegacyMultipleModules, and SPFStandardMultiplModules as examples of using the property broker and Struts Portlet Framework


Parent: Struts Portlet Framework