As I mentioned in a previous blog, I’ve been playing around a little with the Click framework in the hopes of having stumbled on a front end framework that is suitable for people like me (who really don’t like the front end). But while I think that Click is quite clever in a number of its ideas, it is not quite what I was hoping.

So being somewhat disappointed with the results of my experiment, I got to thinking about why I don’t like the front end and what should change. And I’ve come to the conclusion that at the very least a large part of what I don’t like is session management as it has been defined by Sun on the server side.

In this blog entry I will explain where I think JEE session management causes you to run off the rails and what I think might be an alternative.

The ideal architecture

In order to explain my likes and dislikes, let me start by describing what I consider to be the “ideal architecture” of the modern business application: the architecture that allows maximum reuse of the business core of the application and maximum flexibility with respect to front end exposure and backend service use.

Somewhat unsurprisingly, this architecture is very similar to the standard three-tier application architecture. As a diagram it looks like this:

Ideal Architecture Diagram

This architecture positions the core business functionality in a center tier, protected from backend services (like web services and databases) by abstraction layers organized by topics. On the other side the core business services of the center tier are exposed to the outside world through a single point of access (a service layer). Any consuming components of these core business services (through the service layer) access these services without being known to the center tier.

The main advantages of this ideal architecture have already been described: the architecture envisions a layer of core business services that depends on nothing but a number of abstract backend services — meaning that the layer can be plugged into any service collection that offers the required functionality. At the same many different exposing layers can be hooked into the core business layer, which means that its services can be thrown onto a website using JSPs, JSF, another framework or in a completely different form (like a collection of web services or exposed through JMS).

Going off the rails with session management

An essential part of making the ideal architecture work correctly is in having all aspects of the core functionality in the central layer. And none of it in any other layer (like a front end layer). Now, that sounds obvious; but since it is so important I will say it again: none of the core functionality should be outside the core layer. And this is where the JEE model gets it wrong (in my opinion): the JEE model attaches all session management to the front end technologies [0].

Session management is that part of server side technology that pertains to maintaining state across multiple requests from the same client within a relatively short period of time. Its effect is felt in all sorts of aspects of server side applications, such as maintaining a shopping cart or storing form contents (which is important with regards to the infamous “back-button problem” in web applications). These examples show, by the way, that while some parts of session management actually belong in the center tier because they are part of the core functionality (like shopping cart management) others really do belong in the front end (form contents). Which probably explains why Sun’s JEE platform puts all of it in the front end: when the Enterprise Edition got started, the very visual web front end was all that there was so it seemed to fit together.

In any case, the problem is that having session management based totally in the front end (in the form of the javax.servlet.http.HttpSession, in the case of servlet technology) makes it natural and necessary to pull some functionality from the center layer into the front end. Most shopping cart applications for instance save the “cart” in the front end session (although there are exceptions like Amazon). The result is this:

JEE session architecture

As you can see, there is now a dependency from the center tier business components to the front end component, specifically the session management part. Just to be clear, I’m not saying there will necessarily be a type dependency in your code (although I wouldn’t be surprised if a number of people had dealt with this situation by passing a javax.servlet.http.HttpServletRequest to the business layer and giving the business services direct access to the HttpSession). But the dependency will be there, often in an invisible way: the business services will depend for their operation on the session management in the front end to hold on to domain objects which the front end can receive in one request and pass back to the center tier in the next request. Which is bad, since it (implicitly) binds the center tier to the front end (i.e. to the front end functionality).

In the ideal architecture, the front end layer should obey the simple behavioral pattern of request-format response data-response. Limiting the front end layer to this simple pattern isn’t possible if the center tier relies on session management functionality from the front end — the front end will have to be equipped with knowledge to store domain objects from the center tier in the session and retrieve the correct object from the session at the right time to pass it to the center tier. It also makes it very difficult to connect the center tier to another front end layer (or front end technology), since another front end must provide the same session management functionality in order to allow the enter tier to function correctly.

Doing it another way

What is needed to solve this, I think, is a form of session management that is directly accessible to the business layer. Having session management in the center tier allows you to have session management, yet maintain the independence of the business code from the front end layer (which provides the session management). However, simply making the front end session management directly accessible to the center tier is not a complete solution (you could accomplish that simply by passing the front end session object to the center tier with every call); if you simply export the front end session management to the center tier, you are still binding the two layers to one another.

What is needed is to introduce a session management mechanism into the center tier that is not itself bound to the front end session management mechanism — the center tier should have session management of its own. Doing so maintains the independence of the center tier; the center tier can still be moved from server to server, be plugged into back ends and front ends transparently without binding to them while at the same time allowing the center tier to take all of its functionality with it wherever it goes.

There are, I think, two ways to introduce session management for the business layer:

  1. Use some separate, platform independent library to introduce separate session management for the business layer
  2. Make the front end session management accessible to the business layer through an abstraction layer

A separate library

