The Source for Java Technology Collaboration
User: Password:



   

Transparent State Management Using the Decorator Pattern Transparent State Management Using the Decorator Pattern

by Sharfudeen Ashraf
01/09/2007

What Is Transparent State Management?

In the context of a Java EE web application, the term transparent state management refers to a mechanism that is capable of maintaining state across multiple requests, at the same time remaining completely invisible to the underlying components that make use of it. In other words, if such a mechanism is in place, then web applications can acquire statefulness without having to explicitly deal with state management APIs such as HttpSession. In many scenarios, this would provide a better alternative to the traditional way of explicitly handling session management APIs. The article explains where transparent state management would be useful, and discusses a reusable solution to implement transparent state management. It should be noted that for the purpose of understanding, this article uses Struts as an example web framework to explain the subsequent sections.

Where Do We Need Transparency in State Management?

Before we go further into transparent state management, we need to make sense of the context where actually this could be useful. So let us look at two example scenarios that will help us understand the context.

First, let us consider a typical shopping cart application--this application allows the user to browse a limited number of pages without requiring him to be logged in. Each of these pages also has a separate link to a login page, which will conditionally appear if the user has not logged in yet. If the user wants to access restricted pages of this application, he will click on the login link, which will take him to a login page. After a successful authentication, the login page will bring the user back to the previous page from where he accessed the login link.

Another example is a typical business application with lots of data entry forms. Some of these data entry forms have links to other pages. For the sake of understanding, let us call the data entry page the parent page and the pages to which we have links child pages. Users who are in the middle of entering some data in the parent page may optionally access the link to a child page in order to verify or collect some additional details and can always return back to the parent page to continue with their current activity.

The two example scenarios described have similar user interaction patterns: users who are working in a given page make a short trip to another page and come back to the original page. In the shopping cart application, they go to the login page and come back, while in the business application they go to a child page and return back to the parent page. In both cases, when the users return back to their respective previous pages, the application should display all the input values entered in the previous page, which requires the state of the page the user is leaving to be saved, so as to show those values when he returns.

Generally this is achieved by storing all input fields of the current page in the session as an object before navigating to another page. When the user returns, we retrieve the values from the session and show them. If the application has one or two interactions, then the general solution of storing the form values explicitly in the session and retrieving it back won't add any overhead in terms of effort. But if the same pattern repeats across several pages in the application, then it would force us to repeat the same scaffolding logic of storing objects in the session when the user temporarily moves out of the main page and retrieve it when she returns back.

It is exactly these kinds of scenarios where managing state transparently would provide a better solution. Essentially, transparent state management provides a design to abstract the task of storing and retrieving the information in the session without any coding effort. It could be argued that frameworks like Struts have already simplified and made session management declarative by simply requiring the developer to configure the scope of the form to session. However, in the context of the examples described earlier, it is still not the best approach for the following reasons.

  • Frameworks like Struts support declarative session management by providing a configuration file where we specify the action form in the action mapping to be session scoped. Similarly, JSF, Tapestry, and other popular frameworks all have their own configuration files where we specify the scope of the object. The framework uses this object to bind the request parameters for this specific flow, which is known either by URL or by any other information passed as part of the request. After data binding, this object will be stored in the session for future reference. It should be noted that every object that needs to stored in the session should have a corresponding entry in the configuration file. For example, if we were to use Struts, we would need to configure which form object should be used to bind request parameters against a certain URL.

    Given our login page and parent-child data entry examples, if the same scenario repeats in several pages, then the need to store the state of the form in session scope would force us to repeat the same logic in terms of configuring additional action mappings for every page. What this implies is that a single solution cannot be reused in all the pages that require the same functionality, though the pattern of the user interaction is similar in all these scenarios.

  • Though many frameworks provide a simple configuration option to specify the object corresponding to the HTML form to have session scope, they can't decide when to remove the objects from the session, leaving that decision to the developer. However, if the developer explicitly removes the form object from the session every time after it is used, then the solution is not transparent anymore. On the other hand, if the objects are not removed from the session after they're used, they remain in memory as long as the session is alive and increase the memory footprint of the server.

    The requirement to support this kind of user interaction in many pages would make their corresponding objects session scoped, and if we combine this with the fact that web frameworks don't transparently remove the objects from session once they are used, we may soon realize that this way of solving the problem might turn the larger part of an otherwise stateless application into a stateful application.

