The first part of this article series showed developers who are familiar with the Struts framework in the servlet environment how to use Struts in the WebSphere Portal environment. We created a simple portlet application and then applied features of the Tiles and Validation frameworks to the application. In this part, we look at some portlet specific features provided by the IBM Struts Portlet Framework 5.1.0.1 (hereafter called the framework).
Portlets differ from servlets in several ways so the framework includes support beyond the servlet Struts framework. In Part 1, we talked about how it supports additional request processing requirements using the IViewCommand interface. Now you look at some of the additional support provided by the framework; specifically, you see how to use support for modes, devices, internationalization, and creating URLs, as well as how to integrate with the property broker so you can use cooperative portlet capabilities (a capability not yet available through the JSR 168 API).
You can download sample code (which is the same as that used for Part 1).
Portlets use the concept of a mode to indicate what a user is doing. For example, the view mode indicates normal functionality in an application. In the case of a mail portlet, the view mode lets the user do normal mail tasks, such as checking for email, composing new mail, or reading email. Edit mode presents editing capabilities. When a user requests edit mode a mail portlet, it might ask the user for login information. Help mode (as the name suggests) displays the application help to the user.
Because the different modes have different purposes, you will need different business logics to handle each of them. It is a good idea for you to create your different mode related Action classes in a different package. You also need to configure them in different struts-config.xml files.
You need to use different welcome pages for the different modes. In the case of the mail Portlet application, the view mode welcome page should display a list of emails in the user's inbox, and the welcome page for the edit mode should provide a way for the user to log in.
The framework supports these things by letting you use a different struts-config.xml file for each mode, and it lets you use different welcome pages for each mode. A difference from the servlet Struts environment is that you are not forced to use a JSP page for the welcome page; instead, you can use a Struts Action for the welcome page.
- Create this type of file structure in your Web Content folder.
The Contact Management application supports web browser access only so we will have HTML Folder.
- Create an html folder under Web Content. In that folder, create a folder for each mode. In the edit mode we have one page, EditContact.jsp. Likewise, for help mode we have one help file, Help.jsp. The view folder has several JSPs which are required for view mode. Download the sample code provided with this article and import the JSP files from it.
- Then, you need to change portlet.xml to inform the portal server which modes you want to support for text/html content type.
<supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> <portlet-mode>help</portlet-mode> </supports>
This definition informs the framework that Contact Management Portlet supports view, edit, and help modes for the html markup type.
- Next, you need to configure different welcome pages for the different modes. Change the portlet.xml file to add these lines in the portlet-preferences element.
<portlet-preferences> <preference> <name>viewMode.page</name> <value>html/view/index.jsp</value> </preference> <preference> <name>editMode.page</name> <value>html/edit/EditContact.jsp</value> </preference> <preference> <name>helpMode.page</name> <value>html/help/contactHelp.do</value> </preference> </portlet-preferences>
The viewMode.page portlet-preferences attribute says that /html/view/index.jsp should be used as the welcome page for view mode. When the user tries to access Edit mode, the EditContact.jsp should be used as welcome page.
The welcome page definition for Help mode is a little bit different; we specify that ContactHelp Action should be called for generating markup. When the framework gets a request from the user, it determines which mode the user is requesting and then attempts to determine which Action should be initiated for that mode. If the framework cannot determine which Action to take, it calls the welcome page for that mode.
- You can either create a simple Action class called ContactHelpAction.java or import it from sample code. Only thing this class will do is forward control to Help.jsp. Don't worry we will talk in detail about this in next section.
- Create a struts-config.xml file in the /WEB-INF/html/help/ folder, and add a definition for ContactHelpAction.java. (You can import struts-config.xml from sample code.)
- Change the portlet.xml to add this initialization parameter:
<init-param> <name>config/html/help</name> <value>/WEB-INF/html/help/struts-config.xml</value> </init-param>
This definition informs the framework that the struts-config.xml file under /WEB-INF/html/help folder contains aStruts definition for help mode.
- Now, export your application into a WAR file, put it on the server, and try it out. You should see buttons for Edit and Help, as shown below.
When you click on those buttons it now shows you the proper user interface for these modes.
Supporting help for multiple locales
Next, let's look at another powerful feature of the Struts Portlet Framework which enables you to forward control to a localized version of a JSP.
For example, suppose you want to support other locales in addition to English in the Contact Management application, and you want to show different Help pages to users based on their locales. So, if English users access help, they see an English page; French users see a French page.
If you want to achieve this in the Struts framework in a servlet environment, then you have to write a lot of low-level code. Inside your action class, you determine the locale of the user. Using that information you find out where the user should be forwarded.
For English users, it would be:
mapping.findForward("helpenglish") |
For French users it would be:
mapping.findForward("helpfrench") |
Just imagine how ugly your struts-config.xml file will become if you have four or five possible forwards, and you want to support ten locales!
Fortunately, if you want to achieve the same thing in a Portlet Struts Framework environment, all you have to do is define one forward definition and it takes care of creating the path to the locale specific JSP.
Inside the Action class you code:
mapping.findForward("help") |
Forwarding control to “help” action forward will result in English users getting
/html/help/en/Help.jsp and French users getting /html/help/fr/Help.jsp.
Let's add this feature to the Contact Management Portlet application. That is, we will add support for displaying locale-specific help to English, French, and Spanish users. To keep things simple, we will create all three pages in English, but each page will indicate which locale it is intended to serve.
- Create one folder each for French, English, and Spanish. Then, create a Help.jsp file in each of these folders.
- In the Help.jsp page in the
frfolder, add text saying:
"This is the French Help page" .Similarly, add an appropriate statement to each of the Help.jsp pages in the other two folders:
"This is the English Help page" in theenfolder.
"This is the Spanish Help page" in theesfolder. - Specify to the framework that our application includes locale-based JSPs by adding an initialization parameter to StrutsPortlet inside the portlet.xml file, like this:
<init-param> <name>IncludesSearchPath</name> <value>markup, mode, locale</value> </init-param>
The IncludesSearchPath initialization parameter for the StrutsPortlet defines how the path to the included JSP should be constructed. For example, in our application we specify the value:
IncludesSearchPath as markup, mode, locale |
and inside the ContactHelpAction.java we code:
mapping.forward("help") |
The framework will create a path to forward the JSP like this. It determines:
- Which markup the user is asking for. Let's assume it is HTML.
- The mode the user is asking for; let's assume it is help. So, the path so far would be
/html/help. - Which locale the user is requesting (from the third parameter, ).
For an English user it will build a path like this:
/html/help/en/Help.jsp and pass control to it.
However, if the user is French, then it forwards control to /html/help/fr/Help.jsp.
So, you can change the value of the IncludeSearchPath variable to specify the order the framework should use to build the path to forward the JSP.
To see how languages are mapped to locales, see the browser's Language setting.
Handling exceptions gracefully is a very important requirement for any application. Suppose you are building an online shopping mart. As part of the application, the user enters his credit card information, and clicks submit. However, your application has some database connection failure and it shows a stack trace to the user. This experience will guarantee that user will not come back to your site ever again!
Struts 1.1 supports the concept of a global exception handler. You can create a class by extending org.apache.struts.action.ExceptionHandler, and override the execute() method in it. Then, you can declare this exception handler in struts-config.xml, where you can say that this class will handle IOException. After that, if the execute() method of your Action class throws an IOException, control is passed to your custom Exception handler class. Most Struts developers are familiar with this concept, so we will not talk about this in much detail.
Instead, in this section, we want to talk about the Struts Portlet Framework specific features of com.ibm.portal.struts.plugins.IErrorResponseFormatter. The com.ibm.portal.struts.portlet.StrutsException class is used as a base class for all framework exceptions. If a StrutsException occurs, and it does not have a Struts Exception handler configured for it, then the error displays using the default ErrorResponseFormatter. What that means is that if (for example) one of your Action classes throws MaxChainingExceededException and you don't have an Exception handler configured for it in, the framework displays 500 Unhandled Exception and the type of exception, which is MaxChainingExceededException, in this case.
Let's change our Contact Management Portlet application so that if there is a MaxChainingExceededException it displays this message to the user:
Error occurred in processing your request please contact admin@contactmang.com for assistance. |
To add this support:
- Create a ContactErrorResponseFormatter.java class like this:
public class ContactErrorResponseFormatter implements IErrorResponseFormatter{ private static Log log = LogFactory.getLog(ContactErrorResponseFormatter.class); public void formatError(ErrorResponseInfo errorResponseInfo, PortletRequest request, PortletResponse response, ViewCommandExecutionContext viewContext) { try{ if ( response instanceof RenderResponse ) formatError(errorResponseInfo,((RenderResponse)response).getWriter()); }catch ( Exception exc ){ exc.printStackTrace(); } } public void formatError(ErrorResponseInfo errorResponseInfo, PrintWriter printWriter) { log.fatal("Error occurred in application "); log.fatal("Error Code " + errorResponseInfo.getErrorCode()); log.fatal("Error Text " + errorResponseInfo.getErrorText()); printWriter.write("Error occurred in processing your request please contact admin@contactmang.com" ); } }
When an error occurs, the top formatError() method gets control. It reads the object of PrintWriter, and passes it to the second formatError() method, which logs the details about the exception, and then displays a message to the user saying an error occurred.
- Now, you need to register this class with the framework so that whenever there is an exception of StrutsException type, it uses ContactErrorResponseFormatter class to display information to the user.
public class ContactErrorFormatterPlugIn implements PlugIn { public void destroy() { } public void init(ActionServlet actionServlet, ModuleConfig moduleConfig) throws ServletException { PluginConfig.storePluginObject(actionServlet, moduleConfig, PluginConstants.ERROR_RESPONSE_FORMATTER_KEY, new ContactErrorResponseFormatter()); } }
- Finally, you add a definition of ContactErrorFormatterPlugIn class in the struts-config.xml file. You declare a plug-in in the struts-config.xml file. (See Part 1 if you need help doing this) .
- In order to test our ContactErrorResponseFormatter:
- Change one of your Action classes to throw MaxChainingExceededException.
- Build and deploy the modified portlet
- Try to invoke the Action class which will pass control to our ErrorResponseFormatter.
You should see the error message generated by ContactErrorResponseFormatter.
In your applications, you might sometimes need to create a URL which points to another page. For example, index.jsp in our Contact Management application has URL's pointing to all the other pages of our application. One URL points to List Contact, another to Add Contact, and so on.
To create URLs pointing to some other action class, you can use the Struts Tag library, or you can write Java™ code.
- If you use your own Java code, you can use various methods of com.ibm.portal.struts.common.PortletApiUtils to create an URL which points to another action.
- To create an URL pointing to /Welcome.do, you could code:
apiUtils.createPortletURIWithStrutsURL(request,"/Welcome.do");
- Now you may want to do two things at the same time: The user should be taken to the welcome page and, at the same time, his window should be maximized. You create an object of com.ibm.portal.struts.common.PortletURIAttributes class, which allows you to specify attributes about the Portlet URI that need to be created.
com.ibm.portal.struts.common.PortletURIAttributes uriAttributes = new com.ibm.portal.struts.common.PortletURIAttributes(); uriAttributes.setWindowState(javax.portlet.WindowState.MAXIMIZED.toString()); apiUtils.createPortletURIWithStrutsURL(request,"/Welcome.do",uriAttributes);
- To create an URL pointing to /Welcome.do, you could code:
- You could achieve the same thing using struts-html.tld tag library from the Struts Portlet Framework. The tag libraries, which are part of the framework, have been modified to make them more "portlet friendly".
For example, to create an URL pointing to /Welcome.do using the tag library, then use this code
<html:link forward="welcome"/>
And if you want to change the Window Mode of the portlet at the same time code:
<html:link forward="welcome" windowState="MAXIMIZED"/>
The windowState attribute is not there in Servlet version of struts-html.tld. The portlet version of the link tag also provides a portletMode attribute, which lets you create an URL pointing to an action in a particular mode.
- You might also need to create URLs pointing to a Struts Action which is the value of an action attribute in your html forms. You can use the framework's version of html:form to create an URL pointing to action classes. So if you want a form to be submitted to /welcome.do, then you can code:
<html:form action="/welcome.do" portletState="MAXIMIZED">
The portletState attribute of html:form makes sure that once the form is submitted to /welcome.do it opens the next page in a maximized window.
The cooperative portlets capability (originally known as Click-to-Action or C2A) is a very popular and powerful feature in WebSphere Portal. With it, you can create a dynamic connection between portlets so they can share information. Suppose you have two portlets on a page. Portlet 1 publishes property A and portlet 2 is interested in performing some action when property A is published. You can create a wire between them. If you're new to cooperative portlets (which use the PropertyBrokerService) both the WebSphere Portal InfoCenter and the WebSphere Portal zone have extensive information on this topic. See Resources for links to both of these sources and to an article which describes this capability at length.
Let's modify the Contact Management Portlet application to take advantage of this capability. We'll make it the receiver, or target, of a property, and we will create another portlet which will act as the provider, or source, of the property. The source portlet will take the contactId as input, and then publish it to the property broker. We will change Contact Management Portlet such that whenever contactId is published, it opens the corresponding contact details for editing.
Before we get into detailed changes in the Contact Management application, you should know that there are two key differences in cooperative portlet behavior for JSR 168 compliant portlets.
- WSDL is the only way to provide information about portlet actions because programmatic cooperative portlets are not available for JSR 168 portlets. (The JSR 168 standard does not yet support cooperative portlets; however, WebSphere Portal provides a way to support them.)
- You create wires between cooperative portlets using the Portlet Wiring Portlet, which is one of the administrative portlets available with WebSphere Portal. This is the only way to connect JSR 168 compliant portlets because Click-to-Action (a restricted form of cooperative portlet functionality) is not supported for JSR 168 portlets.
To add support to become the target portlet in a Struts application does not require many changes. In theory, you only need to declare the property that you are interested in listening for and which Action should get control when this property is published.
In this example, we are interested in contactId and we want to invoke the /contactAction.do?method=searchContact method when this property is published.
To make the changes:
- Create a ContactIdConsumer.wsdl file in the /WEB-INF/wsdl folder. It should be similar to any consumer wsdl, but the binding in this file should look like this:
<binding name="ContactIdMessageBinding" type="tns:ContactId_Service"> <portlet:binding/> <operation name="EditPersonByContactId"> <portlet:action name="/contactAction.do?method=searchContact" type="standard-struts" actionNameParameter="spf_strutsAction" caption="FindPerson" description="FindPerson_Desc"/> <input> <portlet:param name="contactId" partname="contactId_msg" caption="Edit Contact"/> </input> </operation> </binding>
where:
portlet:actionelement defines which action should be called.
nameattribute specifies the action path that should be invoked.
actionNameParameterelement should be equal to spf_strutsAction when the consumer portlet is a JSR 168 compliant Struts Portlet.
inputsub-element specifies that we want property broker to call our action when the contactId type of parameter is published. - Next, you inform WebSphere Portal that this portlet wants to use the property broker. Add the path of your WSDL file as a portlet preference like this:
<preference> <name>com.ibm.portal.propertybroker.wsdllocation</name> <value>/wsdl/ContactIdConsumer.wsdl</value> </preference>
- Add a ContactId Portlet (which a part of of the sample code available for download with this article) and a ContactMang portlet on one page. Go to the Edit page, and click on the last tab called Wires. Create a wire between these two portlets, as shown in the figure below.
Now you can test the Contact Management portlet cooperation.
- On the page where you have both portlets, enter an ID for an existing contact in the contactId source portlet. The details for it show in the other portlet on the page, as shown below.
Generating sufficient and useful information in the log will really help you after you deploy your application. The framework uses the Jakarta Commons Logging interface for logging. It also supplies an implementation of the Log interface that can be used to map trace messages to the logging facility used by WebSphere Portal.
Trace logging is enabled by setting properties in the <wp_root>/shared/app/config/log.properties file. By default, tracing is disabled. You configure a trace for a portlet application in log.properties. (The entire trace string must be on one line in that file; however we have to show it wrapped here.)
traceString=com.ibm.portal.struts.*=all=enabled:com.ibm.struts.* =all=enabled:com.ibm.wps.portlet.filters.*=all=enabled:com.ibm.wps.portlets.struts.* =all=enabled:com.ibm.wps.shared.struts.*=all=enabled:com.ibm.wps.struts.* =all=enabled:org.apache.struts.*=all=enabled |
Check the generated log in wps_<timestamp>.log.
If you develop any industrial strength portlet application, you need to use some kind of MVC framework, and it is a good idea to use an existing framework instead of creating your own.
The Struts Portlet Framework is a powerful MVC framework because:
- It lets you take advantage of your knowledge of the Struts framework from the servlet environment.
- It does not prevent you from taking advantage of some of the advanced concepts from the portlet environment such as support for devices and modes.
The sample code provided in the Download section of this article is packaged in a single ZIP file which contains these two WAR files:
- ContactMang.war
- Contains source code for the Contact Management application referenced in this article. You can directly deploy this war file to WebSphere Portal. Inside ContactMang.war, see WEB-INF\source for the Java source code and the html folder for the JSPs. Configuration files are inside \WEB-INF.
- ContactIdProducer.war
- Contains source code for ContactId, source portlet which is required for testing the cooperative portlet capability of Contact Management Portlet.
| Description | Name | Size | Download method |
|---|---|---|---|
| Code samples | jsr168-struts-samples.zip | 2.7 MB | FTP |
Information about download methods
Learn
-
developerWorks WebSphere Portal zone: Provides a wide variety of technical resources to help you develop portals and portlets.
-
Developing JSR168 compliant cooperative portlets: Provides an overview of enabling JSR 168 portlets with cooperative portlet functionality.
-
Error validation and exception handling in portlets, using the Struts Portlet Framework
: Introduction to error processing with Struts.
- Executing Struts actions during the render phase of IBM WebSphere Portal: Good overview of using the IStrutsPrepareRender interface including advanced features.
- IBM Redbook: IBM Rational Application Developer V6 Portlet Application Development and Portlet tools: Basic information for creating portlets using the Rational Application Developer environment and built-in portal tools .
-
Portlet development guide: Helps you get started developing portlets in WebSphere Portal V5.x.
- Official Stuts home page
-
WebSphere Portal production documentation: Provides access to all current product documentation including InfoCenters, release notes, and readmes for all releases of WebSphere Portal.
Get products and technologies
-
Rational Application Developer V6: Download trial software from developerWorks. Includes the portal tools and a test runtime copy of portal that you can use to develop a prototype.




