C H A P T E R  5

Persistent Sessions

So far, we have covered the basics about the HTTP protocol and how it’s used in a servlet container, like Tomcat. The HTTP protocol, like the Servlet API, has its foundations in the simple request/response mechanism. The state of the conversation between client and server is not maintained in such cases. In this chapter, we will see how the Java Servlet specification overcomes that challenge, and becomes stateful if required, using user sessions. In addition, we will learn how to configure Tomcat to store the session information for users accessing the site, so that it isn’t lost when the server crashes, for example.

In this chapter, we

  • Discuss HTTP sessions servlet implementation and their uses
  • Implement a sample web application to demonstrate usage of HTTP sessions
  • Configure Tomcat to store sessions to a file
  • Use a SQL database to store session information

HTTP Sessions

Before we can start examining HTTP sessions, we must first understand their purpose. When the HTTP protocol—the transport mechanism of all World Wide Web traffic—was first introduced, it was intended to be only a simple request/response protocol, and no state was required to be maintained between autonomous requests. Based on the HTTP protocol specification, the client needs to establish a new TCP connection on every request; therefore, no state information about previous requests is available. This was fine until the Web’s popularity exploded.

One of the biggest demands as the Web’s popularity grew was the ability to maintain—between requests—a state that is specific to each client. For example, online shops required to maintain the customer’s shopping basket between requests. Or, for secured web sites, the mechanism was required to remember the user’s login details for a period of time so the user doesn’t have to log in every time the protected page is required.

Several solutions to this problem are currently available: request parameters, cookies, and session management.

Using request parameters to pass information about the request state is a workaround to simulate statefullness of the request. This is achieved by either by adding hidden form fields to the current form, or by appending request parameters to the URL (usually called URL rewriting). If using hidden form fields, the user won’t see any difference in the browser; but the content of these parameters can be seen if the user opens the HTML source of the web page, making this approach a security risk. In addition, in order to make a web application stateful using this approach, hidden fields need to be added to all forms on the site, and the additional request parameters need to be added to all URLs. For a large site, this makes the web application much more complex to develop and maintain. Finally, this approach increases the bandwidth usage, as information about all previous requests is sent to the server with each subsequent request, affecting the server performance.

Cookies are simple text fields, which are stored on the user’s browser machine. The content of the cookie is controlled by the web application, and with each user request, the browser sends all the cookies for the accessed web application to the server as HTTP headers. By reading the information about the request state in the cookie, the web application can maintain the state for each browser. The web application can store anything to a cookie that relates to the user’s web application access– shopping cart details, login information, and the like.  Because the content of  cookies is just text, a web application on the server must parse the text in order to understand it. As simple text files, cookies do not pose security risks (such as accessing a user’s private files) because they cannot be executed on the client’s machine. However, as they are sent to the server on each request, they can be malicious if used as spyware. That’s why all modern browsers allow users to disable cookies for web browsing. If security is a concern, the cookies’ content can be encrypted as well. As with hidden form fields, using cookies for maintaining state increases the bandwidth and affects the server’s performance in case there is a lot of data stored in a cookie (for example, large shopping carts).

The biggest issue with request parameters and cookies as mechanisms for maintaining states is that that in both cases state information is stored on clients’ machines—either in the HTML code of the web page or in the text file on the file system. The consequences are security concerns (if secured resources need to be part of the state, storing them on clients’ computers is risky) and the performance (the state information needs to be sent to the server with each request, and the server needs to process it before reading).

However, as the HTTP protocol is natively stateless, the described solutions are the only two native HTTP mechanisms to maintain states in the HTTP request/response communication. Therefore, a better solution is required, one that will enable web applications to store the state information on the server. That’s why web developers came up with the session management solution: a way to maintain the request state on the server, and still use the stateless HTTP protocol.

The idea behind session management is simple: a web server stores the stateful information for each client locally. It is identified by a session identifier (session id), which is a unique identifier for each client accessing the web application. A server can store this information however it wants – as a hash table in memory, with session identifiers as keys; or in a file or even a SQL database table, with the session id as the primary key. When the client accesses the server for the first time, the server allocates a session id to the client, and creates a new session. The client will then pass its unique session id with each subsequent request to identify itself to the server. The server will find the existing session for the received session id, and match it to the request, making it available for further processing on the server.

The question here is how does the client store the session id that the server allocates to it? And how then does the client send its session id to the server with each subsequent request? The answer is simple: it uses cookies, or hidden form fields. We said before that cookies and request parameters are the only native HTTP features available to simulate statefulness—and session management uses just that. If request parameters are used, every form in the web application has a hidden field for the session id, which is then passed to the server just like any other request parameter. If using cookies, a server sends a cookie with session id after first request. The cookie is then sent back to the server with each request, and that’s how the server gets the session identifier from the client. Because only the identifier is ever stored in the browser, there is no fear that confidential data can be left unprotected in the user’s browser. In addition, the amount of information sent with the cookie is small, so there is no performance trade-off.

Session management (or HTTP session, as it’s usually called) quickly became the de-facto standard for stateful HTTP communication. Most web servers implement session management, including Microsoft’s IIS, Apache Web Server, and Apache Tomcat. Using HTTP session mechanism, all the user session information is stored on the server, which is more secure and much more performant. In this chapter, we focus on HTTP sessions, specifically HTTP sessions in the Java servlets context with Tomcat.

