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

This is Part 1 in a series that discusses how to apply the state pattern to your portlets so that you end up with a cleaner portlet implementation. Part 1 provides an overview of the state pattern and how it works.

Share:

Tim Hanis (mailto:hanistt@us.ibm.com), Senior Consultant, IBM Raleigh Lab

Tim Hanis is a senior consultant for IBM WebSphere Portal at IBM Research Triangle Park Lab in Raleigh, North Carolina. You can reach Tim at hanistt@us.ibm.com.


developerWorks Contributing author
        level

Skyler Thomas (mailto:tskyler@us.ibm.com), Executive Consultant, IBM Raleigh Lab

Skyler Thomas is an executive consultant for IBM WebSphere Portal at IBM Research Triangle Park Lab in Raleigh, North Carolina. You can reach Skyler at tskyler@us.ibm.com.



Chris Gerken (mailto:cgerken@us.ibm.com), Senior Software Engineer, IBM Austin Lab

Chris Gerken is a senior software engineer for IBM WebSphere Portal at the IBM Austin Lab in Austin, Texas. You can reach Chris at cgerken@us.ibm.com.



11 December 2002

© Copyright International Business Machines Corporation 2002. All rights reserved.

Introduction

IBM ® WebSphere® Portal (hereafter called Portal) is quickly becoming a standard platform for building enterprise class applications. Moving beyond the simple HelloWorld portlet, developers are starting to establish standard frameworks for building portlets. Many customers want to build portlets that display extremely complex behavior. Currently, the J2EETM model provides well-defined frameworks to handle complex behaviors. Developers can use Struts, EAD4J, Turbine, and even JSP Model-2 Model-View-Controller (MVC) strategies to address complexities of page flows in the servlet world. Portal itself provides a page-level navigation framework. However, presently, developers need to handle the behavior of an individual portlet. When creating more 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.

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 this problem of how to best implement 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 since you need to wade through the control logic to get to the real work that the portlet is doing. The control logic is also prone to error because it relies on 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 since 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 create a reusable state pattern to address this problem generically. You will find that applying the state pattern to your portlet results in a cleaner portlet implementation.


Problems with the standard implementation approach

A simple MVC-based portlet can present a list of items that are retrieved from a database and then sent to a main view for the user to select. When the user selects an item, a detailed view is displayed showing specific information about the item selected. This might involve another request to the database. The important point here is that a requirement now exists to follow two different control flows.

In the first scenario, the controller must call the business objects to create the appropriate beans, which are passed to a JSP to render in the main view. In the second scenario, you must retrieve an indicator of the selected item from the user's request. You would call the business objects to create the appropriate bean, which would then be 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 these 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, and 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 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, and what to render back to the user.


The components of a state pattern implementation

Applying the state pattern to portlets lets developers cleanly implement process control. The state pattern uses the following components:

  • StateManagerPortlet. This is the main portlet class. It is portlet-independent, and is where you typically write all of the portlet-specific code. The class serves as a dispatcher to support action and state classes where the portlet code resides. StateManagerPortlet implements the actionPerformed method and the 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 will occur properly, without you having to code much extraneous control logic.
  • Action. Classes that implement this interface will 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 gets called, it performs work specific to its function, and then sets the state for the next transition.
  • 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 will invoke a JSP to render its results. The UI 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.
  • StateURI custom tag. This class provides a custom tag for the JSP to use. As mentioned above, the JSP associates an action instance with an action on the page. This association is performed through the generated PortletURI. This custom tag provides the function to generate the URI given an action class name. You can remove this URI creation code from the portal code. With this tag and the action state structure, you do not need to provide code to manage state transitions within the portlet.

Actions and states

As mentioned earlier, applying the state pattern to portlets results in a cleaner portlet implementation. Also, 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, then you avoid any problems with form data not getting reposted when the the portal page gets refreshed. With this pattern, you can easily determine where and when you want data to be retrieved from the source, and when it should retrieved from cache. Since actionPerformed methods do not get invoked on portal page refreshes, you can place data access code in the action state and cache them there. The state classes may use data from cache 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 behavior is such that the user can select a contact entry to delete from the main edit page. There will be 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 will be displayed, but under normal processing, there isn't a view associated with this action.

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

Using the standard approach

First, consider the doView method. Using standard portlet programming techniques, you can implement 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 gets refreshed as a result of the portal page getting 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, 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, and there is an implementation for the actionPerformed method in the action listener that 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 gets 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(); 
         PortletAction portletAction =  
            new DefaultPortletAction("detailRequest"); 
         portletURI.addAction(portletAction); 
         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 { 
   DefaultPortletAction portletAction = 
      (DefaultPortletAction) event.getAction();  
   if (portletAction instanceof DefaultPortletAction) { 
      DefaultPortletAction action = (DefaultPortletAction) portletAction;  
      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 (action.getName().equals("detailRequest")) {  
         String oid = request.getParameter("selectedContact");  
         request.getSession(true).setAttribute("oid", oid);  
      }  
   }  
}

There is quite a bit of code here to manage simply the page transitions between the main and detail views. You haven't written any code yet 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, set and remove data and flags from session.

public void actionPerformed(ActionEvent event) throws PortletException { 
 
    DefaultPortletAction portletAction =  
      (DefaultPortletAction) event.getAction();  
    if (portletAction instanceof DefaultPortletAction) {  
 
       DefaultPortletAction action = (DefaultPortletAction) portletAction;  
       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 (action.getName()Equals("detailRequest")) {  
          String oid = request.getParameter("selectedContact");  
          session.setAttribute("oid", oid);  
       }  
 
       // Handle the request to go to the add view  
       if (action.getName()Equals("ADD_REQUEST")) {  
          session.setAttribute("NEXT_EDIT_PAGE", "ADD_PAGE");  
       }  
 
       // Handle the request to go to the edit view  
       if (action.getName()Equals("EDIT_REQUEST")) {  
          session.setAttribute("NEXT_EDIT_PAGE", "MODIFY_PAGE);  
       }   
 
       // Handle the reqeust to delete content  
       if (action.getName()Equals("DELETE_REQUEST"))  
          ContactsManager.getInstance().(deleteContact(request));  
 
       // Handle the Add Content event  
       if (action.getName()Equals("ADD_CONTACT"))  
         ContactsManager.getInstance().addContact(request));  
 
       // Handle the Modify Content event  
       if (action.getName()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 1. 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 since 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(); 
   if (session == null) 
      throw new MessageException("Login to the portal first"); 
   // Get the state request from session; main view as default 
   State nextState = (State) session.getAttribute(Action.VIEW_STATE); 
   if (nextState == null) 
   nextState = new MainViewState(); 
 
   // 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 current action class instance 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();  
   Action action = (Action) event.getAction();  
 
   action.actionPerformed(request, portletContext);  
}

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. The code for the actionPerformed method for the action class that adds a contact would resemble:

public void actionPerformed(PortletRequest request,  
   PortletContext portletContext) { 
   // Add the contact 
   ContactHelper contactHelper = ContactHelper.getInstance(); 
   contactHelper.addContact(request); 
   // Set next portlet state - Main Edit 
   State nextState = new MainEditState(); 
   PortletSession session = request.getPortletSession(); 
   session.setAttribute(EDIT_STATE, nextState);   
}

Since 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 to 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.

Since 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 discusses 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=14276
ArticleTitle=Applying the State Pattern to WebSphere Portal Portlets : Part 1 -- Overview
publish-date=12112002