Skip to main content

skip to main content

developerWorks  >  Sample IT projects  >

The Go-ForIt Chronicles: Memoirs of eXtreme DragonSlayers, Part 3

Session management, servlets, and maintaining state

developerWorks
Document options

Document options requiring JavaScript are not displayed

Discuss


Rate this page

Help us improve this content


Level: Intermediate

Jeff Wilson (wilsonje@us.ibm.com), e-business Architect, IBM

01 Jul 2001

In this third part of our series about the DragonSlaying technical consulting team, Jeff Wilson describes how the GoFor-It.com project uses session management. This article discusses the methods, inner workings, and pitfalls of the session object, and shows how to create your own session-aware servlet. It also explains how, by understanding the concept of "maintaining state" within an application, you can contribute to a rich user experience while giving your server a break from the tedious chore of continuous data crunching.

Introduction

Session management is an integral part of application development. Web sites that tailor information to your own preferences, or remember stuff about you and what you're doing, are probably using session management to persist data throughout your visit. Sites like my.yahoo.com and amazon.com use session management to give a rich user experience by collecting information about their user base and using that data to calculate and predict the content individuals are looking for.

The GoFor-It.com project doesn't have as sophisticated a system of personalization as sites like amazon.com, but the concepts and practices are similar. The requirements for GoFor-It.com are based on user stories that give specific aspects of a typical visit to the site. One of the first user stories dictates two requirements:

  • Users must be registered and logged in to access the errand service application.
  • Once logged in, users must have access to any part of the application without having to log in again during a single visit (unless they log off).

To meet the requirements, we must recognize a user who is not logged in, verify the user is in a database of registered users, and, once the user is logged in, remember that a particular user has previously been verified and allow access to any permitted areas of the site throughout the visit.

The GoFor-It.com project is a great introduction to session management because it's rather simple and straightforward, and is set up so that as the site matures and more requirements depend on persisting data, more information can easily be plugged in any time down the road.



Back to top


What are sessions and why do I care

If you're new to application development, you might wonder what "sessions" are. Conceptually, sessions are fairly simple; they just remember stuff. Like what stuff? Well, anything -- your first name, your dog's birthday, the last book you dropped in a shopping cart, maybe an account history or a list of users pulled from a database. Sessions hold anything that you don't want to have to retrieve, or figure out, repeatedly throughout an application. A session is a repository of data that can, at any time, be easily accessed by any part of an application. If you're familiar with caching, it is essentially the same concept -- storing data to reduce both response time and server workload.

Important points about sessions

  • Session data is stored on the server.
  • An ID or pointer to that data is stored on the client as a cookie.
  • Sessions are unique to every user and data cannot be shared by other users.
  • Sessions are unique to a domain and data collected by a single user cannot be shared among multiple domains.
  • The life span of the data saved to a session is, at most, from the moment the user accesses a domain to the moment when a user times out after a preset amount of time of inactivity.
  • Sessions die when the client connection is closed (for example, when a user closes a browser or other device).
  • A session life span can be altered while a user is still connected by either programmatically killing a session, or overriding the server default timeout and resetting it to a shorter or longer time.
  • Sessions are for data that's collected and used by a single user, and should generally be available when a user is interacting with a domain. This is not ALWAYS the case. Sessions should not be considered secure and can be volatile. Restart the server, for example, and all current sessions are lost. Be aware of this when using sessions for storing data that may be sensitive or not easily recoverable.

Why do we need a special tool?

You might know that HTTP is stateless by default. (HTTP, of course, is the basic protocol for getting around the Internet; it allows clients, like browsers, and servers to communicate.) HTTP being stateless essentially means that it does not remember stuff. This is by design to maintain a lightweight, versatile protocol.

The W3 approved specification for HTTP has a four-step process of "Connect > Request > Response > Close". The Internet -- or the chunk of the Internet we see through a browser -- was initially designed for simply requesting and transmitting files. Herein lies the problem: the data to be requested and transmitted is not necessarily resident in static files.

Useful Web-based tools get dynamic content generated by applications that gather information about what a user is doing, and use it to better predict what that user will do next. This is called maintaining state, which is a way to hold onto user information across multiple "Connect/Request/Response/Close" transactions. Otherwise, any information gathered in one round-trip transaction is dumped at each "Close". The figure below shows the transaction paths.

