Session Management Part 3SJSP Home « Session Management Part 3

In the last lesson we looked at cookies, the first of two tracking techniques that are more flexible and work with web applications regardless of page volume. In our final lesson on session management we look at the HttpSession interface and how we can use objects of this type for session tracking.

Before we look at some of the methods within the HttpSession interface let's talk about objects of this type. Whenever a user visits a web application for the first time, if we require sessions, then the container uses the HttpSession interface to create a session between an HTTP client and an HTTP server. We can think of this session as an object and will refer to it as a HttpSession object from now on. A user can have zero or one HttpSession objects and is uniquely identified by, and can only access their own HttpSession object.

Unlike URL rewriting, hidden fields and cookies, values stored in a HttpSession object are not sent to the client but rather a unique session identifier that identifies each individual session is sent. The unique session identifier is sent appended to the URL as a jsessionid parameter or a cookie is created called JSESSIONID containing the unique session identifier as its value. We as programmers do not need to worry about the method involved to create the unique session identifier as this is done automatically by the container.

HttpSession Interface Overviewgo to top of page Top

Using methods of the HttpSession interface we can add and remove attributes, create and invalidate sessions and also set the life expectancy of a HttpSession object. With the methods available, this makes a HttpSession object the most powerful of all the session management techniques to use and also allows us to use the HttpSession object with the other techniques we have seen such as URL rewriting and cookies.

The table below shows the declarations of some of the more common methods in the HttpSession interface, which we cover on the site:

Method Declaration Description
java.lang.Object getAttribute(java.lang.String name)Returns value of bound object with specified name for this session, or null if bound object not found.
java.util.Enumeration getAttributeNames()Returns Enumeration of String objects containing attribute names of all objects bound to this session.
java.lang.String getId()Returns a String object containing the unique session identifier for this session.
int getMaxInactiveInterval()Maximum time interval, returned in seconds, that the servlet container will keep this session open between client requests.
void invalidate()Invalidate session and unbind any objects bound to it.
boolean isNew()Returns true if client does not yet know about, or chooses not to join the session.
void removeAttribute(java.lang.String name)Removes bound object with specified name from this session.
void setAttribute
  (java.lang.String name, java.lang.Object value)
Binds object to this session using specified name.
void setMaxInactiveInterval(int seconds)Maximum time interval, specified in seconds, that the servlet container will keep this session open between client requests.

If setMaxInactiveInterval() is passed 0, the HttpSession object expires immediately.

If setMaxInactiveInterval() is passed a negative value, the HttpSession object will never expire and will remain on the heap until the application is unloaded or the servlet container is shut down.

Use the links in the table above to go to example code of the method in question. For more information on these and the other methods in the HttpSession Interface follow the link to the official site at the end of the lesson.

Before we write a servlet demonstrating use of a HttpSession object, let's take a look at the advantages and disadvantages of using this session management technique.

Advantages

A HttpSession can be used for web applications with small, or large page volumes without any increase in workload or maintenance.

We can add, get or remove attributes from the HttpSession giving us a great deal of flexibility.

If cookies are turned off we can use the unique session identifier from the HttpSession object in conjunction with a form of URL rewriting to keep track of a session as shown in the following code and screenshot:


package controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class NoCookiesServlet extends HttpServlet {

    private static final long serialVersionUID = 871964L;

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException { 
        // Create a form with display options 
        resp.setContentType("text/html");
        PrintWriter writer = resp.getWriter(); 
        // Get a HttpSession 
        HttpSession session = req.getSession();
        // Do we have a HttpSession 
        boolean isThisNewSession = session.isNew(); 
        // Get Session id
        String sessionId = session.getId(); 
        // Session Expiry 
        int sessionExpiry = session.getMaxInactiveInterval(); 
        // Write response
        writer.println("<html><head><title>No Cookies</title></head><body>" + 
                "<a href='" + resp.encodeURL("/nocookies/NoCookies") + "'>click, no Cookies</a>" +
                "<p>Was session new on entry? " + isThisNewSession + "</p>" +
                "<p>Unique Session identifier: " + sessionId + "</p>" +
                "<p>Session removal after "  + sessionExpiry + " seconds inactivity</p>" +
                "</body></html>");
    }
}

As you can see from the screenshot a cookie named JSESSIONID has been created as a session cookie with a session id and path. We can then use methods from the HttpSession interface, or check for this session cookie, to find out if this is a new visitor or one who already has a session.

NoCookiesServlet