Now that we understand more about session management in general, we can take a look at the session implementation in the Tomcat servlet container.

The Servlet Implementation of HTTP sessions

The Java Servlet API implements HTTP sessions using an interface named, appropriately enough, javax.servlet.http.HttpSession. Every servlet container must implement this interface, including Tomcat 7. The class that implements this interface will use a unique identifier, the session id, to look up a user’s session information. This identifier is stored in the client’s browser (in the cookie, or as a request parameter) and is part of every HTTP request.

The HttpSession interface defines several methods for accessing and modifying a user’s session information. Table 5-1 describes the some most commonly used of these methods.

Table 5-1. Commony Used Methods of the HttpSession Object

Method Description
getId() The getId() method returns a java.lang.String representing the unique identifier assigned to this user’s session.
getAttribute(String name) The getAttribute() method takes a java.lang.String parameter, name, and returns the object bound with the specified name in this session, or null if no object is bound under the name.
getAttributeNames() This method returns a java.util.Enumeration of java.lang.String objects containing the names of all the objects bound to this session.
setAttribute(String name, Object value) The setAttribute() method takes a name/value pair and binds the object referenced by the value parameter to this session. The name parameter is used as the key to access object. If an object is already bound to the name parameter, the object is replaced with the most recent value.
removeAttribute(String name) This method removes the attribute stored in the session and specified by name argument. In case the attribute is not present, it doesn’t do anything.
getCreationTime() This method returns the long value of the time when the session was created. The value represents the milliseconds passed since January 1, 1970, 00:00:00 GMT.
getLastAccessedTime() This method returns the long value of the time when the session was last accessed. The value represents the milliseconds since January 1, 1970, 00:00:00 GMT.
invalidate() This method is used to invalidate this user’s session, which will in turn remove all session attributes from the invalidated session.

By default, the servlet container uses cookies for session tracking. The cookie stored in the browser is named JSESSIONID by default, as per Java Servlet specification. Since Servlet API 3.0, this name can be customized in the web.xml file. In order to see the cookie with session information, we’re going to access Tomcat’s Manager web application, which we discussed in Chapter 4. If you start your Tomcat instance, and access Tomcat’s Manager web application on the URL http://localhost:8080/manager/html, a session will be created for you. You will see the Manager web application under Applications in the Manager home page, and the value in the Sessions column will be 1. If you now take a look at the cookies stored in your browser (you can search cookies by host name localhost), you will see the JSESSIONID cookie with the session id as its content. Figure 5-1 shows the cookie in the Firefox web browser.

images

Figure 5-1. JSESSIONID cookie stored in the Firefox browser

Every time you click on any of the links in the Manager web application, this cookie will be sent to Tomcat, and based on the session id stored in the cookie, Tomcat will identify the session as belonging to you and match it to the request.

If cookies are disabled in your browser, the session id will have to be sent along with every request as a request parameter (whether as a hidden form parameter or as part of the URL). We can demonstrate this by disabling cookies in your browser. Consult your browser documentation to find out how can this be achieved (or just search the web for “BROWSER_NAME disable cookies,” replacing BROWSER_NAME with the name of your browser). In Firefox, you can go to Options > Privacy, select “Custom history settings,” and uncheck the “Accept cookies from sites” check box. Restart your browser and Tomcat server after the changes so all other sessions are cleared.

If you now access the Manager web application like before, you will see one session—just like with the cookies. But now if you click on any link on the Manager’s home page, you will see that the URL in the browser address bar has changed, and now includes the jsessionid request parameter, with a long String value, representing the session id. Figure 5-2 illustrates the browser’s address bar after we clicked on the “List applications” link from the Manager’s home page.


images

Figure 5-2. URL rewritten for session tracking when cookies are disabled

We have seen how Tomcat employs cookies and URL rewriting for session tracking. For the rest of the examples in this chapter, we will assume cookies are enabled and used for session tracking. Let’s now see session in action, using a real-world example.

Shopping Basket Session Example

To demonstrate the HTTP session usage with servlets, we will introduce an example that we will use throughout this chapter. We will implement a session-stored shopping basket, with the typical functionality used on online shopping web sites.

Firstly, let’s introduce the HTTP shopping basket object model in Java. We will need two classes to model the shopping basket: class Item, which will represent a single product that can be bought (and added to shopping basket), and the shopping basket itself, modeled in HttpShoppingBasket class. Listing 5-1 shows the Java implementation of the Item class.

Listing 5-1. Class Item, Representing the Single Product Added to the Shopping Basket

public class Item implements Serializable{
    private String name;
    private double price;

    public Item(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }
}

As you can see, we are using a simple implementation, storing only the name of the product and its price, which will be sufficient for our example.

Listing 5-2 shows the HttpShoppingBasket class, which contains the collection of products (Items), and the total value of the order.

Listing 5-2. Simple Shopping Basket Implementation

public class HttpShoppingBasket implements Serializable{

    private List<Item> items = new ArrayList<Item>();
    private double totalValue = 0;

    public void addToBasket(Item item){
        this.items.add(item);
        this.totalValue+=item.getPrice();
    }
    
