Applying the State Pattern to WebSphere Portal V5 Portlets: Part 1. Overview

IBM ® WebSphere® Portal (hereafter called WebSphere Portal) provides a platform for building enterprise applications. Developers are using it to build portals and portlets that display extremely complex behavior, well beyond the simple HelloWorld portlet. The J2EE™ model provides well-defined frameworks to handle complex page flows in the servlet world. Portlet developers need the analagous frameworks to support their portlets. This paper describes a state transition pattern which you can apply to portlets developed to the WebSphere Portal V5 Portlet API (also known as Portlet API 1.2).

Share:

Tim Hanis (hanistt@us.ibm.com), Senior Software Engineer, EMC

Tim Hanis is a senior software engineer for WebSphere Portal at IBM Research Triangle Park Lab in Raleigh, North Carolina.


developerWorks Contributing author
        level

Skyler Thomas (tskyler@us.ibm.com), Executive Consultant, EMC

Skyler Thomas is an executive consultant for IBM WebSphere Portal at IBM Research Triangle Park Lab in Raleigh, North Carolina.



Chris Gerken (cgerken@us.ibm.com), Senior Software Engineer, EMC

Chris Gerken is a member of the Asset Reuse Enablement Team within the IBM Software Services for WebSphere group. He created and wrote the Design Pattern Toolkit.



03 December 2003

Introduction

This article supercedes an article by a similar name. It was updated to be consistent with the IBM WebSphere Portal Version 5.0 Portlet API, and to remove references to deprecated methods and classes. Specifically, the pattern implementation was modified to remove references to the deprecated class org.apache.jetspeed.portlet.DefaultPortletAction and to the deprecated methods:org.apache.jetspeed.portlet.PortletURI.addAction(PortletAction) and org.apache.jetspeed.portlet.event.ActionEvent.getAction().

The example implementation in part 2 is now a J2EE 1.3 application, and it uses WebSphere Portal V5 development tooling.

WebSphere portlet developers have typically relied on the IBM Portlet API as the basis for their implementation. This API exposes the components needed to develop fully functional portlets. However, it does not provide assistance to implement a well-designed page navigation model. When creating complex portlets, a developer must handle the view navigation within the portlet, and consider how to efficiently use action events and listeners to respond to user action events, such as clicking on a button or a link.

The state pattern design discussed here provides a good approach to portlet application design to help organize page transitions and to implement a separation of responsibilities in the Model-View-Controller (MVC) design pattern. If you choose to program portlets using the IBM Portal API you need to use an approach like this.

This paper describes a pattern implementation. The recommended framework solution for portlet development is Struts. The Jakara Struts project, an Apache Software Foundation open-source project, provides a framework to address these same issues of MVC design and page control flow. It also provides support for exception handling, error handling, input validation, forms, and internationalization. Applications can be written using the Struts framework and deployed as a portlet. For a corresponding example implementation using Struts, see Developing and Deploying a Struts Application as a WebSphere Portal V5 Portlet.

Portlet development guidelines and example implementations provide a good understanding of the Portlet API. However, implementing complex process control is outside the scope of the API. Without a well-designed approach to implementing control logic you end up creating portlets that contain a fair amount of code dedicated to simply addressing the intent of a user's request. Besides being repetitive, this code makes the portlet much harder to read because you need to wade through the control logic to get to the real work of the portlet. The control logic is also prone to error because it relies on aspects, such as string name matching to hook an event to a listener, or a listener action, to behavior in the portlet method. It can also complicate the business logic because the implementation of the control function can resemble the business logic.

To solve these problems with control logic, you can consider your application to be a collection of portlet actions and states. Then, given a well-defined approach to state transitions, you can remove the cumbersome control logic code from within the portlet application. This article discusses how you can apply this same approach to state management for portlet development, and to create a reusable state pattern to address this problem generically.


Problems with the standard implementation approach

Consider a simple MVC-based portlet which retrieves a list of items from a database and presents the list to the user. When the user selects an item, a detailed view is displayed showing specific information about the selected item. This detailed view could involve another request to the database. The important point here is that a requirement exists to follow two different control flows.

In the first step, the controller must call the business objects to create the appropriate beans, which are passed to a Java™ Server Page (JSP) to be rendered in the main view. In the second step, the controller retrieves an indicator of the selected item from the user's request and then calls the business objects to create the appropriate bean, which is passed to a different JSP to render in a detailed view.