Here's another screenshot showing what happens when we turn off cookies to the local host via pagespeed. This shows that when we click on the hyperlink after initial entry the session id is now passed in the URL. So this form of URL rewriting using a session can be used as a fallback if you use cookies and they are disabled.

NoCookiesServlet2

Disadvantages

Attributes added to a HttpSession are stored in container memory as objects, not on a client computer, so you have to be aware of memory considerations such as object size and the amount of objects being stored.

Garbage collection of memory stored objects needs to be done efficiently. This can be achieved using the setMaxInactiveInterval(int seconds) method of the HttpSession interface, to set an inactive time limit on a session. You can also use the invalidate() method of the HttpSession interface, which invalidates the session and unbinds all objects belonging to it.

Objects added to a HttpSession should be from a class that implements java.io.Serializable, so that if serialization is necessary, we don't get an exception.

Terminating A Sessiongo to top of page Top

Before we look at an example of using sessions it's worth discussing how we can terminate a session. You might think that a session ends when a user closes the browser but it persists. If that user then reopens the browser and goes back to the same web application a new session is started and the old session sits there using up resources. So we need a way to manage our sessions. Sessions can be terminated in a variety of ways using methods within the HttpSession interface or the DD. We will look at the code to terminate sessions using methods of the HttpSession interface first:


HttpSession.setMaxInactiveInterval(60);  // Session will expire in 60 seconds
HttpSession.setMaxInactiveInterval(0);   // Session will expire immediately
HttpSession.setMaxInactiveInterval(-60); // Session will never expire

HttpSession.invalidate(); // Immediately invalidate session and unbind any objects bound to it

The other way to terminate a session is via the DD using the top-level <session-config> element which is used to define the session timeout attributes for a web application.


<?xml version="1.0" encoding="UTF-8"?>
<web-app xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
                             http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
   <session-config>
      <session-timeout>60</session-timeout>
   </session-config>
</web-app>

The <session-config> sub-level element is optional and defaults to 60. The value you enter for the <session-timeout> sub-level element must be an integer and is expressed as whole minutes. Entering a negative value or 0 means the session will never time out. Entering a positive number denotes the period in minutes before the session is invalidated with a maximum value of Integer.MAX_VALUE รท 60.

HttpSession Examplego to top of page Top

Lets look at an example of creating an object within the HttpSession, which we store as a key/value pair attribute (more on this shortly).

Within the _ServletsInt folder create a new folder for the web application we will create in this lesson and call it bookchoice

Within the bookchoice folder create separate folders to hold our DD, source files and our compiled byte code and called dd, src and classes. Within the src folder create two subfolders called controller and model.

So after creating these folders your directory structure should look something like the following screenshot:

bookchoice directory structure

Coding The Book Classgo to top of page Top

We used the Book class in an earlier example and it models a book title and some details and uses the JavaBeans standard of get<someProperty> and set<someProperty>. For a refresher on JavaBeans you can look at the Java5 Tutor website and in particular the section on Getters & Setters.

Cut and paste the following code into your text editor and save it in the   c:\_ServletsInt\bookchoice\src\model directory as Book.java.


package model;

public class Book {

    private int num;
    private String title;
    private String rating;
    // Getters and setters
    public int getNum() { 
        return num;
    }
    public void setNum(int num) { 
        this.num = num;
    }
    public String getTitle() { 
        return title;
    }
    public void setTitle(String title) { 
        this.title = title;
    }
    public String  getRating() { 
        return rating;
    }
    public void setRating(String rating) { 
        this.rating = rating;
    }
}

Compiling The Book Classgo to top of page Top

Open your command line editor:

Change to directory  c:\_ServletsInt\bookchoice\src\model

Compile Book.java using the java compiler with the -cp and -d options as below, making sure you change apache-tomcat-6.0.37 to wherever you downloaded Tomcat to.

  javac -cp c:\apache-tomcat-6.0.37\lib\servlet-api.jar -d ..\..\classes Book.java

The following screenshot shows that we get a clean compile and also the Book class now compiled into the classes\model directory.

compile Book class

Coding The BookItem Classgo to top of page Top

The BookItem Class models a book item that is to be added to a wish list and uses the JavaBeans standard of get<someProperty> and set<someProperty>. For a refresher on JavaBeans you can look at the Java5 Tutor website and in particular the section on Getters & Setters.

Cut and paste the following code into your text editor and save it in the   c:\_ServletsInt\bookchoice\src\model directory as BookItem.java.


package model;

public class BookItem {