    public List<Item> getItems() {
        return items;
    }

    public double getTotalValue() {
        return totalValue;
    }
}

We have two simple properties: items, representing the collection of all items added to the basket by the customer, and totalValue, a double property that represents the total sum of the entire basket. The method addToBasket(..) simply adds Item to the basket’s items collection, and increases the totalValue for the Item’s price.

images NOTE Java code for this example can be found in the source code distributed with this book, along with all other examples in the book. The directory of the source code represents the chapter number, so all examples can be found in the BookCode/chapter5 directory.

Now that we have the basic object model for a shopping basket, we need to implement the servlet that will update the basket when the user clicks on the “Add to basket” button for the product he or she wishes to buy.

We will implement the AddToBasketServlet servlet, using the Servlet API 3.0 style, introduced in Chapter 3. Listing 5-3 shows the servlet class implementation.

Listing 5-3. AddToBasketServlet Java class, Implemented Using the Latest Servlet API 3.0

@WebServlet(urlPatterns={"/addToBasket.html"})                           #1
public class AddToBasketServlet extends HttpServlet{                     #2

    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response)
                throws ServletException, IOException {                   #3
 
        HttpSession session = request.getSession(true);                  #4
        HttpShoppingBasket basket = (HttpShoppingBasket)
             session.getAttribute("SHOPPING_BASKET");                    #5
        if(basket == null){
            basket = new HttpShoppingBasket();                           #6
        }

        String productName = request.getParameter("productName");        #7
        double price = Double.parseDouble(request.getParameter("price"));

        Item item = new Item(productName, price);
        basket.addToBasket(item);                                        #8

        session.setAttribute("SHOPPING_BASKET", basket);                 #9
        response.sendRedirect("/chapter5/jsps/products.jsp");            #10
    }
}

images NOTE At the time of writing, Apache Tomcat 7 and Apache GlassFish 3 are the only released application servers that support the Servlet API 3.0.