In the MVC implementation, the view components are clearly different; two different JSPs exist for the two views. You might need to access different business model components to generate the beans needed for each request. The controller function would need to know:

  • What business methods to call
  • What beans to generate
  • Where to put those beans on the request object
  • How to invoke the appropriate JSP

This is where the code becomes cumbersome. In the case of servlet programming, you might choose to implement these as separate servlets, so that the controller function for each request is naturally separated by the service method of each appropriate servlet class. If you prefer to use a dispatcher approach instead of a separate servlet implementation, then you would have to use a framework, such as Struts, to implement a similar MVC design.

This is a complicated task because you do not have the option of using multiple portlet classes to implement one portlet. You are not able to address a portlet class directly. The same service method of the same portlet handles all HTTP requests sent back to the portlet. Consequently, you need to implement control code to:

  • Determine which action is being requested
  • What processing needs to occur
  • What state to leave the portlet in
  • What to render back to the user

The components of a state pattern implementation

Applying the state pattern to portlets lets you cleanly implement process control. You use the following components in the state pattern, as shown in Figure 1.

Figure 1. The interaction diagram for the state pattern.
Interaction diagram for the state pattern

StateManagerPortlet

This is the main portlet class. It is portlet-independent, and would typically include all of the portlet-specific code. This class serves as a dispatcher to support action and state classes where the portlet code resides. StateManagerPortlet implements the actionPerformed,doView, doEdit, doHelp, and doConfigure methods.

The actionPerformed method simply gets the current instance of the action class and dispatches to its actionPerformed method. Similarly, the do methods get the current state object and dispatch to its perform method. Therefore, the StateManagerPortlet does not need to know anything about the current portlet implementation and does not have a large number of ifs and checks to determine where processing should continue. As long as you are familiar with the flow between states and actions, the processing occurs properly, without your having to code much extraneous control logic.

ActionClassManager

In WebSphere Portal V4.2 the method to add an Action class instance to a PortelURI was deprecated. This API was useful because you could retrieve your Action subclass instance from the PortletEvent object and dispatch to it's actionPerformed method as part of our state transition. The recommended replacement API is to use a string instead of an Action to add to the PortletURI and retreive from the Portlet Event. This class provides the mapping from the given string to an Action class instance.

Action

Classes that implement this interface implement an actionPerformed method. This method performs any action necessary to implement the function required by the action request. However, the function implemented is specific to the action event being invoked. An individual actionPerformed method for a specific action class contains code only for that particular action. This method also sets the current state for further processing. In this flow process, an action is called, which performs work specific to its function and then sets the state for the next transition.

InitialStateManager

This class provides the inital states for each of the supported portlet modes.

State

Classes that implement this interface will implement a perform method. This method gets called from the do methods of the StateManagerPortlet, and contains the code that would normally reside in these methods. Again, this code is specific to the state of the class it resides in, and therefore avoids additional clutter.

Typically, the state's perform method invokes a JSP to render its results. The user interface might let the user set other actions within the portlet. The JSP associates an action class with each of these actions on the page. When a user invokes one of these actions, the appropriate action class instance is invoked from the StateManagerPortlet actionPerformed method, and state transition occurs. The State class is not responsible for state management and transition.

Actions and states

Applying the state pattern results in a cleaner implementation, and, as you move between portlet modes, the state is always remembered. When you use the actionPerformed method to retrieve form data and hold it in session, you avoid problems with form data not getting reposted when the the portal page is refreshed.

With this pattern, you can easily determine where and when you want data to be retrieved from the source, and when it should be retrieved from cache. Because actionPerformed methods are not invoked on portal page refreshes, you can place data access code in the action state and cache them there. The state classes can use data from cache in order to avoid multiple trips to get data, when the portal page is refreshed.


Implementation specifics

Consider the following scenario. You have a portlet that presents a list of items on the main page to the user; the user can select one item to get more detailed information, which is presented on another page. The user is also able to to add, edit, and delete items. The data is persisted in a database. In this example, the items are contacts, as in an address book.

From a visual perspective, an implementation of the above scenario can have the following pages:

  • Main view page - Displays the list of contacts with an option to select one contact for more information
  • Detail view page - Displays the selected contact's information
  • Main edit page - Displays the list of contacts with options to add, delete, or modify
  • Add entry page - Displays a form to get the contact information to be added
  • Modify entry page - Displays a similar form with the existing data inserted for modification