Introducing separate session management (using a library or possibly a homegrown solution) is probably the most straightforward solution of the two. You plug in a jar and you’ll probably have to refactor the exposed business services so that they always get some sort of token with every request that allows your business layer session management to retrieve the session state uniquely.

The major downsides of this option include that the fact that you will probably be duplicating functionality that is already available on the server and that you will have to expose part of your session management mechanism to the front end (leaving open the possibility of errors, security risks and of course violating the “don’t show your guts” principle of software engineering). But it is simple and portable.

Abstracting the front end session management

The second option is a bit more involved to arrange, but is probably cleaner in the long run: you still make use of the front end session management but in a way that doesn’t bind the business layer to the front end. The way to do this is to expose the front end session management to the center tier in such a way that the center tier has no type dependencies on the front end session management. The architecture looks something like this:

Abstract session management

This architecture introduces an abstract session management component for use by the center tier. This abstract session management component provides access to the session management provided by the front end, but in such a way that the center tier has no type dependency on that session management component. This way, if you move to a front end with a different session management component you don’t have to touch the center tier, just as long as the new session management component has functionality similar to that of the old one.

Trying it out

In order to see if there is any hope for this idea, I tried to implement it myself with a small library. The idea I had was to define two abstract types for use by the center tier (Session and SessionManager) which would provide the business layer with abstract access to session management. In my back-of-a-napkin design the Session represents a session that you can store objects in using a key object (just like in an HttpSession). The SessionManager allows for session management: creating and retrieving Session instances as well as terminating (discarding) existing instances. This should form the core of the “library”:

Session exposer core

As you can see above, Session is an interface; it simply defines a generic type of session and implementations of this interface for a specific session management mechanism should be adapters between specific session classes and the interface defined by this type. The SessionManager on the other hand is an abstract class that also follows the singleton pattern. This allows simultaneously to have specific implementations for each session mechanism necessary (with knowledge on how to get a specific session object) and to make getting hold of a specialized session manager type transparent to the business layer; the getInstance() method in this case makes use of a properties file which contains the name of the specialized session manager to use for the current session management mechanism.

In order to be of practical use this abstract core should be extended with functionality that provides specific implementations for specific session management mechanisms. As a test case I implemented a bridge to the session management mechanism of the JEE platform (embodied by the HttpSession interface).

As described above, the implementation of Session should merely be an adapter, a wrapper class around the HttpSession. As such it is remarkably unspectacular:

class HttpSessionBridge implements Session
{
        /** The actual session. */
        private final HttpSession       realSession;

        /**
         * Constructor.
         * 
         * @param httpSession The real session that this adapter class delegates to.
         */
        protected HttpSessionBridge(final HttpSession httpSession)
        {
                this.realSession = httpSession;
        }

        /**
         * {@inheritDoc}
         */
        public Object clearKey(final Object key)
        {
                if (String.class.isInstance(key))
                {
                        Object currentValue = realSession.getAttribute((String) key);
                        realSession.removeAttribute((String) key);
                        return currentValue;
                } else
                {
                        throw new InvalidSessionAttributeKeyException("Key to clear is not of type String", key);
                }
        }

        /**
         * {@inheritDoc}
         */
        public void clearSession() throws UnsupportedOperationException
        {
                Enumeration keys = realSession.getAttributeNames();
                while (keys.hasMoreElements())
                {
                        String key = (String) keys.nextElement();
                        realSession.removeAttribute(key);
                }
        }

        /**
         * {@inheritDoc}
         */
        public Object getAttribute(final Object key) throws InvalidSessionAttributeKeyException
        {
                if (String.class.isInstance(key))
                {
                        String keyString = (String) key;
                        Object result = realSession.getAttribute(keyString);
                        return result;
                }
                throw new InvalidSessionAttributeKeyException("Key is not a String object", key);
        }

        /**
         * {@inheritDoc}
         */
        @SuppressWarnings("unchecked")
        public SetgetKeys() throws UnsupportedOperationException { Setset = new HashSet(); Enumeration enumeration = realSession.getAttributeNames(); while (enumeration.hasMoreElements()) { set.add(enumeration.nextElement()); } return set; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public ListgetValues() throws UnsupportedOperationException { Listlist = new ArrayList(); Enumeration enumeration = realSession.getAttributeNames(); while (enumeration.hasMoreElements()) { list.add(realSession.getAttribute((String) enumeration.nextElement())); } return list; } /** * {@inheritDoc} */ public void setAttribute(final Object key, final Object value) throws InvalidSessionAttributeKeyException, InvalidSessionAttributeValueException { if (String.class.isInstance(key)) { String keyString = (String) key; realSession.setAttribute(keyString, value); } else { throw new InvalidSessionAttributeKeyException("Key is not a String object", key); } } } 

The SessionManager is a lot more interesting. Its role is to provide access to the JEE HttpSession object; since this access is only available through the javax.servlet.http.HttpServletRequest, the session manager must be able to access the request provided by the servlet container. I will return to the question of how this is done a little later.

In order to hold on to the HttpServletRequest and remember which call to the business services belongs to which request (since there may be multiple being handled concurrently) the SessionManager uses a java.lang.ThreadLocal instance. This under the assumption that each request is being handled by a single thread at any given time. This, by the way, is also the major weak spot of my little trial implementation: if the business services spawn internal threads, they will not be able to access the session correctly (or at all).

With the ThreadLocal instance in place, the rest of the SessionManager implementation is straightforward:

public class HttpSessionManager extends SessionManager
{
        /** The store for servlet requests during the lifetime of that request. */
        private static final ThreadLocal<httpservletrequest>    servletRequestStore;