However, application development has a lot of overhead. Adding this function to HTTP would bog down the Internet, and reduce effectiveness of areas that may not need it. The theory is, "Let those who need it build it." And those who build it would manage the overhead of squeezing a server utility -- like one that maintains this "state" -- between the "Request" and "Response" steps of the approved standard. See the example below.



Back to top


How does session management work?

Session management is not a new idea. Many languages have techniques to make applications more efficient by retaining data and thereby reducing needed system resources.

The GoFor-It.com project is written mainly in the Java programming language using JSP and EJB components, servlets, and beans. The Java language makes it very easy to maintain state through the HTTPSession package included in the Servlet API.

Data stored in the session is managed by the servlet engine because beans and servlets are multithreaded, and therefore live independently from one another. Data stored in one bean or servlet is not accessible to another beyond the immediate transaction. For example, data used by the login servlet in the GoFor-It.com project cannot be used by the beans that edit a user profile unless that data was stored in the session first. If the data was not stored in the session, those beans would need to retrieve it by hitting the database again.

The information stored in the session is available throughout the user's visit to the GoFor-It.com site, unless we need to remove certain data or destroy the entire session altogether. This makes one of our requirements easy to manage.

Though it seems like it should go without saying, we made sure the GoFor-It.com site didn't bother users with continuous login screens every time they move from page to page. We needed to ensure valid users get the pages they need, but without prompting for a username and password every time they request one -- just once per session. When a user logs in, we store some known information in the session. At every page request, we look for that information stored in the session. If the information is NOT there (or in this case, if the session itself was never created), we know the user has not logged in and redirect them to a login page. Otherwise, we let the user have the page that was requested, as shown below.

When a new user registers with our system, we do not really need to revalidate that user. If they just registered, we know who it is and, immediately after registering, the user information is stored and we can bypass the login page. It is similar to having your hand stamped in a bar; to get back in, you need to show the stamp or pull out some ID. The only way we know if a user is valid is either with a username and password that can be validated against the database (the ID), or by the temporary session information (the stamp). In both cases, the temporary "access ID" allows the user entry more quickly.

Stored information can be any type of object in Java. We could store a string named userLoggedIn and assign it the value youBetcha and just check for that string to have that value. However, all we could use that information for is to give the user access. In the GoFor-It.com project we store the entire user object itself, because we may want some of that information throughout the visit, like putting the user's first name on some pages or pre-populating forms. It takes up some memory on the server, but makes for a far more pleasurable user experience (and might also be used in more functional ways that don't involve the user).

What would I use it for?

Before looking at GoFor-It.com in more depth, I'll provide some context by looking at a few reasons to make use of maintaining state.

Using sessions directly relates to response time. When information about a user is learned and saved, it can be reused later without rerunning the initial process. Suppose you needed to hit a database to verify a username and password and return a user ID, which in turn would be used to pull the user's first name. Saving the first name to a variable would be easier than hitting the database all over again.

Gathering information about a user is essential when dealing with personalization. Sites that generate content based on specific user preferences (such as e-commerce, or news) need to collect data and pass it from page to page, recalculating the content based on that user's actions. One could save that data dynamically on each page and pass it all as form data, but that poses problems when users backtrack or go to other sites then come back again. Sessions provide a consistent, flexible repository in this example.

Sessions also provide a way to track anonymous users, or those who aren't required to log in first before roaming through a site. Because sessions are linked to a specific client, users who do not intentionally identify themselves can be monitored. We might not know who they are but we can know a lot about what they're doing.



Back to top


Life cycle of a session

The life cycle of a session is fairly straightforward:

  1. A session object is created
  2. Some data is stored in that session
  3. The data is requested and used in some manner
  4. Eventually that data is wiped clean

In the Java language, the HTTPSession package includes all the methods needed to manage the variables, such as initializing the session object, creating variables, getting and setting values, and finally destroying the session. We'll walk though a simple example later in this article.

Creating the session

The session is an object that can be requested in a couple of ways. When requested, the session can be told whether or not to initiate one if it doesn't already exist. Before you can check a value, you need to request the session object and then look inside it. Likewise, if you want to drop some information in the session you also need to request it. Keeping stray objects from mistakenly being initialized makes it easy to control when objects are created.

Once the session is created, it is assigned an ID that's stored on the client as a transient cookie. A transient cookie is like any other with the exception that it is destroyed once the user disconnects from the server or is inactive for some preset time. Typical cookies have a life span that can exceed the session. In either case the cookies can be wiped out programmatically.

