Wizard API updated!
Tim Boudreau has released a new version of the Swing Wizard library (version 0.997) that fixes the WizardException bug reported in JavaWorld's recent Open Source Java Project profile. The article's examples have been reworked to test out the new, improved WizardException. Thanks, Tim, for this helpful fix!
Open Source Java Projects: The Wizard API

Newsletter sign-up

Sign up for our technology specific newsletters.

Enterprise Java
View all newsletters

Email Address:

Manage distributed sessions

To avoid a single point of failure, use a distributed architecture for managing sessions

For a good description of what sessions are and what the problem with having sessions across multiple servlet servers is, refer to Thomas E. Davis and Craig Walker's "Take Control of the Servlet Environment, Part 2: Alternatives to Servlet Session Management" (JavaWorld, December 21, 2000). Basically, the problem is that if you have more than one servlet server, session information exists only in one servlet engine's JVM and is not communicated to the other servlet servers. If a servlet server fails or shuts down for maintenance, any information saved in a session is lost. Also, if an environment has multiple servlet servers, a user with a session must always be redirected to the same servlet server in order to reference any data in that session. Davis and Walker suggest using a relational database that all servlet servers can access to save the session information. However, that solution still has a single point of failure: the server hosting the database. If the database went down, all session information would become unavailable to the servlet servers. Also, saving serialized objects into the database is a functionality that is difficult to implement in all databases.

Another possibility for multiserver session management utilizes the JavaSpaces API (for documentation, see Resources) to maintain records of the session objects. However, if the machine hosting the JavaSpace were to go down because of crashing or maintenance, all session information would be lost. So again, we are left with a single point of failure.

To accomplish the distributed session server with n-nodes, we must address three main problems:

  1. How to set up a repository to store session information
  2. How to synchronize distributed repositories
  3. How to let a servlet server retrieve the session information in such a way that if one repository suddenly goes offline, it tries to retrieve the object from the next repository


Introduction to the Mnemosyne

The repository we will use to store the session information is an implementation of the Mnemosyne interface (named after the Greek goddess of memory and mother of the muses). An object that implements Mnemosyne is responsible for managing all objects in the repository. Any other object that wants to write, retrieve, or remove objects in the repository must call a method of the Mnemosyne.

To be saved to a Mnemosyne, an object must implement the Memory interface, which defines the equalsMemory()operation for determining whether two memory objects are equivalent. That allows the Mnemosyneto figure out what object should be returned on a read or a take request. The Memory interface also extends Serializable so that you can use RMI to transmit the object across the network.

A Mnemosyne uses three other interfaces to represent its state:

  1. A CommonContext interface will store all information for the Mnemosyne. Each Mnemosynewill have one instance of a CommonContextobject to allow for synchronization between methods when reading, writing, and taking Memoryobjects. For writing and taking, the CommonContextdefines both "silent" and "loud" methods. The silent methods are used when objects need to be added without any event notifications. For example, when a Mnemosynereceives a WriteRemoteEvent (a notification that an object has been written to a remote Mnemosyne), it will want to write another object to the CommonContext. However, it doesn't need to notify the other remote Mnemosynes; the original Mnemosynehas notified them. So the write is done "silently" by calling the CommonContext's silentWrite()method. The "loud" methods give details of the event to any interested listeners that are called when an object is first put into the context.
  2. A Transaction interface allows for distributed transactions when reading, writing, or taking Memoryobjects. That means multistep operations can occur on the Mnemosyne.
  3. A TransactionContext interface manages a distributed transaction. That makes it possible to abort or commit transactions.


Keeping Mnemosynes synchronized is accomplished with two methods defined by the Mnemosyne: synchronize()and notify(). synchronize()is intended to get a local Mnemosyne"in sync" with a Vector of other Mnemosynes. (Those Mnemosynes may be local or remote, but for the sake of clarity, we will assume they are remote.) A sample implementation of the synchronize()method (from the MnemosyneImplclass) is displayed below. (See Resources for the complete sample code to this article.)