This article provides a reusable design to address the aforementioned issues in the following section.

Design Approach

The proposed design has two main components:

  • First, when the user navigates to a child page from the parent page, the input parameters of the parent page are captured in a Map structure and stored in session by a servlet filter called RequestRetainAndRestoreFilter.The decision to store the input request parameters in a generic Map instead of using objects obviates the need to have different object structures to store details pertaining to different pages.

  • Secondly, when the user comes back to the parent page after visiting the child page, the previously stored Map of parameters are exposed as part of the current request to the parent page. This ensures the values entered in the parent page are not lost when the user comes back from the child page. The piece of functionality that combines the current request as well as the previously stored requests is handled by a component called SessionStateDecorator.

RequestRetainAndRestoreFilter

The purpose of this filter is to copy the request parameters during a particular request and resend them as part of another request. In order to do this, we need to specify when the servlet filter should perform these two operations. Generally, this can be achieved by mapping these two operations against two different URLs. They are:

  • retainRequestAndForward.do: When the filter intercepts this URL, it copies all the request parameters in a Map and stores them in session, as depicted in Figure 1.

    Retain sequence
    Figure 1. Retain sequence

  • restoreRequestAndForward.do: When the filter intercepts this URL, it puts all the copied parameters back into the current request. This is achieved by decorating the current request object using the already-captured request parameters, as depicted in Figure 2. At the end of this request, the servlet filter also removes the parameter map stored in the session. By doing this, the filter relieves the developer from the responsibility of manually removing the session and eliminates the possibility of too many objects floating around and eventually bloating the memory footprint of the server.

    restore sequence
    Figure 2. Restore sequence

All other URLs of the application are simply delegated down the filter chain without any specific action taken by the filter. If we extend this same concept to multiple scenarios, then each such scenario would require a separate pair of URLs in this list, which will do the job of intercepting and restoring their corresponding request values. However, doing so will make the filter code cumbersome and difficult to maintain.

In order to overcome this problem, a generic action class is mapped against these two URLs, which will in turn forward to the actual target action based on a runtime parameter. This particular action class is called Proxy Action. So if the current page's parameters need to be stored in the session before moving to another page, then the current page should call retainRequestAndForward.do and it should pass the name of the actual child page (or another action) as an additional parameter that will then be forwarded by the Proxy Action.

This design achieves two purposes: first, by referring to retainRequestAndForward.do, the servlet filter would know that it has to store the current request parameters, and secondly, since retainRequestAndForward.do is mapped against a Proxy Action, the target page (action) will finally be forwarded by this Proxy Action. This class can also be used as a placeholder if additional logic is required while navigating to a different page and reverting back to the original page. When the user wants to come back to the parent page, we would refer to restoreRequestAndForward.do and pass the parent page's action as the runtime parameter; this time, the servlet filter would take the stored parameter map from session and pass it as part of current request. Since the runtime parameter now refers to the parent page, the proxy action will forward the request to that page.

Configuring web.xml

Instead of directly referring to retainRequestAndForward.do and restoreRequestAndForward.do, the servlet filter uses two init parameters called retain and restore to get the corresponding values of the URL. This indirection allows us to specify URLs adhering to naming conventions specific to different frameworks. The examples accompanying the article uses the following configuration values:

<filter>

<filter-name> RequestRetainRestoreFilter</filter-name>

<filter-class>
decoratorexample.filter.RequestRetainRestoreFilter
</filter-class>

<init-param>
<param-name> retain</param-name>
<param-value> retainRequestAndForward.do</param-value>
</init-param>

<init-param>
<param-name> restore</param-name>
<param-value> restoreRequestAndForward.do</param-value>
</init-param>

</filter>

SessionStateDecorator

The SessionStateDecorator exposes stored session parameters as part of the HttpRequestRequest, thereby obviating the need to explicitly deal with the HttpSession object. However, modifying the HttpServletRequest object to include the stored parameters taken from the session is not possible, because the HttpServletRequest interface doesn't have a provision to set request parameters manually. It is also not possible to extend the HttpServletRequest interface to add the desired functionality because the responsibility of creating the actual instance of HttpServletRequest lies with the servlet container; what we need is the ability to dynamically add behavior in runtime, to an already created object.