    private Book book;
    private int quantity;
    // Constructor
    public BookItem(Book book, int quantity) {
        this.book = book;
        this.quantity = quantity;
    }
    // Getters and setters
    public Book getBook() { 
        return book;
    }
    public void setBook(Book book) { 
        this.book = book;
    }
    public int getQuantity() { 
        return quantity;
    }
    public void setQuantity(int quantity) { 
        this.quantity = quantity;
    }
}

Compiling The BookItem Classgo to top of page Top

Open your command line editor:

Change to directory  c:\_ServletsInt\bookchoice\src\model

Compile BookItem.java using the java compiler with the -cp and -d options as below, making sure you change apache-tomcat-6.0.37 to wherever you downloaded Tomcat to.

  javac -cp c:\apache-tomcat-6.0.37\lib\servlet-api.jar;..\..\classes -d ..\..\classes BookItem.java

The following screenshot shows that we get a clean compile and also the BookItem class now compiled into the classes\model directory.

compile BookItem class

Coding WishListServletgo to top of page Top

Ok with our model, the Book and BookItem classes coded and compiled we are now ready to code up a servlet that uses the HttpSession object as its session management technique. On deployment the init() routine will run and create 4 books objects for later use.

After initialisation, when the WishListServlet class below is run the doGet() will be invoked and we use the HttpServletRequest method encodeURI() to get the URI and then use the String method endsWith(), to determine whether to invoke the displayBookList() method, the displayBookDetails() method, or the displayWishList() method.

On initial entry the displayBookList() method runs and produces a list of books with links to details for each. On book detail selection the displayBookDetails() method runs and you can enter a quantity and add to the wish list via the link. On submission the doPost() method is invoked or you can return to the booklist rather than adding to wish list.

Within the doPost() method we get an existing HttpSession object, or create a new HttpSession object, using the getSession() method of the HttpServletRequest interface. We then create or get our wish list parameter and then add a BookItem to the wish list. Finally the displayWishList() method is invoked to display the wish list.

Cut and paste the following code into your text editor and save it in the   c:\_ServletsInt\bookchoice\src\controller directory as WishListServlet.java.


package controller;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import model.Book;
import model.BookItem;

public class WishListServlet extends HttpServlet {

    private static final long serialVersionUID = 871964L;
    private static final String WISHLIST_ATTR = "wishList";

    private List<Book> books = new ArrayList<Book>();

    @Override
    public void init() throws ServletException { 
        // Create some Book records for later use
        Book book1 = new Book();
        book1.setNum(1); 
        book1.setTitle("Wuthering Heights"); 
        book1.setRating("Classic"); 
        books.add(book1);
        
        Book book2 = new Book();
        book2.setNum(2); 
        book2.setTitle("The Railway Children"); 
        book2.setRating("Good"); 
        books.add(book2);
        
        Book book3 = new Book();
        book3.setNum(3); 
        book3.setTitle("Treasure Island"); 
        book3.setRating("Great"); 
        books.add(book3);
        
        Book book4 = new Book();
        book4.setNum(4); 
        book4.setTitle("Gullivers Travels"); 
        book4.setRating("Classic"); 
        books.add(book4);
    }

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException { 
        // Get request URL so we can decide which method to invoke
        String uri = req.getRequestURI();
        if (uri.endsWith("/book")) { 
            // display book list
            displayBookList(resp);
        } else if (uri.endsWith("/bookDetails")) {
            // display all book details
            displayBookDetails(req, resp);
        } else if (uri.endsWith("/wishList")) {
            // display the wish list
            displayWishList(req, resp);
        }
    }

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException { 
        // Add a Book to wish list
        int bookNum = 0;
        int quantity = 0;
        String num = req.getParameter("num");
        
        if (num != null) { 
            // Retrieve and convert book number to int type
            try {
                bookNum = Integer.parseInt(num); 
                // Retrieve and convert quantity to int type
                String quantityStr = req.getParameter("qty");
                quantity = Integer.parseInt(quantityStr);
            } catch(NumberFormatException ex) {
            }
        }
        Book book = getBook(bookNum);
        if (book != null && quantity >= 0) {
            BookItem bookItem = new BookItem(book, quantity);
            HttpSession session = req.getSession();
            // Unavoidable unchecked cast warning as getAttribute() returns Object 
            List<BookItem> wishList = (List<BookItem>) session.getAttribute(WISHLIST_ATTR);
            if (wishList == null) {
                wishList = new ArrayList<BookItem>();
                session.setAttribute(WISHLIST_ATTR, wishList);
            }
            wishList.add(bookItem);
        }
        displayWishList(req, resp);
    }