public void synchronize(Vector Mnemosynes)
                throws RemoteException, TransactionException
    {
        // ...
                            // The MatchAllMemory object is a utility class that
returns true when 
                           //  compared against any Memory object
            MatchAllMemory matchAllMemory = new MatchAllMemory();
            // Obtain all Memory's from the Primary
            Mnemosyne Mnemosyne = (Mnemosyne) Mnemosynes.firstElement();
            Vector allMemories = Mnemosyne.readAll(matchAllMemory,null);
            // Write all Memory's silently as there is no need
                            //  to notify write listeners
            commonContext.silentWriteAll(allMemories);
            // Register to send and receive events
            Enumeration enumeration = Mnemosynes.elements();
            while(enumeration.hasMoreElements())
            {
                Mnemosyne nextMnemosyne = (Mnemosyne) enumeration.nextElement();
                // Register to obtain notification
                nextMnemosyne.addTakeRemoteEventListener(this, matchAllMemory); 
                nextMnemosyne.addWriteRemoteEventListener(this, matchAllMemory);
                // Register to send notification
                addTakeRemoteEventListener(nextMnemosyne, matchAllMemory);
                addWriteRemoteEventListener(nextMnemosyne, matchAllMemory);
            }
         // ...
    }


The local Mnemosyne object reads all of its Memoryobjects of the first Mnemosyne in the Vector, and silently writes them to its CommonContextobject. Next, the local Mnemosyneadds itself to all remote Mnemosynes, as both a TakeRemoteEventListenerand a WriteRemoteListener. That means that any takes or reads on the remote Mnemosynes will result in a call to the local Mnemosyne's notify() method. Finally, the local Mnemosyne adds the remote Mnemosyneto its list of TakeRemoteEventListeners and WriteRemoteListeners. This ensures that any write or take calls will notify the remote Mnemosyne.

Now our synchronized local Mnemosyne object needs to update all other Mnemosynes whenever a Memoryobject is added or removed. You can accomplish that with the notify() method. Whenever a write or take event occurs, a Mnemosyne calls the notify()method of all appropriate listeners for the event. In the synchronize() method, you register the local Mnemosyne as a listener for take and write events on all remote Mnemosynes. If such an event occurs on any of those remote Mnemosynes, the local Mnemosyne's notify() method will be called. When that happens, the local Mnemosyne must respond to the event. Below is an example of how the Mnemosynecan synchronize itself with the remote Mnemosyne:

public void notify(RemoteEvent remoteEvent) throws RemoteException
    {
        // Write the written Memory to self, but no need to notify all of the 
        //  Mnemosynes 
        if(remoteEvent instanceof WriteRemoteEvent)
        {
            WriteRemoteEvent wre = (WriteRemoteEvent) remoteEvent;
            commonContext.silentWrite(wre.getMemory());
        }
        // Take the written Memory from self, but no need to notify all of the 
        //  Mnemosynes
        if(remoteEvent instanceof TakeRemoteEvent)
        {
            TakeRemoteEvent tre = (TakeRemoteEvent) remoteEvent;
            commonContext.silentTake(tre.getMemory());
        }
   }


A Mnemosyne has now been set up that can hold memory objects, synchronize itself with remote Mnemosynes, and keep itself up to date if any remote Mnemosynes gain or lose a Memory object.

To manage HTTP sessions using Mnemosynes, a servlet creates an instance of HttpSession (using the getSession()method from HttpServletRequest), wraps the session in a class that implements Memory (in this example, it is called SessionWrapper), and writes the wrapper class to a Mnemosyneby calling the Mnemosyne object's write() method.

By calling the write() method, the Memoryobject that wraps the session is serialized, sent down the network to the Mnemosyne, and instantiated on the remote machine. When the Memory object is written to the Mnemosyne, a WriteRemoteEventis sent to all WriteRemoteEventListeners registered with the Mnemosyne. This allows all other Mnemosynes to add the new object to their repository as Mnemosynes.

To look up a stored session, a servlet calls the read()method to look for the Memory object that contains the session. If the Mnemosyne finds the object, the object is sent via RMI back to the servlet server.

Finally, to remove the session, the servlet would call the Mnemosyne's take() method. The Mnemosynewould send back the Memory object just like it does on the read, but the object would also be removed from the repository. Also, a TakeRemoteEvent would be sent out to all TakeRemoteEventListeners. That would notify all remote Mnemosynes of the Memoryobject's removal.

Setting up a session server

Now that we have shown how the repository of objects will be maintained on multiple servers, we will show you how to build an implementation of a session server. On initialization, a session server does the following:

1 | 2 |  Next >
Resources