Using the session

To save information to the session, you just need to specify a variable and set some value to it. To retrieve the value, you request the value of the variable. (My apologies if this seems stupidly simple; I point it out only to show how easy all this is.)

Killing the session

Normally, you can simply let the default timeout deal with data left hanging around unless there is a specific reason to clean house. The GoFor-It.com project needs to deal with users who are finished, or are logging out. Remember, we recognize validated users by checking the session for their user information. If a user is finished with their account and leaves the browser without closing it for some reason, we don't want someone else jumping in "borrowing" the other user's information. By dumping the session, we force any continued access to first be revalidated by logging back in.

Managing session functions

Listed below are the methods to manage your session. The most frequently used methods are getAttribute() and setAttribute(), because they let you retrieve and update the values saved in the session. Less obvious methods include getId(), which returns the ID assigned to the session (you do not need the ID to access the session), and the inactive interval methods, which let you retrieve and override the default timeout. The inactivate() method destroys the session altogether without waiting for an inactive timeout.

  • getAttribute()
  • getAttributeNames()
  • getCreationTime()
  • getId()
  • getLastAccessedTime()
  • getMaxInactiveInterval()
  • inactivate()
  • isNew()
  • removeAttribute()
  • setAttribute()
  • setMaxInactiveInterval()

Code example for building a session

The following is a simple but complete example of a servlet that stores how many times it was requested. Because the GoFor-It.com project uses a session to store user information that's pulled from a database, it has extra code doing other things and may confuse the issue. In this case, we'll create a servlet that stores a greeting and the number of times the servlet was requested. After the servlet has been requested 5 times, the session is deleted and the count starts over at 1.

To run this example you need the latest JDK and JSDK, some sort of servlet engine, and an HTTP server. I run it in the test environment of VisualAge for Java, which includes all of the prereqs, but you should be able to use this code with other versions if the JDK and JSDK are current.

The following example follows these steps:

  1. Create 2 classes: one manages the session (SessionTest.java), and the other (VisitInformation.java) is the object stored in the session.
  2. The doPost() and doGet() methods of the SessionTest class both call a performTask() method that does all the work.
  3. The VisitInformation class has a couple of variables (numberOfVisits and greetingText) and a few methods to get and set those variables.
  4. The first thing the SessionTest performTask() method does after setting some initial variables is request the session allowing it to create a new session if one does not already exist.
  5. Then it creates an instance of the VisitInformation object (labeled "visits") that holds the session data. It first attempts to pull the object from the session. If it cannot find the object in the session it creates a new one.
  6. It tests if the session is new (session.isNew()) -- meaning the first time this session has been accessed -- and if not, resets the greeting.
  7. It increments the page request count by calling visits.incrementVisitCount().
  8. Gets the new count back by calling visits.getNumberOfVisits().
  9. If the count is equal to or greater than the allotted maximum page count it changes the greeting again.
  10. Now that the visit object has all the information it needs saved to it, and what we need to display is saved to local variables, it tests the count again.
  11. If it is less than the maximum page request count, the new visit object is saved to the session. Otherwise the entire session is destroyed (session.inactivate()).
  12. The local variables are then printed to the screen.

Note the highlighted segments that create, test, and manipulate the session.


SessionTest.java code

package com.sessiontest;

import javax.servlet.*;
import javax.servlet.http.*;

public class SessionTest extends javax.servlet.http.HttpServlet {

    public void doGet(javax.servlet.http.HttpServletRequest request, 
                             javax.servlet.http.HttpServletResponse response) 
                             throws javax.servlet.ServletException, java.io.IOException {

        performTask(request, response);

    }

    public void doPost(javax.servlet.http.HttpServletRequest request, 
                              javax.servlet.http.HttpServletResponse response) 
                              throws javax.servlet.ServletException, java.io.IOException {

	performTask(request, response);

    }


    public void init() {}