    public void displayBookList(HttpServletResponse resp) throws IOException {    
        // Create a response
        resp.setContentType("text/html");
        PrintWriter writer = resp.getWriter();
        writer.println("<html><head><title>Books</title></head>" + 
                "<body><h1>Books In Stock:</h1><ul>");
        for (Book book : books) {     
            writer.println("<li>" + book.getTitle() + "(" + book.getRating() +  ")  (" + 
                    "<a href='bookDetails?num=" + book.getNum() + "'>Book Details</a>)</li>"); 
        }
        writer.println("</ul><a href='wishList'>View Your Wish List</a></body></html>");
    }

    public void displayBookDetails(HttpServletRequest req, HttpServletResponse resp) 
            throws IOException {
        // Create a response
        resp.setContentType("text/html");
        PrintWriter writer = resp.getWriter();
        int bookNum = 0;
        // Retrieve and convert book number to int type
        try {
            bookNum = Integer.parseInt(req.getParameter("num"));
        } catch(NumberFormatException ex) {
        }
        Book book = getBook(bookNum);    
        
        if (book != null) { 
            // We found the book so lets output to a table with quantity
            writer.println("<html><head><title>Display Book Details</title></head>" + 
                "<body><h1>Display Book Details:</h1><form action='wishList' method='post'>" +
                "<fieldset style='background-color:silver;border:1px solid black;" +
                "padding:5px;text-align:left;width:300px;'>" +
                "<legend>Edit Book Quantity Details</legend>");
            writer.println("<input type=hidden name='num' value='" + bookNum + "'/>");    
            writer.println("<table><tr><td>Title:</td><td>" + book.getTitle() + "</td></tr>"); 
            writer.println("<tr><td>Rating:</td><td>" + book.getRating() + "</td></tr>"); 
            writer.println("<tr><td>Quantity:</td><td><input name='qty' value='1' /></td></tr>"); 
            writer.println("<br /><tr><td><input type='submit' value='Add To Wish List'/></td>" +
                    "<td></td></tr></table></fieldset></form>");
            writer.println("<br /><a href='book'>Book List</a></body></html>"); 
        } else {
            writer.println("Sorry, the requested book was not found.");
            writer.println("<tr><td><a href='book'>Book List</a></td><td></td></tr>"); 
        }
    }

    public void displayWishList(HttpServletRequest req, HttpServletResponse resp) 
            throws IOException {
        // Create a response
        resp.setContentType("text/html");
        PrintWriter writer = resp.getWriter();
        writer.println("<html><head><title>Book Wish List</title></head>" + 
                "<body><h1>Book Wish List:</h1>");
        
        HttpSession session = req.getSession();
        // Unavoidable unchecked cast warning as getAttribute() returns Object 
        List<BookItem> wishList = (List<BookItem>) session.getAttribute(WISHLIST_ATTR);
        
        if (wishList != null) {
            writer.println("<form action='wishList' method='post'><table><tr>" + 
                    "<th style='width:200px;text-align:left;'>Book Title</th>" +
                    "<th style='width:200px;text-align:left;'>Rating</th>" + 
                    "<th style='width:200px;text-align:left;'>Quantity</td></th>"); 
            
            for (BookItem bookwishlist : wishList) {
                Book book = bookwishlist.getBook();
                int quantity = bookwishlist.getQuantity();
                if (quantity != 0) {
                    writer.println("<tr><td>" + book.getTitle() + "</td>" +
                            "<td>" + book.getRating() + "</td>" +
                            "<td>" + quantity + "</td></tr>"); 
                }
            }
            writer.println("<tr><td></td><td></td></tr>"); 
            writer.println("</table></form><a href='book'>Book List</a></body></html>");
        } else {
            writer.println("Sorry, the requested book(s) not found.");
            writer.println("<br /><br /><a href='book'>Book List</a></body></html>"); 
        }
    }

    public Book getBook(int bookNum)  {    
        // Retrieve a book using number
        for (Book book : books) {        
            if (book.getNum() == bookNum) {
                return book;
            }
        }
        return null;
    }
}

Compiling WishListServletgo to top of page Top

Open your command line editor:

Change to directory  c:\_ServletsInt\bookchoice\src\controller

Compile WishListServlet.java using the java compiler with the -cp and -d options as below, making sure you change apache-tomcat-6.0.37 to wherever you downloaded Tomcat to.

  javac -Xlint -cp c:\apache-tomcat-6.0.37\lib\servlet-api.jar;..\..\classes -d ..\..\classes WishListServlet.java

