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
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 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
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.
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.
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.
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.
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
}
}
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.
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.
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.
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 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.
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
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
.
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.
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
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. |
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
.
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.
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
, ororg.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
", orJAVA_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.
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
.
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.
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.
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).
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.
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.
3.144.71.142