    public void performTask(javax.servlet.http.HttpServletRequest req,
                                       javax.servlet.http.HttpServletResponse res) {

   /* Let's first initiate a few necessary objects.. */
      HttpSession session = null;
      VisitInformation visits = null;
      java.io.PrintWriter out = res.getWriter();

   /* Set a few variables */
      int numberOfVisits = 0;
      int maxNumberOfVisits = 5;
      String greetingText = null;
      String charS = "";   // This will only be used to make a word plural...

   /* Check to see if the session was started already 
   and if not create one */
      session = req.getSession(true);

   /* Create a new visits object if one
   was not saved in the session */
      visits = (VisitInformation)  session.getAttribute("visits");
      if (visits == null){
         visits = new VisitInformation();
      }

   /* Reset some of the values in the visits object as 
   needed and grab some variables from it */
      if(!session.isNew()){
         /* session.isNew() will return true only the FIRST time you access 
         the session so this block will only happen from the 2nd time on...*/
         visits.setGreetingText("What? Still here?");
      }
      visits.incrementVisitCount();
      numberOfVisits = visits.getNumberOfVisits();
      if(numberOfVisits>=maxNumberOfVisits){
         visits.setGreetingText("You come around too much.  Leave me alone.  Bye, bye.");
      }
      greetingText = visits.getGreetingText();

   /* Save the final visits object back to the session or kill
   the session if we have reached the max increment number */
      if(numberOfVisits<maxNumberOfVisits){
         session.setAttribute("visits", visits);
      }
      else{
         session.invalidate();
      }

   /* Print away... */
      if (numberOfVisits!=1){
         charS = "s";
      }
      out.println("<h3>" + greetingText + 
                         "</h3>You have requested this servlet <b>" + 
                         numberOfVisits + "</b> time" + charS +".");

   }
  }	
}





VisitInformation.java code

package com.sessiontest;

public class VisitInformation {
    public int numberOfVisits = 0;

    public java.lang.String greetingText = "First time, eh?...";

    public VisitInformation() {
        super();
    }

    public String getGreetingText() {
        return greetingText;
    }

    public int getNumberOfVisits() {
        return numberOfVisits;
    }

    public void incrementVisitCount() {
        setNumberOfVisits(++numberOfVisits);
    }

    public void setGreetingText(String newGreeting) {
        greetingText = newGreeting;
    }

    public void setNumberOfVisits(int newNumberOfVisits) {
        numberOfVisits = newNumberOfVisits;
    }
}




Back to top


Issues and concerns

Sessions are an easy way to store information to be used throughout an application. There are, however, some drawbacks to consider.

Most importantly, remember that sessions store their ID as a cookie on the client's machine. If a user has disabled cookies in their machine, you may need to take a few more steps to keep track of that user. You could pass the ID in the location, with the drawback that you'll need to dynamically generate every link throughout the site to include that ID, in this case. Not only is that a bother, but a bookmarked page would then have an old ID (a new ID is generated every session).

Because the data is stored on the server, data being used by current users will be lost if you need to restart the server for any reason. Similarly, if you are doing load balancing by clustering several computers, the session data is stored only on the computer that created it. Since load balancing spreads out the work load by sending users to any of the machines, there is no guarantee that a user who accesses the site on one machine will hit the same machine on the next page request. Solving this problem requires session affinity, which persists the session data to a repository. It can give you the ability to store indefinitely your session data where it can be retrieved not only after a restart, but from any of a number of machines in a cluster. This simply requires extra steps.

Also, keep in mind that there is no real verification used to access stored data. Technically, an ID can be mimicked by another user and the original user can have the session hijacked. This can be made even easier if the ID is being passed within the URL.

Because of the security and volatility risks, it is important to know what is and is not appropriate to store in the session. Session data can be used to greatly simplify accessing information. But with sensitive information, or data that will be hard to retrieve if the session is lost, you might want to use other means to safely retrieve information, or find other means of storing the data permanently.



Back to top


Stay Tuned...

Watch for our next installment of the Go-ForIt chronicles, where we'll cover client-side beans. To see the previous articles in our tale of dragonslaying, go to our overview.



Resources



About the author

Jeff K. Wilson is an e-business Architect in IBM's Developer Relations group. Before joining IBM in April, Jeff K. Wilson went by Jeff Wilson -- and in some circles, just Jeff would suffice. Currently, Jeff K. Wilson enjoys the company and solidarity of the ever-growing "Club De Jeff Wilson" here at IBM. Be wary of contacting Jeff Wilson, even if you have the city -- or even building of the Jeff Wilson you may think wrote this article. The official "Jeff Wilson" in town sits a short walk down the hall collecting occasional poorly addressed Jeff K. Wilson e-mail. Jeff K. Wilson can be properly reached at wilsonje@us.ibm.com.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top