The following screenshot shows that we get a compile with warnings. We are using the javac -Xlint compiler option to show these warnings which are unavoidable as the session.getAttribute() method returns type Object which we then have to cast. The screenshot also displays the WishListServlet class now compiled into the classes\controller directory.

compile WishListServlet class

Coding The DDgo to top of page Top

There are no DD entries for this application that we haven't seen before.


<?xml version="1.0" encoding="UTF-8"?>
<web-app xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
                             http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
   <servlet>
      <servlet-name>bookWishList</servlet-name>
      <servlet-class>controller.WishListServlet</servlet-class>
   </servlet>
   <servlet-mapping>
      <servlet-name>bookWishList</servlet-name>
      <url-pattern>/book</url-pattern>
      <url-pattern>/bookDetails</url-pattern>
      <url-pattern>/wishList</url-pattern>
   </servlet-mapping>
</web-app>

Save the web.xml file in the DD folder.

Tomcat Deploymentgo to top of page Top

Go to your Tomcat installation which in my case is:

C:\apache-tomcat-6.0.37\webapps\

Within the webapps folder create a folder for our web application called bookchoice

Within the bookchoice folder create the WEB-INF folder.

Copy the web.xml file and the classes directory and contents from our development environment into the WEB-INF folder.

After creating these folders and copying the files from development your Tomcat directory structure within the webapps\bookchoice folder should look something like the following screenshot:

Tomcat directory structure1
Testing Our Servletgo to top of page Top

Open a command prompt and change the directory to the location of our Tomcat 6 bin directory and then type in startup (Windows 7) or startup.sh (Linux/Unix/OSX). Press Enter and the Tomcat 6 server should open up in a new window.

You can also start up Tomcat by double-clicking the startup batch file within your Tomcat /bin folder.

With Tomcat up and running type the following into your web browser address bar and press enter:

  http://localhost:8080/bookchoice/book

The web browser should be directed to the WishListServlet servlet class within Tomcat and execute it. On initial entry to the WishListServlet class the doGet() method will be invoked and will show a list of books wiht links that looks like the following screenshot:

run WishListServlet class

If we were to click on the wish list now we would get the following screenshot as we haven't selected any books for the wish list yet.

no WishListServlet class

Selecting a book details link from the book list will display the following screenshot:

Get details WishListServlet class

This last screenshot below was taken after adding a few items to the wish list:

Show WishListServlet class

As you can see the state of the wish list is being stored in the HttpSession object and being retrieved when required.

Java Documentationgo to top of page Top

The following link will take you to the online version of documentation for the Servlet API Documentation . Take a look at the documentation for the HttpSession interface we have discussed, which you can find by clicking on the javax.servlet.http package in the top left pane and then clicking on the HttpSession interface in the lower left pane.

Session Management Part 3 Quizgo to top of page Top

Try the quiz below to test your knowledge of this lesson.

Question 1 : How many HttpSession objects can a client have?
- A client can have 0 or 1 <code>HttpSession</code> objects.
Question 2 : Values stored in a HttpSession object are not sent to the client?
- Values stored in a <code>HttpSession</code> object are not sent to the client but are retained in container memory. A unique session identifier is sent appended to the URL as a <code>jsessionid</code> parameter or a cookie is created called <code>JSESSIONID</code> containing the unique session identifier as its value.
Question 3 : The HttpSession object is always removed at the end of the session?
- If <code>setMaxInactiveInterval() </code> is passed <code>0 </code>, the <code>HttpSession </code> object will never expire and will remain on the <code>heap </code> until the application is unloaded or the servlet container is shut down.
Question 4 : How is session data stored for a HttpSession object?
- Session data stored for a <code>HttpSession</code> object is stored via attributes.
Question 5 : Objects added to a HttpSession should be from a class that implements java.io.Serializable ?
- Objects added to a <code>HttpSession</code> don't have to be implement <code>java.io.Serializable</code>, but if the container needs to serialize then we get an exception.
Question 6 : What type of session management can we use with HttpSession if cookies are disabled?
- If cookies are disabled we can encode our URLs using URL rewriting and get the unique session identifier using <code>HttpSession</code>.
Status Bar Please select an answer

Lesson 4 Complete

In our final lesson on session management we looked at the HttpSession object.

What's Next?

In the next lesson we look at attributes and the different scopes they can be used in.

go to home page Homepage go to top of page Top