In this case, you do not have an explicit page to delete an entry. The user can select a contact entry to delete from the main edit page. There is no confirmation page or successful execution page. Entry deletion processing occurs, and the main edit page is refreshed. If an error occurs, an appropriate message displays, but under normal processing, there is no view associated with this action.

Assume you want the Main and Detail view pages available to users who have view permission for the portlet; you want add, edit, and delete capabilities available to users who have edit permission for the portlet. In this case, the Main and Detail view pages are controlled from the doView portlet method. The remaining pages are controlled from the doEdit method.

Using the standard approach

First, consider the doView method. Using standard portlet programming techniques, you can implement it as follows:

public void doView(PortletRequest request, PortletResponse response)
   throws PortletException, IOException {

   String oid = request.getParameter("selectedContact");
   if (oid == null) {
      // Main view processing goes here
      portletContext.include("main_view.jsp", request, response);
   else {
      // Detail view processing goes here
      portletContext.include("detail_view.jsp", request, response);
   }
}

However, you realize that if you attempt to retrieve the selectedContact index from the HTTP request on every refresh, then, when your portlet is refreshed as a result of the portal page being refreshed, the form data is lost and this implementation returns the portlet to the main view - even if the user was working with a different portlet on the page. Therefore, you use an action listener to get the selected contact value and set it on session so that future refreshes can get the correct value and preserve the appropriate page setting.

Now the doView code looks like the code below; the actionPerformed method, implemented in the action listener, retrieves the appropriate value and puts it on session. Of course, you also need to specify the action event on the portlet URI so that the listener is called.

public void doView(PortletRequest request, PortletResponse response)
   throws PortletException, IOException {
   PortletSession session = request.getPortletSession(false);
   if (session != null) {

      String oid = (String)session.getAttribute("oid");

      if (oid == null) {
         // Main view processing goes here

         // URI and action listener for showing the contact details
         PortletURI portletURI = response.createURI();
         portletURI.addAction("detailRequest");
         request.setAttribute("detailURI", portletURI.toString());

         portletContext.include("main_view.jsp", request, response);

      else {
         // Detail view processing goes here
         // Get bean for given OID

         session.removeAttribute("oid");
         portletContext.include("detail_view.jsp", request, response);
      }
   }
   else
      response.getWriter().println("You must log in first");
}


public void actionPerformed(ActionEvent event) throws PortletException {
      String actionName = event.getActionString();
      PortletRequest request = event.getRequest();
      // Show the contact detail view. Get the contact oid number
      // of the selected contact and put it on the session object
      if (actionName.equals("detailRequest")) {
         String oid = request.getParameter("selectedContact");
         request.getSession(true).setAttribute("oid", oid);
      }
}

There is quite a bit of code here to simply manage the page transitions between the main view and detail views. You have not yet written any code for the application's business logic. There are many opportunities for bugs to be introduced into this code. Also, you need to implement component pieces in various places to make the process flow successfully; often these components rely on string matching to hook up an event with an appropriate action or set and read a flag in the code that manages process control.

Including the additional actions to add, edit, and modify the control code would make the code more cumbersome. The following code source shows the actionPerformed method for this implementation. This does not display the corresponding code in the doEdit method that needs to set portletURI with the appropriate action, interrogate the event flags, and set and remove data and flags from session.

public void actionPerformed(ActionEvent event) throws PortletException {

       String actionName = event.getActionString();
       PortletRequest request = event.getRequest();
       PortletSession session = request.getPortletSession();

       //  Show the contact detail view.  Get the contact oid number
       //  of the selected contact and put it on the session object
       if (actionName.equals("detailRequest")) {
          String oid = request.getParameter("selectedContact");
          session.setAttribute("oid", oid);
       }

       // Handle the request to go to the add view
       if (actionName.equals("ADD_REQUEST")) {
          session.setAttribute("NEXT_EDIT_PAGE", "ADD_PAGE");
       }

       // Handle the request to go to the edit view
       if (actionName.equals("EDIT_REQUEST")) {
          session.setAttribute("NEXT_EDIT_PAGE", "MODIFY_PAGE);
       }

       // Handle the reqeust to delete content
       if (actionName.equals("DELETE_REQUEST"))
          ContactsManager.getInstance().(deleteContact(request));

       // Handle the Add Content event
       if (actionName.equals("ADD_CONTACT"))
         ContactsManager.getInstance().addContact(request));

       // Handle the Modify Content event
       if (actionName.equals("EDIT_CONTACT"))
       ContactsManager.getInstance().modifyContact(request));
}

How does this change with the state pattern?

How can you improve this implementation? First, keep in mind that this application represents a collection of application actions and states. An action is a class that implements the action interface, and literally handles the processing for a specific application task or action. This is a portion of application code that today exists in the actionPerformed method of the actionListener class; only this portion is specific to a single action event.

A state is a class that implements the state interface and represents the effect of the portlet as a result of applying an action. This class typically has a visual component.

Such an application would typically contain the following:

Actions:

  • Show Main Page
  • Show Detail Page
  • Show Main Edit Page
  • Show Add Contact Page
  • Add a Contact
  • Show Modify Contact Page
  • Modify a Contact
  • Delete a Contact

States:

  • Main View Page
  • Detail View Page
  • Main Edit Page
  • Add Contact Page
  • Modify Contact Page

Now, determine the state transitions available for this application. The transitions are managed by applying the appropriate actions to the current state, which results in the application entering another specific state.

Figure 2. A state diagram
A state diagram

With this understanding of the portlet, you can create this application using the state pattern. The application is built using a common structure for state transition that lets you eliminate the excessive if statements with string matching and multiple flags being set.


How does the state pattern work?

The code below shows the simple processing in the doView method in the StateManagerPortlet class. The other do methods would be similar. You could easily collapse this processing to the service method. In this case, you would look for the state object from session using the portlet mode as the key. It is displayed this way because you would typically code to the do methods and the comparison would be more familiar. Also, you would extract the default state into a properties file or another initialization parameter to remove the connection to this class.

Public void doView(PortletRequest request, PortletResponse response)
   throws PortletException, IOException {
   // Ensure we are logged in
   PortletSession session = request.getPortletSession();
   User user = request.getUser();
   if (session == null) || user == null)
      throw new MessageException("Login to the portal first");
   //  Get the portlet state object from session, if not there get the initial
   //  state for the mode.
   State nextState = (State) session.getAttribute(request.getMode().toString());
   if (nextState == null)
      nextState = InitialStateManager.getInitialState(request.getMode());

   // Dispatch to state handler
   nextState.performView(request, response,
      getPortletConfig().getContext());
}

