Changes to configuration files
New init parameters have been added for the Web deployment descriptor (web.xml) of a Struts application to adapt to the portal environment. In addition, the Struts configuration file must be changed to specify the WpsRequestProcessor as the controller. There are no portlet deployment descriptor changes needed to work with Struts applications.
Changes to the Web deployment descriptor
Most of the configuration for a Struts application is in the Web deployment descriptor file (web.xml). The servlet class that is specified in the Web deployment descriptor should be the portlet servlet. The portlet class name is WpsStrutsPortlet. Here is an example definition.
<servlet id="Servlet_1"> <servlet-name>MyStrutsApp</servlet-name> <servlet-class>com.ibm.wps.portlets.struts.WpsStrutsPortlet </servlet-class> ... </servlet>When subclassing WpsStrutsPortlet, designate the subclass name for <servlet-name/>. Note that the servlet id should be suffixed with a unique id to prevent conflicts with other portlets. See Guidelines for portlet application UIDs for more information.
Initialization parameters
The following is a list of names of the initialization parameters that can be configured in the Web deployment descriptor for Struts portlets (using the <init-param> element).
- struts-servlet-mapping
Identifies a Struts action mapping so that paths that should be treated as Struts actions can be recognized. See Servlet mapping for further details.
- ModuleSearchPath
Indicates the search path for Struts modules, default value is the markupName and mode. See Support for multiple modes for more information.
- IncludesSearchPath
Indicates a more fine-tuned search path for including a file. If this value is not specified then PortletContext.include() is used instead.
- EditModeLabel
Configures the label used by the Struts Portlet Framework to search for the module for edit mode. If not specified as an init-parameter, edit is used.
- ConfigureModeLabel
Configures the label used by the Struts Portlet Framework to search for the module for configure mode. If not specified as an init-parameter, configure is used.
- HelpModeLabel
Configures the label used by the Struts Portlet Framework to search for the module for help mode. If not specified as an init-parameter, help is used.
- ViewModeLabel
Configures the label used by the Struts Portlet Framework to search for the module for view mode. If not specified as an init-parameter, view is used.
- UseGroupsForAccess
Configures the Struts portlet to use portal server group names as if they were role names. Struts uses roles to determine access to an Action. However, WebSphere Portal V 4.2 does not support roles. Set this parameter to true to configure the Struts application to use the portal's group names as if they were role names to emulate role-based access control.
- DetermineStrutsCallerInfo
Configures the Struts portlet's log messages to display the source method name as part of the log message. See Logging for more information.
Servlet mapping
In a portlet, you need to use a servlet URL mapping for your portlet. So, one would typically use a path prefix servlet mapping similar to the following.
<servlet-mapping id="ServletMapping_1"> <servlet-name>MyStrutsApp</servlet-name> <url-pattern>/Struts/*</url-pattern> </servlet-mapping>The typical Struts application uses an extension mapping. The Struts example uses extension mapping and it is expected that most Struts applications use the extension mapping "*.do". In the normal Struts case, this means that URIs ending in "*.do" are directed to the Struts servlet. Remember that Struts provides a single servlet that you can use (or subclass) for all of your Struts applications. When you wish to cause control to be given to a Struts Action when a link is selected or form submitted, use the extension from the extension mapping (e.g. "*.do") on the URI so that the processing is routed to the Struts ActionServlet. Other URIs, such as those for JSPs, do not have this extension and, thus, would not be routed through the Action Servlet for processing.
A Struts application, which is itself a servlet application, also requires a servlet mapping. Although our Struts portlet already has a servlet mapping as shown above, you still need to identify what to use as a Struts action mapping so that paths that should be treated as Struts actions can be recognized. To specify the pseudo servlet mapping used by Struts, specify it as a servlet initialization parameter in the web.xml file, as follows:
<init-param> <param-name>struts-servlet-mapping</param-name> <param-value>*.do</param-value> </init-param>The Struts Portlet Framework supports the same servlet mappings modes as the servlet-based Struts application. However, the current Struts implementation only supports extension mapping for the module implementation. Since the portal server implementation uses modules for the mode and markup support, it is strongly recommended that the extension mapping is used.
Welcome file list
The Struts application needs to specify what the initial screen is for the application. The typical way that the initial screen is specified is through the welcome file list. The Struts Portlet Framework supports the welcome file list configuration, and has some additional features. The servlet implementation of the welcome file list only supports JSP and HTML pages. The Struts Portlet Framework supports a JSP or HTML welcome file, and also allows a Struts action to be specified as the welcome file. The other feature in the Struts Portlet Framework is the support for modules. A unique welcome file should be configured for each module by specifying the application prefix. For example, to support the view mode for the markup WML, a welcome file wml/view can be specified.
The following is an example for the welcome file list that is specified in the file web.xml.
<welcome-file-list> <welcome-file>index.jsp</welcome-file> <welcome-file>help/index.jsp</welcome-file> <welcome-file>wml/view/index.jsp</welcome-file> </welcome-file-list>The default Struts configuration, which has a "" prefix, is used any time a module prefix is not found.
A welcome file entry should be configured for each module. The welcome file entry is the initial screen shown when first switching to that module.
Support for multiple modes
The Struts Portlet Framework is set up to use the mode and markup to find the Struts configuration. However, if you do not want to use a Struts application in one of the modes, the Struts portlet (WpsStrutsPortlet) should be subclassed. The appropriate service() method should be overridden to implement the desired behavior. For example, if the developer wants a help mode that does not use a Struts application, then the doHelp() method should be implemented in a subclass.
Portlet modes can be supported with the Struts Portlet framework. The Struts Portlet Framework stores the IViewCommands on a per mode basis. That means that an application that supports view and edit mode will have an IViewCommand for view and edit mode. The welcome file list is typically used for the initial page when entering a mode.
The MultipleStrutsApp.war example demonstrates support for edit mode. The Module 1 portlet implements an edit mode wizard that steps through three screens. The example also demonstrates how to use a StrutsActionForward so that the IViewCommand object for the last page of the wizard can be marked to be removed on a mode change. This allows the IViewCommand object for edit mode to be deleted when the user switches from edit mode back to view. The anticipated use is that the user is entering data in edit mode and the configuration data is submitted to be stored. The last page displays that the data has been successfully stored. The user then leaves edit mode using the skin. If the user goes back into edit mode, they do not want to see the message that the data was successfully stored. Since the command was deleted, the welcome file list entry can be used to start the wizard again. This feature also reduces the number of attributes stored in session.
<action path="/nextEditPage2" type="com.ibm.wps.struts.example.multipleapps.actions.ForwardAction"> <forward className="com.ibm.wps.struts.action.StrutsActionForward" name="success" path="/edit3.jsp"> <set-property property="removeOnModeChange" value="true"/> </forward> </action>The Struts example application demonstrates support for help mode. A Struts configuration has been added to the Web deployment descriptor, web.xml, to demonstrate help mode. This mode is entered when the user clicks the help button for the Struts example portlet. The following code fragment from the Web deployment descriptor demonstrates how to configure the struts-configuration to support help mode.
<init-param> <param-name>config/html/help</param-name> <param-value>/WEB-INF/html/help/struts-config.xml</param-value> </init-param>The search path set in the Web deployment descriptor includes mode, which enables a separate Struts configuration for a mode.
<init-param> <param-name>SubApplicationSearchPath</param-name> <param-value>markupName, mode, locale</param-value> </init-param>The welcome file list defines the welcome file for the help mode, as follows:
<welcome-file-list> <welcome-file>index.jsp</welcome-file> <welcome-file>html/help/index.jsp</welcome-file> <welcome-file>wml/view/index.jsp</welcome-file> </welcome-file-list>
Support for devices
The Struts Portlet Framework's support of devices was designed to be flexible. The way the configuration and JSP files are placed in the directory structure should be tolerant of the devices that need to be supported. Some applications may just support HTML browsers and only need a single Struts configuration file. Other applications may support multiple markups and locales but do not need to know specifics about the device itself, while other implementations may need unique implementations for each of the supported devices.
Note: This support requires extension mapping. The prefix mapping only supports only a single configuration file.
WebSphere Portal is designed to allow portlets to access information about the client device, such as locale and markup language supported, using the Client interface. The Struts support for modes and markups has been implemented by allowing a configuration parameter to specify the fields in the client object that are interesting. The Client object is treated like a bean, where the field value corresponds to a get method in the Client object. This allows developers to specify the fields in the Client object that are necessary to their device support.
There are two paths that can be specified for supporting modes and device types (clients). The first path is the search path, and that path is used to specify the module to use. The search path locates the appropriate Struts configuration to use and is the prefix that is used as the base directory for the necessary JSPs. The default search path is markupName, mode. This will search for a configuration based on the current markup and the portlet mode.
<init-param> <param-name>SubApplicationSearchPath</param-name> <param-value>markupName, mode, locale</param-value> </init-param>For example, if a desktop browser is used and the user is in view mode, then the search path would be:
- html/view
- html
- "" ( default configuration )
And if the Client object specified "wml" as the markupName then the search path would be:
- wml/view
- wml
- "" ( default configuration )
The search path is checked against all of the prefixes for the ModuleConfig objects for a match. The modules are configured in the Web deployment descriptor, web.xml, by specifying a prefix for a Struts configuration.
For example, a module for the WML markup in view mode would be configured by adding a configuration to the web deployment descriptor like:
<init-param> <param-name>config/wml/view</param-name> <param-value>/WEB-INF/wml/view/struts-config.xml</param-value> </init-param>If the wml/view module is selected, the corresponding JSPs are searched for under the wml/view directory of the Struts application WAR file.
This is a good start for separating the files for different markups and modes, but this granularity is probably not enough to deal with all available devices, or even locales. You can modify this search path to have a finer granularity. For example, you could create a unique module for every phone that they support.
The search path of markupName, mode, locale, manufacturer, model, version could be used. This offers a very fine-grained deployment of the JSPs. The problem with this approach is that the majority of the JSPs would probably be the same for each of the devices. There are probably a few that need to be modified for each device, but this approach does not allow sharing.
This is why two paths are offered, search and include. The search path is used to find the module. This would be for applications that can use a common configuration, share actions and some of the JSPs. The include path can then be used to look for specific implementations of the JSP and if not found, fall back to a common file.
Take the following include path for including JSPs that is dependant on the device.
<init-param> <param-name>IncludesSearchPath</param-name> <param-value>manufacturer, model, version</param-value> </init-param>This would search for the file relative to the module and then take the requested fields into account. So if the search path found the wml/view module and the file index.jsp is requested, then the above search path for an ACME T400 would be as follows:
- wml/view/Acme/T400/index.jsp
- wml/view/Acme/index.jsp
- wml/view/index.jsp
Note that version did not show up in the search. That is because the default client configuration for the T400 did not have one defined. If one was configured at a later time, then it would be used.
Portal aggregation is flexible in how the differences between devices are supported. The common features are supported through the search path; all WML devices in view mode in a locale. The subtitle differences between devices would be supported through the file search of the include path. The common versions of the JSPs would be at a root level, and specific files would be at lower levels.
The Struts example ships with several tags, two of which required a minor change to support modules. The LinkSubscriptionTag and LinkUserTag tags had to be modified so that the current ModuleConfig's prefix had to be added to the computed URL.
Internationalization support
The Struts Portlet Framework supports i18n through the use of resource bundles. Each Struts application can specify a message resource. The Java resource bundle implementation determines the resource bundle to use based on the locale and variant. The Struts application typically uses the message tag to include translated text from the application-supplied resource bundle.
WebSphere Portal also provides an implementation to find localized JSPs using the include() method on PortletContext. PortletContext inserts the locale into the path to locate the locale-specific JSP. For more information, see Generating markup for multiple devices, markups, and languages.
The Struts Application can be deployed to use the portal server's aggregation method to find localized JSPs, instead of a resource bundles for each locale. The Struts Portlet Framework uses the PortletContext.include() to include JSP pages, so both methods of localization are supported.
PortletContext.include() searches for locale-specific files by inserting the markup and locale name in the path. Depending on how the file structure has been set up, this may not be desirable. This behavior can be configured through an initialization parameter in the web deployment descriptor. An include search path can be specified that controls how the includes are located. For example, the default implementation would be markup, locale but can be changed to just locale if desired. The mode and markup support may have been already configured to include markup, so this configuration allows the markup to not be needed a second time.
<init-param> <param-name>IncludesSearchPath</param-name> <param-value>locale</param-value> </init-param>The Struts framework was designed to give the application control over locale by using a locale object that is set in session with a known key. The Struts RequestProcessor sets the locale object for each user during the processLocale() method. The processLocale() method sets the locale object using the key Globals.LOCALE_KEY if the feature is enabled through the Struts configuration file and the attribute has not already been set in session.
The locale support in Struts is controlled by a setting on the controller element that is available in the Struts configuration file. The support can be turned off, which will stop the locale processing in the Request Processor processLocale() method.
WebSphere Portal supports dynamic changes to the locale. Portlets should use the locale object that is obtained through the PortletRequest object for the locale setting. The Struts Portlet Framework sets the locale object in the session using the key Globales.LOCALE_KEY during the actionPerformed() and service() methods. The support is implemented by calling the WpsStrutsPortlet processLocale() method, which sets the locale object if one is not found or the locale is different than the one already in session. This assures that a portlet written using the Struts Portlet Framework uses the correct locale.
Locale support can also be customized. The WpsStrutsPortlet processLocale() method can be overridden for a customized implementation. The invocation of WpsStrutsPortlet's processLocale() can also be stopped by adding the following init-parameter to the Web deployment descriptor for the application.
<init-param> <param-name>UsePortalsLocale</param-name> <param-value>false</param-value> </init-param>However, in most cases the portlet should use the default implementation.
WML support
Because WebSphere Portal supports pervasive devices, such as WAP phones, the example has been configured to support a generic WML device. The view mode for a WML device is supported by adding a Struts configuration for WML devices. The following code fragment demonstrates how to set up the struts-configuration for the view mode for WML markup.
<init-param> <param-name>config/wml/view</param-name> <param-value>/WEB-INF/wml/view/struts-config.xml</param-value> </init-param>The search path that is set up in the Web deployment descriptor, takes markup, mode, and locale into account. When a WAP device connects to a portal server, the markup will be WML, and the mode will be view. This example does not use the locale that is requested.
The welcome file list also defines the welcome file for the wml/view mode.
<welcome-file-list> <welcome-file>index.jsp</welcome-file> <welcome-file>html/help/index.jsp</welcome-file> <welcome-file>wml/view/index.jsp</welcome-file> </welcome-file-list>For more information about WML support in the Struts Portlet Framework, see Use the WML tags.
Logging
The Struts Portlet Framework uses the Commons-Logging interface for a logging facility. The Struts Portlet Framework has supplied an implementation of the Commons-Logging Log interface that can be used to map the trace messages to the logging facility used by the portal server. This file is normally found in the wp_root/log directory.
The logging in WebSphere Portal typically creates a logging object per class and the trace messages display the log class that the message is from. This is very convenient to determine where a trace message originated from and also allows only enabling the tracing on certain classes.
The typical trace string configured in the log.properties file for tracing a Struts application would be as shown below (all on one line). This can be modified to add or remove classes.
traceString=org.apache.struts.*=all=enabled:com.ibm.wps.portlets.struts.*=all=enabled:com.ibm.wps.struts.common.* =all=enabled:com.ibm.wps.struts.base.*=all=enabledSome of the Apache source uses a string to specify the class name and, in some cases, this string may not be a valid class. In those cases the class name in the trace message will be com.ibm.wps.portlets.struts.logging.WpsStrutsTraceLogger.
This package or class should be specified in the traceString to account for these occasions.
The log messages from Struts use the portal server's logging facility when logging is enabled. When this is the case, the log messages from Struts may not display the correct method name as part of the log message. There is a configuration parameter to enable correcting this problem, but this is an expensive operation. This feature should only be enabled when needed. This feature is not required as much with the logging changes implemented in this release.
<init-param> <param-name>DetermineStrutsCallerInfo</param-name> <param-value>true</param-value> </init-param>
Changes to the Struts configuration file
The Struts configuration used by Struts portlets references the 1.1 version of the DTD.
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">To have the Struts Portlet Framework operate properly, the RequestProcessor must be configured. The RequestProcessor is responsible for the main functionality of processing a request. The default RequestProcessor can be overridden in the struts-config.xml via the controller element. In the struts-config.xml, the following portal server RequestProcessor must be specified:
<controller processorClass="com.ibm.wps.portlets.struts.WpsRequestProcessor"> </controller>Of course, you may also have had the need to extend the Struts RequestProcessor in order to extend the functionality of Struts. If you simply place the path to your RequestProcessor in the controller element in the struts-config file, your application will not work properly in the portlet environment. Instead, what do is have your RequestProcessor class extend the
WpsRequestProcessor
rather than the normal Struts RequestProcessor.The following are the methods overridden in WpsRequestProcessor:
- processLocale
- processActionPerform()
- processActionForm()
- processActionForward()
- processPath()
- processRoles()
- processMapping()
- processValidate()
- doForward()
- doInclude()
- getMapping()
If you override any of the methods listed above, make sure to invoke the super class version of the method from within your method.
There are existing Struts Extensions which require you to use their RequestProcessor. Clearly, you cannot use their RequestProcessor and the
WpsRequestProcessor
at the same time. Instead, you would need to get the source to that RequestProcessor and following the approach just described.
Tiles support
Struts integrates the Tiles package for creating views. A Tiles sample is included with the Struts Portlet Framework that demonstrates how to configure Tiles in the Struts Portlet Framework. Tiles requires a specific Request Processor that is configured through a plugin. The following is from the Struts configuration to show how to configure Tiles for a Struts application that is deployed in WebSphere Portal.
<!-- add the Tiles plugin. --> <plug-in className="com.ibm.wps.portlets.struts.plugins.WpsStrutsTilesPlugin"> <set-property property="definitions-config" value="/WEB-INF/conf/tiles-def.xml"/> <set-property property="definitions-debug" value="2"/> <set-property property="definitions-parser-details" value="2"/> <set-property property="definitions-parser-validate" value="true"/> </plug-in>The Tiles plugin requires that compatible request processor is configured in the controller element. The WpsStrutsTilesPlugin has been written to accept the default Struts RequestProcessor, WpsStrutsRequestProcessor, or the WpsStrutsTilesRequestProcessor.
See also