It is in this context the Decorator pattern comes to our rescue. The Decorator pattern works by wrapping the original object with a new "decorator" object, to provide extra behavior to the original object without subclassing it. Since the decorator implements the same interface as that of the object that it decorates, it has to implement all the methods of the interface, but most of the methods simply call the original object except for a few methods. Since we are trying to add dynamic behavior to HttpServletRequest, our decorator class should actually forward all methods to the original HttpServletRequest object and customize the few methods that are of interest to us. However, we don't have to write the repetitive forwarding logic for each method of HttpServletRequest, because as of version 2.3 of the Servlet API, we are provided with a convenient implementation of the HttpServletRequest interface called HttpServletRequestWrapper, which by default forwards all its method calls to the HttpServletRequest it decorates. So the SessionStateDecorator in our example extends this class and overrides specific methods to provide customized logic.

While creating the SessionStateDecorator, both the current request object as well as the parameters copied in the session during previous request are passed to its constructor; by doing so, the SessionStateDecorator becomes a single repository of both the current and the previously captured request parameters. The responsibility of the SessionStateDecorator is to override the getParameterMap method to return a Map that contains both the current request parameters and the parameters stored in the previous request.

The other overridden methods such as getParameterValues() and getParameter() check the given parameter name against the current request object; if the value for the parameter name is not present, then it uses the saved parameter Map to retrieve the value corresponding to the name. Since the decorator implements the same HttpServletRequest interface, the components dealing with it will continue to see the same HttpServletRequest object and remain oblivious to the fact that stored parameters are provided as part of the request instead of the session. This approach completely hides the fact that the user has moved to another page in the middle of her task, as the server-side code doesn't have to look for any session-scoped parameters to restore the state of the page.

SessionStateDecorator.java

The following source code shows how SessionStateDecorator overrides the methods of HttpServletRequestWrapper to provide additional values stored in session as part of the current request.

public class SessionStateDecorator extends HttpServletRequestWrapper {

    Map retainedParams;

    public SessionStateDecorator(HttpServletRequest request, Map params) {

        super(request);
        this.retainedParams = params;

    }

    public String getParameter(String paramName) {

        if (!StringUtils.isEmpty(super.getParameter(paramName)))
            return super.getParameter(paramName);

        return (String) retainedParams.get(paramName);
    }

    public Map getParameterMap() {

        Map allParamsMap = new HashMap();
        allParamsMap.putAll(retainedParams);
        allParamsMap.putAll(super.getParameterMap());

        return Collections.unmodifiableMap(allParamsMap);
    }

    public Enumeration getParameterNames() {

        Vector allParamNames = new Vector();
        Enumeration parentParams = super.getParameterNames();

        while (parentParams.hasMoreElements()) {

            allParamNames.addElement(parentParams.nextElement());

        }

        Iterator paramNameItr = retainedParams.keySet().iterator();
        while (paramNameItr.hasNext()) {

            allParamNames.add(paramNameItr.next());
        }

        return allParamNames.elements();
    }

    public String[] getParameterValues(String paramName) {

        if (super.getParameterValues(paramName) != null)
            return (String[]) super.getParameterValues(paramName);

        return (String[]) retainedParams.get(paramName);

    }

}

Understanding the Examples

The example code (see Resources) includes four JSPs. The "Customer Entry" and "Region Search" pages show how the values entered in the Customer Entry are restored when the user returns to this page after moving to the Region Search page in the middle of data entry. The other example (Product Entry/Manufacturer Search) demonstrates how the same filter can be utilized to any number of such UI interactions in the application without adding any new classes or configuration.

Summary

This article demonstrated how to manage session state in a web application by simply delegating the request through different URLs. It also explained how to make use of the Decorator pattern to provide transparency to this solution. Though the example provided in this article is designed to manage the state of only a single request, the concept can be easily extended to support wizard-like scenarios by maintaining a stack of Maps of request parameters. In this case, one more URL by the name of retainMoreAndForward can be used to differentiate between retaining parameters pertaining to a single page against a series of pages as in a wizard.

Resources

Sharfudeen Ashraf is a Sun Certified Enterprise Architect, working as Technology Specialist in HCL Technologies India.

View all java.net Articles.

 Feed java.net RSS Feeds