The code below shows the simple processing in the actionPerformed method of the StateManagerPortlet class. Again, this method simply gets the action class instance identified by the action name string and invokes its actionPerformed method.

Public void actionPerformed(ActionEvent event) throws PortletException {

   // Execute the perform action method for the event
   PortletContext portletContext = getPortletConfig().getContext();
   PortletRequest request = event.getRequest();
   //  Get the action handler class and dispatch
   String actionClassName = event.getActionString();
   Action action = ActionClassManager.getAction(actionClassName);

   //  Dispatch to that class event handler; register the next state
   action.actionPerformed(request, portletContext);
   action.setState(request);
}

Then, the actionPerformed method of the action classes performs normal processing and also puts the proper state on the session for appropriate flow control. Keep in mind that the action and state classes in general have no or very few fields. So, there is minimal expense in adding them to the session object.

Because no changes are needed to implement this pattern in the portlet do methods, you do not show the state classes perform method equivalents. However, you may extend the pattern here to include a refresh method for the state. The refresh method would logically extract the application code that is responsible for portlet data retrieval, so that you can isolate the logic needed to determine if a portlet refresh causes the source data to refresh or be retrieved from a cache.

Before the state class is asked by the StateManagerPortlet to write an HTML fragment to the response (or invoke a JSP to do this), the StateManagerPortlet can ask the state class to refresh itself. In the refresh method, you can include logic to refresh or re-retrieve data for the display.

For example, a portlet that displays volatile data may need to go back to the data source each time the screen is refreshed. You would include the logic to do this in the refresh method. On the other hand, a portlet that displays stable information would more likely put the data access logic in the initial state lazy initialization logic and cache the results. The refresh method for this type of stable data state would do nothing.

Because business logic in the refresh method could cause a transition to a different state (including an error state), you could extend the state pattern to allow for state transitions from within states.


Conclusion

As WebSphere Portal becomes the preferred platform for enterprise portal applications, it becomes increasingly more critical to have well-defined frameworks and patterns for portlet development. This article discussed the need for a pattern to manage complex application page flow within a portlet, which results in efficient processing and clean code that lets you easily debug, maintain, and enhance an application.

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=13201
ArticleTitle=Applying the State Pattern to WebSphere Portal V5 Portlets: Part 1. Overview
publish-date=12032003