The Java TM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search

Trail: Servlets
Lesson: Saving Client State

## Session Tracking

> Session tracking is a mechanism that servlets use to maintain state about a series of requests from the same user (that is, requests originating from the same browser) across some period of time.

> Sessions are shared among the servlets accessed by a client. This is convenient for applications made up of multiple servlets. For example, Duke's Bookstore uses session tracking to keep track of the books being ordered by a user. All the servlets in the example have access to the user's session.

> To use session tracking,

> - Get a session (an `HttpSession` object) for a user.
> - Store or get data from the `HttpSession` object.
> - Invalidate the session (optional).

### Obtaining a Session

The `getSession` method of the `HttpServletRequest` object returns a user's session. When you call the method with its `create` argument as `true`, the implementation creates a session if necessary.

To properly maintain the session, you must call `getSession` before any output is written to the response. (If you respond using a `Writer`, then you must call `getSession` before accessing the `Writer`, not just before sending any response data.)

The Duke's Bookstore example uses session tracking to keep track of the books in the user's shopping cart. Here is an example of the `CatalogServlet` getting a session for a user:

        public class CatalogServlet extends HttpServlet {

            public void doGet (HttpServletRequest request,
                               HttpServletResponse response)
        	throws ServletException, IOException
            {
                // Get the user's session and shopping cart
    	    HttpSession session = request.getSession(true);
                ...
    	    out = response.getWriter();
                ...
            }
        }

### Storing and Getting Data from a Session

The `HttpSession` interface provides methods that store and return:
:

- Standard session properties, such as a session identifier
- Application data, which is stored as a name-value pair, where the name is a `String` and the value is an object in the Java programming language. (This is like `java.util.Dictionary`.) Because multiple servlets have access to a user's session, you should adopt a naming convention for organizing the names associated with application data. This avoids servlets accidentally overwriting each other's values in the session. One such convention is `servletname.name` where `servletname` is the full name of the servlet, including its packages. For example, `com.acme.WidgetServlet.state` is a cookie with the servletname `com.acme.WidgetServlet` and the name `state`.

The Duke's Bookstore example uses session tracking to keep track of the books in the user's shopping cart. Here is an example of the `CatalogServlet` getting a user's session identifier, and getting and setting the application data associated with the user's session:

        public class CatalogServlet extends HttpServlet {

            public void doGet (HttpServletRequest request,
                               HttpServletResponse response)
        	throws ServletException, IOException
            {
                // Get the user's session and shopping cart
    	    HttpSession session = request.getSession(true);
    	    ShoppingCart cart =
                    (ShoppingCart)session.getValue(session.getId());

            // If the user has no cart, create a new one
            if (cart == null) {
                cart = new ShoppingCart();
                session.putValue(session.getId(), cart);
            }
                ...
            }
        }

Because an object can be associated with a session, the Duke's Bookstore example keeps track of the books that a user has ordered within an object. The object is type `ShoppingCart` and each book that a user orders is stored in the shopping cart as a `ShoppingCartItem` object. For example, the following comes from further down in the `doGet` method of the `CatalogServlet`:

        public void doGet (HttpServletRequest request,
                           HttpServletResponse response)
    	throws ServletException, IOException
        {
    	HttpSession session = request.getSession(true);
    	ShoppingCart cart = (ShoppingCart)session.getValue(session.getId());
            ...
            // Check for pending adds to the shopping cart
            String bookId = request.getParameter("Buy");

            //If the user wants to add a book, add it and print the result
            String bookToAdd = request.getParameter("Buy");
            if (bookToAdd != null) {
                BookDetails book = database.getBookDetails(bookToAdd);

                cart.add(bookToAdd, book);
                out.println("<p><h3>" + ...);
            }
        }

Finally, note that a session can be designated as new. A new session causes the `isNew` method of the `HttpSession` class to return `true`, indicating that, for example, the client does not yet know about the session. A new session has no associated data.

You must deal with situations involving new sessions. In the Duke's Bookstore example above, if the user has no shopping cart (the only data associated with a session), the servlet creates a new one. Alternatively, if you need information from the user to start a session (such as a user-name), you might want to redirect the user to an "starting page" where you collect the necessary information.

### Invalidating the Session