Based on the Servlet API 3.0 specification, we have set the urlPattern for this servlet using @WebServlet annotation (#1). The Servlet API 2.X style would require the servlet metadata, including urlPattern, to be configured in a web.xml file. However, Servlet API 3.0 allows this to be set on the servlet class itself, using @WebServlet annotation. This annotation is the only Java Servlet 3.0 specific feature we are using in this example; the rest of the AddToBasketServlet class is typical servlet implementation, regardless of the Servlet API you are using.

The servlet class extends standard HttpServlet from Java Servlet API library, and extends its doPost(..) method, that handles HTTP POST method (#3).

Next, we take the currently active session (#4). The HttpSession object that represents current session can be extracted from the HttpServletRequest by calling HttpServletRequest.getSession(boolean createNew). The boolean argument of this method specifies what the method should return in case no HttpSession has yet been associated with the user. If the argument passed in is false, the method will return null in case no session is found. We are passing value true as the argument—meaning that a new HttpSession will be created if no active session is found for the user.

To get the object previously stored in the session, all we need to do is call HttpSession.getAttribute(String attributeName) method (#5). The method will return the object stored with the key specified as the attributeName argument. If no object is found for the given key, this method will return null. We are trying to find an existing shopping basket in the session, using the “SHOPPING_BASKET” attribute name. In case the user’s shopping basket is empty (for example, when the user tries to add the first item to the basket), this will return null, and we need to construct new HttpShopingBasket for that user (#6).

Now we have a reference to the user’s shopping basket, and we want to add the new Item to it. We will assume that the item’s details are passed as standard request parameters, and we are going to extract them from the request, using HttpServletRequest.getParameter(String paramName) method (#7). Using the values extracted, we will instantiate a new Item object, and add it to the existing basket (#8).

At this point, our shopping basket has been updated to contain the new Item. If the basket has been empty before, we create a new one (#6), and then add the first Item to it (#8). In case we found an existing basket (#5), we just add another Item to its items collection (#8).

All we have to do now is to store the shopping basket to the session again (#9). Simply enough, all we have to do is call HttpSession.setAttribute(String attributeName, Object value), passing the attribute name (“SHOPPING_BASKET”) and the object we want to store (basket instance of HttpShoppingBasket). In case there is a “SHOPPING_BASKET” object stored in the session already, this method will simple overwrite it with the newer value—which is the behavior that we expect.

Finally, after the session has been updated, we redirect the user to our online store web page, so he can buy more stuff (#10). We are using the HttpServletResponse.sendRedirect(..) method to do this, passing the relative URL of the page we want to redirect the user to.

To make sure our servlet is loaded correctly by the Tomcat 7 servlet container, we need to configure its web.xml file. Because we have configured the servlet’s urlPattern using annotation metadata, the web.xml will be quite simple. Listing 5-4 shows the contents of the web.xml file, which should be located in the /WEB-INF/ directory of the web application’s WAR file.

Listing 5-4. Web Deployment Descriptor (web.xml) for a Servlet API 3.0 Web Application

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                                            http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">

   <display-name>Chapter 5</display-name>
    <description>Http Session Demo</description>
    <metadata-complete>false</metadata-complete>        #1
</web-app>

In addition to the standard displayName and description elements, all we need to set is the metadata-complete element with value false (#1). This will tell Tomcat that the servlet configuration in this web.xml file is not complete, and that it should scan all the loaded classes for servlet metadata annotations (like @WebServlet from our servlet class). If you don’t configure this element in the web.xml file, the Tomcat won’t recognize the AddToBasketServlet class as the servlet that needs to be deployed, and you will see 404 Page Not Found page displayed in your browser.

Finally, in order to complete this sample web application, we will need JSP page that will list all the products available in our online shop, and which will show the content of the user’s shopping basket.

Listings 5-5 and 5-6 show the JSP file we will use, and it should be located in the /WEB-INF/jsps/products.jsp location within our project. Let’s first take a look at the list of products available for shopping.

Listing 5-5. JSP File That Lists All Products Available for Shopping

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
  <head>
   <title>Apress Demo Store</title>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <div class="content">
      <b>Welcome to Apress store </b>
        Select product: <br/>
        <table>
            <tr>
                <td>Product Name</td>
                <td>Price</td>
                <td>Actions</td>
            </tr>
            <tr>
                <td>Apache Tomcat 7</td>
                <td>$34.99</td>
                <td>
                    <form action="/chapter5/addToBasket.html"
                          method="POST">                                     #1
                        <input type="hidden"
                              name="productName" value="Apache Tomcat 7"/>   #2
                        <input type="hidden" name="price" value="34.99"/>    #3
                        <input type="submit" value="Add to basket" />        #4
                    </form
                </td>
            </tr>
            <tr>
                <td>Pro Spring 3</td>
                <td>$39.99</td>
                <td>
                    <form action="/chapter5/addToBasket.html" method="POST">
                        <input type="hidden"
                               name="productName" value="Pro Spring 3"/>
                        <input type="hidden" name="price" value="39.99"/>
                        <input type="submit" value="Add to basket" />
                    </form>
                </td>
            </tr>
            <tr>
                <td>Android Development for Beginners</td>
                <td>$24.99</td>
                <td>
                    <form action="/chapter5/addToBasket.html" method="POST">
                        <input type="hidden" name="productName"
                               value="Android Development for Beginners"/>
                        <input type="hidden" name="price" value="24.99"/>
                        <input type="submit" value="Add to basket" />
                    </form>
                </td>
            </tr>
        </table>
</body>
</html>

We are just listing a few books that are available for adding the to basket, in the HTML table. For each row in the table, in the Actions column, we add an HTML form for adding that product to the basket. The form’s action URL is the one we configured for our servlet’s urlPattern before— /chapter5/addToBasket.html—and the form method is set to POST to match our doPost(..) method implementation (#1). The form has two input fields, matching the request parameters we extracted in our servlet (#2 and #3). Finally, we add a submit field that will show in our web page as an Add to basket button.

Now that we have the storefront page, we need to display the content of the basket for the user browsing the store. Listing 5-6 shows the JSP part that does it, which we will add to the bottom of the product list from Listing 5-5.

Listing 5-6. JSP Showing the Contents of the Shopping Basket

<c:set var="basket" value="${sessionScope.SHOPPING_BASKET}"/>            #1
        <c:if test="${basket != null && not empty basket.items}">        #2
            <b>Your shopping cart:</b> <br/>
            <table>
                <tr>
                    <td><b>Product</b></td>
                    <td><b>Price</b></td>
                </tr>
                <c:forEach  items="${basket.items}" var="item">          #3
                    <tr>
                        <td><c:out value="${item.name}"/></td>
                        <td><c:out value="${item.price}"/></td>          #4
                    </tr>
                </c:forEach>

            </table>
            <tr>
                        <td><v>Total:</b></td>
                        <td>
                          $<c:out value="${basket.totalValue}"/>         #5
                        </td>
            </tr>
        </c:if>
        <c:if test="${basket ==null || empty basket.items }">            #6
            <b>Your basket is empty</b>
        </c:if>

In order to get access to the session attribute in the JSP page, we are using the sessionScope implicit argument (#1). This argument is implicitly made available on all JSP pages by the servlet container (Tomcat 7 in our case), and you can access any session attribute by specifying the attribute name after the dot. Using the c:set directive from standard JSP tag library, we store the value of the “SHOPPING_BASKET” session attribute to the local variable basket, for easier access on the JSP page.

Next, we check if the basket has any content, to be safe from unexpected exceptions on the page (#2). If basket is not empty, we simply list its content using JSP’s c:forEach directive (#3), and render the name and price for each item in the basket (#4); and the total basket value at the bottom (#5).

Finally, in case the basket is empty (when user accesses the store for the first time), we display the appropriate message (#6).

Let’s now try our example in the browser. If you package the web application in the WAR file (we will call it chapter5.war) and deploy it to Tomcat—either by copying it to Tomcat’s webapps directory, or using the Manager web application—you should see the products list with empty basket when you navigate to http://localhost:8080/chapter5/products.jsp. Figure 5-3 shows how the page should look when opened in the browser.

images

Figure 5-3. The products list, with an empty basket

If you add a couple of products to the basket (by clicking the “Add to basket” button), you will see the basket updated after adding each product. Figure 5-4 shows the page after two products have been added to the shopping basket.

images

Figure 5-4. Updated shopping basket after adding two products to it

This example demonstrates how the browsing becomes stateful when using sessions. After each reload, the page will show the content of the shopping basket, although the products are added to the basket in the previous requests.

In case cookies are disabled, session tracking will be done using URL rewriting, which requires that a session identifier is encoded in every URL. In order to render links with a session id in your JSP files, you have to encode every URL, using the HttpServletResponse.encodeURL(String url) method. For example, in the previous above, the form will have to have an encoded action attribute, like in the following code snippet:

<form action="<%=response.encodeURL("/chapter5/addToBasket.html")%>" method="POST">

You should use the same approach in case you need to use URL rewriting for session tracking.

Invalidating a Session

The HttpSession object is stored on the server and remains active for the duration of the user’s browsing of the site. If the user becomes inactive for a period of time, his session will be invalidated. By default, this time is set to 30 minutes on Tomcat, but it can be configured for each web application in the web.xml file. To configure the session timeout, just add the following snippet to the web.xml:

<session-config>
        <session-timeout>60</session-timeout>
 </session-config>

The value set using the session-timeout element is in minutes, so the previous configuration will extend the user’s session to one hour. Setting the session timeout to -1 will make the session active indefinitely.

The session is also invalidated when the browser is closed. You can easily demonstrate this by closing the browser after adding a few products to the basket in our sample shopping basket web application (after Figure 5-4, for example). After the browser is shut down, open it again, and navigate to the products.jsp page once more—you will see that the basket is empty again.

Finally, HttpSession can be invalidated programmatically, by calling the HttpSession.invalidate() method. You will call this method from your servlet code when you implement the logout mechanism for your web application, for example.

You can extend our shopping basket example by adding the “Clear basket” button, which will remove all products from the current basket. All you need to do is implement another servlet, which will simply call request.getSession(true).invalidate(), and redirect the user to the products.jsp page again. You can get inspiration from our AddToBasketServlet. After you have the servlet implemented, just add a link to the basket page, pointing to the URL matching the servlet’s urlPattern. We don’t have enough space to show you the solution here, but this chapter’s sample code that is distributed with the book has the solution fully implemented, in case you need any guidance.

Session Management in Tomcat

Session management in Tomcat is the responsibility of the session manager, defined with interface org.apache.catalina.Manager. Implementations of the Manager interface manage a collection of sessions within the container. Tomcat 7 comes with several Manager implementations. Table 5-2 describes all concrete Manager implementations available in Tomcat 7.

Table 5-2. Session Manager Classes Available in Apache Tomcat 7

Session Manager Description
org.apache.catalina.session.StandardManager Default manager implementation, with limited session persistence, for a single Tomcat instance only.
org.apache.catalina.session.PersistentManager Configurable session manager for session persistence on a disk or relational database. Supports session swapping and fault tolerance.
org.apache.catalina.ha.session.DeltaManager Replicates session across the cluster, by replicating only differences in session data on all clusters. Used in a clustered environment only.
org.apache.catalina.ha.session.BackupManager Replicates session across the cluster by storing backup of all sessions for all clustered nodes on a single, known, backup node. Used in a clustered environment only.

Session manager for the Tomcat instance is configured in the TOMCAT_HOME/conf/context.xml file.

All session managers are responsible for generating unique session ids, and for managing session lifecycles (to create, inactivate, and invalidate sessions). In addition to these main responsibilities, each manager has more specific features and options.

images Note  All Tomcat session managers serialize session data using Java object serialization before persisting its binary representation. Therefore, in order for session persistence to work correctly, all Java objects that are stored in the session should implement the java.io.Serializable interface.

In the following sections of this chapter, we will cover the configuration and usage of StandardManager and PersistentManager. We will not be covering configuration of session managers in clustered environments (using DeltaManager or BackupManager). You can find more information about cluster session managers at http://tomcat.apache.org/tomcat-7.0-doc/config/cluster-manager.html.

StandardManager

StandardManager is the default session manager implementation on Tomcat 7. This means that if you don’t configure any session manager in the Tomcat’s context.xml file, Tomcat will instantiate and use the StandardManager for you. If you are using Tomcat as it was downloaded from the Internet, you can see that there is no manager configuration in the Tomcat’s context.xml file—so you are using the default StandardManager.

Using the Default StandardManager

StandardManager stores user sessions on disk between server restarts by default. After the server is restarted, all sessions are restored to the state they were in before the server was stopped.

The sessions are stored in the work directory for each web application, in the file SESSIONS.ser. Only active sessions are persisted to this file; all inactive sessions are lost after every Tomcat restart, if you are using StandardManager. Because StandardManager persists sessions only on graceful server shutdowns, in the case of an unexpected server crash, no session will be saved.

To demonstrate the usage of StandardManager, you can start Tomcat, load our sample shopping basket web application, and add a few books to the basket. Then, without closing the browser, stop the server gracefully (by running a shutdown script in CATALINA_HOME/bin directory). After the server is stopped, you should see the SESSIONS.ser file on your disk, where Tomcat saved all active sessions. The file will be located in the directory that matches the context name where the web application is deployed. We have deployed a shopping basket application under /chapter5 context, so the saved sessions file will be located at CATALINA_HOME/work/Catalina/localhost/manager/SESSIONS.ser. If you see this file, it means that StandardManager persisted the sessions for your web application successfully.

Now start Tomcat again (by running /CATALINA_HOME/bin/startup.sh on Linux or startup.exe file on Windows), and load the shopping basket web application again. You will see that the basket still contains the items you added before the server was stopped.

Let’s now demonstrate what happens when the server crashes unexpectedly. You will need to stop Tomcat without using the shutdown script. On Windows, list all processes in Task Manager, find the java process belonging to Tomcat and kill it. On Linux based operating systems, you can just run killall -9 java in the terminal. That will kill all java processes, including Tomcat.

Now start Tomcat again, using the standard startup script, and load the shopping basket sample application. The basket will be empty this time. In addition, you won’t be able to locate SESSIONS.ser file on the disk anymore; all the shopping you did before the server crashed has been lost.

Configuring StandardManager

We have seen Tomcat’s StandardManager in action, without even configuring it, due to the convenience of the default manager configured by Tomcat automatically. But if you need to customize the behavior of the StandardManager, you will need to configure it manually in Tomcat’s context.xml file. The session manager is configured using the <Manager> element, within the <Context> element in the context.xml file. Listing 5-7 shows a sample StandardManager configuration.

Listing 5-7. Simple Configuration of StandardManager in context.xml File

<Manager
       className="org.apache.catalina.session.StandardManager"        #1
       maxActiveSessions="-1"                                         #2
       pathname="/var/sessiondata/mysessions.ser"                     #3
       sessionIdLength=”32”>                                          #4
</Manager>

We set className attribute to the fully qualified class name of the StandardManager (#1). This tells Tomcat to use the StandardManager implementation as session manager. Next, we configure the maximum number of sessions allowed for the Tomcat instance by setting the maxActiveSession attribute (#2). The negative value means that the number of sessions in unlimited. We can also configure the path of the file where the session information is stored on server shutdown (#3). The path can be absolute, like in our sample configuration, or relative, when the file is created relative to the web application’s work directory. Finally, we configure the length of the session id in bytes (#4). The session ids must be unique on the Tomcat instance, so the value of this attribute should be configured in relation to the maxActiveSessions attribute value.

If you add the code snippet from Listing 5-7 to your Tomcat’s context.xml file, and restart the server, you will be using customized StandardManager for session management. If you access our shopping basket web application again, and shutdown Tomcat gracefully, you will see that the sessions are now stored in the newly configured file on disk.

There are a number of attributes available for the <Manager> element in context.xml file. Some of the attributes are common for all Manager implementations. Table 5-3 shows those attributes that can be used to configure any concrete Manager implementation available on Tomcat.

Table 5-3. Common <Manager> Configurable Attributes

Attribute Description
className The className attribute represents the fully qualified class name of the Manager to use.
distributable Sets whether the manager should enforce distributable application restriction, as specified in the Servlet API—meaning that all session attributes implement java.io.Serializable interface. Default is false.
maxActiveSessions Configures the maximum number of sessions that can be created. By default, there is no limit (value is -1)
maxInactiveInterval The maximum time interval after which session can be marked as inactive, in seconds. The default value is 60. This attribute is overridden by Tomcat’s or the specific web application’s session-timeout value configured in web.xml.
sessionIdLength The length of the session identifier, in bytes.

In addition to these generic Manager configuration attributes, Table 5-4 describes the attributes specific to the StandardManager implementation.

Table 5-4. StandardManager’s Configurable Attributes

Attribute Description
pathname The absolute or relative path of the file where session information is stored. Relative paths are relative to the web application’s work directory. Default is SESSIONS.ser.
processExpiresFrequency Configures how often the background process for checking the session expiration is going to run. The lower the amount, the checks will be more often. The default value is 6.
secureRandomClass The class that is used to generated session ids; must extend java.security.SecureRandom, which is the default option as well.
secureRandomAlgorithm Defines what random algorithm to use, default value is SHA1PRNG.
secureRandomProvider Configures the provider for random numbers generation; defaults to the default provider for the platform where Tomcat is running.
Disabling Session Persistence on Server Shutdown

In some cases, you don’t need persistence of active sessions on server restart. For example, if you want to force all users to create new sessions after you deploy a completely new version of the web application. Because StandardManager by default stores active session on server shutdown, even if you don’t configure it at all, you may get this feature out of the box, without needing it.

In order to achieve this, you need to configure the <Manager> element in the context.xml file, and set the pathname attribute value to empty String (“”). You don’t need to specify any other attribute, including the className—all other attributes will use default values. So in case you want to disable session persistence entirely, the session manager configuration should look like the following code snippet:

<Manager pathname="" />

And that’s all. Although quite simple, this configuration has been the cause of a lot of confusion in the Tomcat community; that’s why we have demonstrated it here specifically.

StandardManager is useful for development and prototyping, and in cases you don’t need sessions persistence at all. But, usually, real-world web applications require some form of session persistence, so that the online users don’t lose their session data in case of planned or unplanned server restarts. Let’s see how we can achieve that using Tomcat’s PersistentManager.

PersistentManager

Session persistence became an issue when session objects needed to be swapped in and out of memory based upon activity, load, and during container restarts. Tomcat servers configured in a clustered environment also are required to store and load session information from other instances in the cluster. There needed to be a way to save and retrieve the session information when these events occurred.

For example, applications with thousands of concurrent users may require too much memory to keep all session data in memory. Since activity patterns between users are different, memory can be managed by backing up idle sessions to some persistence store, like disk or database, and load them when needed.

In addition to standard session creation, access, and maintenance responsibilities, PersistentManager has the ability to swap out idle sessions to external storage. Idle sessions are those that are still active, but are not accessed for a configured period of time (the default is 60 seconds), so they can be stored away temporarily while session of the users currently active can be loaded to memory.

To use session persistence you have to configure Tomcat’s PersitentManager as session manager in the context.xml file.

Configuring PersistentManager

PersistentManager persists session data using the org.apache.catalina.Store interface. Tomcat currently comes bundled with two implementations of the Store interface: the org.apache.catalina.session.FileStore, for persistence to file on disk, and org.apache.catalina.session.JDBCStore, for persisting session data to the relational database. We will discuss both of these implementations in this section.

But first we need to configure the <Manager> element in the Tomcat’s context.xml file to use PersistenceManager. Table 5-5 shows the allowed attributes that can be configured for PersistentManager and their default values.

Table 5-5. PersistentManager’s Configurable Attributes

Attribute Description
saveOnRestart The saveOnRestart attribute, if true, signifies that all active sessions will be saved to the persistent store when Tomcat is shut down. All sessions found in the store are reloaded upon startup. All expired sessions are ignored during shutdown and startup.
minIdleSwap The minIdleSwap attribute represents the minimum length of time, in seconds, that a session can remain idle before it is swapped out to the persistent store. If minIdleSwap equals -1, then there is no minimum time limit before a swap can occur.
maxIdleSwap Opposite to the minIdleSwap, this attribute specifies the maximum length of activity after which the session should be swapped to disk. The default value is -1, which disables this feature entirely
maxIdleBackup The maxIdleBackup attribute represents the length in time, in seconds, that a session can remain idle before it is backed up to the persistent store. When a session is backed up, it remains active as opposed to being swapped out, in which it is removed from the collection of active sessions. If the maxIdleBackup attribute is set to -1, no sessions are backed up.
processExpiresFrequency This configures how often the background process for checking the session expiration is going to run. The lower the amount, the more frequent the checks will be. The default value is 6.
secureRandomClass The class that is used to generated session ids, must extend java.security.SecureRandom, which is the default option as well.
secureRandomProvider This configures the provider for random numbers generation; defaults to default provider for the platform where Tomcat is running.
secureRandomAlgorithm Defines what random algorithm to use, default value is SHA1PRNG.

In order for PersistentManager to perform correctly, based on configuration, Tomcat will need to track the active requests for each session. This is required to determine valid sessions; a session is valid if it has at least one active request. Tomcat does not do this by default, in order not to affect performance and memory footprint unnecessarily, especially since PersistentManager isn’t configured by default. In order to tell Tomcat to start tracking active requests for each session, you need to set at least one of the following Java environment properties:

  • org.apache.catalina.session.StandardSession.ACTIVITY_CHECK, or
  • org.apache.catalina.STRICT_SERVLET_COMPLIANCE

You can set any of these variables in the JAVA_OPTS environment variable:

  • JAVA_OPTS=" org.apache.catalina.session.StandardSession.ACTIVITY_CHECK=true", or
  • JAVA_OPTS=" org.apache.catalina.STRICT_SERVLET_COMPLIANCE=true"

In addition to attributes configuration, PersistentManager requires the Store element be configured as a nested element. The Store element configures one of the two implementations that we will describe in the next sections.

FileStore

The FileStore uses a file as the storage mechanism for session data. Unlike StandardManager, which saves all sessions in one configured file, FileStore stores session data in one file per session. The filenames are not configurable, but you can configure the directory where the files will be stored.

Listing 5-8 shows the sample PersistentManager configuration using FileStore for storage.

Listing 5-8. PersistentManager Configured with FileStore

<Manager
      className="org.apache.catalina.session.PersistentManager"
      saveOnRestart="true"
      maxActiveSessions="-1"
      minIdleSwap="-1"
      maxIdleSwap="-1"
      maxIdleBackup="-1">
      <Store className="org.apache.catalina.session.FileStore"
            directory="/var/mysessionsdata"/>
</Manager>

Table 5-6 shows all the available attributes for configuration of the nested FileStore.

Table 5-6. FileStore Configurable Attributes

Attribute Description
className This configures how often the background process for checking the session expiration is going to run. The lower the amount, the checks will be more often. The default value is 6.
directory The directory where the session data files will be saved to. If relative, it will be relative to the web application’s work directory, which is the default directory if this attribute is not specified.
checkInterval The number of seconds between checks if any of the swapped sessions is expired.

PersistentManager with FileStore is easy to configure, and convenient to demonstrate how the swapping of idle sessions works. However, since it stores session data in one file per session, in case a large number of sessions are used, a lot of files will be saved and loaded on the file system, and the disk IO operations (save and load) can quickly become a performance issue. That’s why FileStore-based session persistence should be only used for prototyping and demonstration purposes. For production level session persistence, session data should be store to relational database, using JDBCStore.

JDBCStore

The JDBCStore acts much the same as the FileStore does, with the only difference being the storage location of the session information. In this store, Tomcat reads and writes session information from the database defined in the <Store> sub-element.

Before you begin configuring Tomcat to use a JDBC persistent session, you must first create a database to hold your collection of session objects. For the purpose of this section, we’ll create a MySQL database.

Creating a Database for JDBCStore

We will create the database called tomcatsessions. The database that we can be use with JDBCStore, has to have one table for storing session data, with well defined columns for specific data types. The table name, and the column names are fully configurable, so you can decide on which names to use. We will use the table name tomcat_session_data for our sample. Table 5-7 shows the column names we will use in this section, and describes the data stored in each column.

Table 5-7. The Definition of Columns for Session Data Table

Column Description
session_id This column contains a string representation of the unique session ID. The id has a type of varchar(100), and is primary key for tomcat_session_data table.
application_name The name of the web applications engine, host, and context are stored in this context, separated by forward slash; for example /Catalina/localhost/chapter5. The column’s datatype is varchar(255).
valid The valid column contains a single character that represents whether the session is valid or invalid. The valid column has a type of char(1).
maxinactive The maxinactive column contains an integer representing the length of time that a session can remain inactive before becoming invalid. The maxinactive column has a type of int.
last_access The last_access column contains an integer that represents the length of time since the session was last accessed. The last_access column has a type of bigint.
session_data This  column contains the serialized representation of the HTTP session. The data column has a type of mediumblob.

Listing 5-9 shows the SQL scripts that can be executed to create the required database and table for storing session data.

Listing 5-9. SQL Script for Creating the tomcatssessions Database and the tomcat_session_data Table

create database tomcatsessions;
use tomcatsessions;
create table tomcat_session_data (   
   session_id         varchar(100) not null primary key,   
   valid              char(1) not null,
   maxinactive        int not null,
   last_access        bigint not null,
   application_name   varchar(255),
   session_data       mediumblob
 );

To be able to access the tomcatsessions database, you need to create user first, and grant access to the required database to the user created. You can do that with following two SQL commands:

CREATE USER ‘tomcatuser'@’localhost’ IDENTIFIED BY 'abc123';
grant all privileges on tomcatsessions.* to tomcatuser@localhost ;

We now have a MySQL database that can be used as a storage container for HTTP sessions objects.

Configuring Tomcat to use JDBCStore

The configuration of JDBCStore with PersistentManager is very similar to the FileStore configuration from the previous section—all you need to do is use JDBCStore to configure the <Store> nested element. Listing 5-10 shows the sample PersistentManager configuration.

Listing 5-10. PersistentManager Configured to Use JDBCStore for Session Persistence

<Manager
  className="org.apache.catalina.session.PersistentManager"
  saveOnRestart="true"
  maxActiveSessions="-1"
  minIdleSwap="-1"
  maxIdleBackup="-1">
     <Store className="org.apache.catalina.session.JDBCStore"                #1
         driverName="org.gjt.mm.mysql.Driver"                                #2
         connectionURL=
 "jdbc:mysql://localhost/tomcatsessions?user=tomcatuser; password=abc123"    #3
         sessionTable="tomcat_session_data"                                  #4
         sessionIdCol="session_id"                                           #5
         sessionAppCol="application_id"
         sessionDataCol="session_data"
         sessionValidCol="valid"
         sessionMaxInactiveCol="maxinactive"
         sessionLastAccessedCol="last_access"
         checkInterval="60"/>
</Manager>

The attributes configured for the <Manager> element are exactly the same as for the PersistentManager application used with FileStore in previous section. The store element this time references JDBCStore as className (#1). The driver name is the fully qualified class name of the driver class for the configured database, in our case the MySQL database driver (#2). The connectionName attribute specifies the database JDBC connection string, with the username and password to use (#3). Attribute sessionTable specifies the name of the table where session data is stored (#4). After specifying the table name, we can configure the name for each column used with corresponding attributes (#5).

images NOTE  Make sure that the JAR file containing the JDBC driver referenced by the driverName attribute is placed in Tomcat’s CLASSPATH. This is done easily by placing the jar file to the CATALINA_HOME/lib directory.

And that’s it. After Tomcat restarts, we will store all session information in the configured MySQL database. You can access our sample shopping basket application, and create a session by adding few products to the basket. You can now log in to the database tomcatsessions, using SQL console for MySQL and see the session data stored by issuing the SQL command:

select * from tomcat_session_data;

The JDBCStore with PersistentManager is a robust and scalable solution for session persistence on Tomcat, usable for web applications with a large number of concurrent users whose idle sessions can be swapped easily to a database to lower the memory print of the Tomcat server.

Summary

In this chapter, we discussed HTTP sessions and how they are used. First we explained how are HTTP sessions used to keep the state of the request/response conversation with the server, and we described the details of HTTP session implementation in Apache Tomcat. We have implemented a shopping basket example to demonstrate the usage of user sessions in Java web applications. Next, we introduced session persistence with Tomcat, using the default StandardManager for session management. Finally we configured Tomcat’s PersistentManager to persist HTTP session objects to a file on disk using FileStore and to a SQL database using JDBCStore.

In the next chapter, we cover Tomcat security realms.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.71.142