        static
        {
                servletRequestStore = new ThreadLocal<httpservletrequest>();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Session getSession()
        {
                Session session = null;
                HttpServletRequest httpServletRequest = servletRequestStore.get();
                if (httpServletRequest != null)
                {
                        HttpSession httpSession = httpServletRequest.getSession();
                        session = new HttpSessionBridge(httpSession);
                }
                return session;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void terminateSession() throws UnsupportedOperationException
        {
                HttpServletRequest httpServletRequest = servletRequestStore.get();
                if (httpServletRequest != null)
                {
                        HttpSession httpSession = httpServletRequest.getSession(false);
                        if (httpSession != null)
                        {
                                httpSession.invalidate();
                        }
                }
        }

        /**
         * Store the servlet request for use by the session manager.
         * 
         * @param request The servlet request.
         */
        public void storeServletRequest(final HttpServletRequest request)
        {
                servletRequestStore.set(request);
        }

        /**
         * Clear the servlet request from the session manager.
         */
        public void clearServletRequest()
        {
                servletRequestStore.remove();
        }
}

So, this leaves the question of how to get each new HttpServletRequest into the ThreadLocal instance without (obviously) impacting the front end code (JSPs, helper classes, et cetera). To make this happen I used a javax.servlet.ServletRequestListener. This is a type of class (defined in the Servlet specification) that is automatically informed by the servlet container when a servlet receives a request. More specifically it allows you to react when a javax.servlet.ServletRequest comes into scope (reaches a servlet or the start of an associated filter chain) or goes out of scope (leaves a servlet or the last filter of its associated filter chain). In my case I combined a listener with the fact that the session manager is a singleton entity to allow me to put each HttpServletRequest into the ThreadLocal instance as the request came into scope.

public class SessionExposerServletRequestListener implements ServletRequestListener
{

        /**
         * {@inheritDoc}
         */
        public void requestDestroyed(final ServletRequestEvent event)
        {
                HttpSessionManager httpSessionManager = (HttpSessionManager) SessionManager.getInstance();
                httpSessionManager.clearServletRequest();
        }

        /**
         * {@inheritDoc}
         */
        public void requestInitialized(final ServletRequestEvent event)
        {
                HttpServletRequest request = (HttpServletRequest) event.getServletRequest();
                HttpSessionManager httpSessionManager = (HttpSessionManager) SessionManager.getInstance();
                httpSessionManager.storeServletRequest(request);
        }

}

Note that this listener doesn’t just make the session manager aware of requests; it also removes the request once it goes out of scope. There are a number of reasons for doing this. First of all, holding on to an HttpServletRequest that has gone out of scope prevents garbage collection. Second, clearing up the request from our ThreadLocal instance prevents problems with servlet containers that use thread pooling (or other object pooling).

Putting it to the test

Finally, in order to test, I created a simple web application with a single, simple redirect servlet. The application does nothing except allow you to browse from a start page (a state in which the server has no session associated with the servlet request) to a page in which a number of values are read from the session object (which must have been created and filled in the intermediate process). The application demonstrates that a POJO class representing a business service can successfully use my POC library to access the session and place values in it. In addition the application has two paths through the servlet, one which creates a session from the servlet and one in which the session can only be created due to access from the POJO.

The only moderately interesting things in this application are the configuration files. The web.xml is dirt common except for a listener section which hooks the listener class described earlier into the application. In addition there is a properties file which identifies the session manager class to use by name.

Recapping

In this article I’ve explained why I don’t like working on the front end and why I think that the current JEE style of server side session management is a bad idea with regards to maintaining the encapsulation and loose coupling of the business layer. I’ve tried to demonstrate that having session management solely in the front end makes it practically impossible not to bind your business tier to a particular front end (unless your entire application consists of one business service).

In addition to that I’ve discussed what I think would be a better approach and some ways of achieving it. Finally I discussed a small experiment I did as a proof of concept of implementing this approach by abstracting away from a specific type of session management.

Sources

In case anybody is interested in experimenting for themselves, I’ve attached the sources (a couple of Maven 2 projects) here: https://www.gridshore.nl/wp-content/uploads/sessionexposer.zip.


[0]

Okay, not all of it. If you use a stateful session bean that can maintain a lot of state in the business layer. But that means tying yourself to an EJB implementation, plus you still need the front end session management to hold on to your EJB reference.

Rethinking session management