A user's session can be invalidated manually or, depending on where the servlet is running, automatically. (For example, the Java Web Server automatically invalidates a session when there have been no page requests in some period of time, 30 minutes by default.) To invalidate a session means to remove the `HttpSession` object and its values from the system.

To manually invalidate a session, use the session's `invalidate` method. Some applications have a natural point at which to invalidate the session. The Duke's Bookstore example invalidates a user's session after the user has bought the books. This happens in the `ReceiptServlet`:

        public class ReceiptServlet extends HttpServlet {

            public void doPost(HttpServletRequest request,
                               HttpServletResponse response)
    	    throws ServletException, IOException {

                    ...
                    scart = (ShoppingCart)session.getValue(session.getId());
                    ...
                    // Clear out shopping cart by invalidating the session
                    session.invalidate();

                    // set content type header before accessing the Writer
                    response.setContentType("text/html");
                    out = response.getWriter();
                    ...
            }
        }

### Handling All Browsers

By default, session tracking uses cookies to associate a session identifier with a user. To also support users that access a servlet with a browser that does not support cookies, or that is set up to reject cookies, you must use URL rewriting instead. (While some web servers support URL rewriting, the `servletrunner` utility that is part of the JSDK2.0 does not. For session tracking to work when a servlet is running within `servletrunner`, the user agent must support cookies.)

When you use URL rewriting you call methods that, when necessary, include the session ID in a link. You must call these methods for every link in the servlet response.

The method that associates a session ID with a URL is `HttpServletResponse.encodeUrl` in the JSDK2.0 and `HttpServletResponse.encodeURL` in the JSDK2.1. If you redirect the user to another page, the method to associate the session ID with the redirected URL is called `HttpServletResponse.encodeRedirectUrl` in the JSDK2.0 and `HttpServletResponse.encodeRedirectURL` in the JSDK2.1.

The URL encoding and encoding redirect methods decide whether the URL needs to be rewritten, and return the URL either changed or unchanged. (The rules for URLs and redirected URLS differ, but in general if the server detects that the browser supports cookies, then the URL is not rewritten.)

> Note: This section shows code that is not a part of the Duke's Bookstore example for JSDK2.1. Duke's Bookstore could use code like that shown in this section if it used URL rewriting. Because URL rewriting is not a part of Duke's Bookstore, think of the examples in this section as pseudo-code.

If the Duke's Bookstore example used URL rewriting, it would, for example, have the following code in the `CatalogServlet`:

        public class CatalogServlet extends HttpServlet {

            public void doGet (HttpServletRequest request,
                               HttpServletResponse response)
    	    throws ServletException, IOException
            {
                // Get the user's session and shopping cart, the Writer, etc.
                ...
    	    // then write the data of the response
                out.println("<html>" + ...);
                ...
                // Get the catalog and send it, nicely formatted
                BookDetails[] books = database.getBooksSortedByTitle();
                ...
                for(int i=0; i < numBooks; i++) {
                    ...
                    //Print out info on each book in its own two rows
                    out.println("<tr>" + ...

                            "<a href=\"" +
                            response.encodeURL("/servlet/bookdetails?bookId=" +
                                               bookId) +
                            "\"> <strong>" + books[i].getTitle() +
                            "  </strong></a></td>" + ...

                            "<a href=\"" +
                            response.encodeURL("/servlet/catalog?Buy=" + bookId)
                            + "\">   Add to Cart  </a></td></tr>" +

                }
            }
        }

Notice that the CatalogServlet returns two links to the user for each book. One link offers details about the book and the other allows you to add the book to your shopping cart. Both URLs would be rewritten because, again, every link that the servlet returns to the user must be rewritten when URL rewriting is used.

If the user clicks on a link with a rewritten URL, the servlet recognizes and extracts the session ID. Then the `getSession` method uses the session ID to get the user's `HttpSession` object.

On the other hand, if the user's browser does not support cookies and the user clicks on an un-rewritten URL, the user's session is lost. The servlet contacted through that link creates a new session, but the new session does not have the data associated with the previous session. Once a servlet loses the session data, the data is lost for all servlets that share the session. You should consistently use URL rewriting if your servlet is to support clients that do not support or accept cookies.

| Start of Tutorial > Start of Trail > Start of Lesson | Search