Session Management Part 3S2C Home « Session Management Part 3
In the last lesson we looked at cookies, the first of two tracking techniques that are more flexible and work with web applications regardless of page volume. In our final lesson on session management
we look at the HttpSession
interface and how we can use objects of this type for session tracking.
Before we look at some of the methods within the HttpSession
interface let's talk about objects of this type. Whenever a user visits a web application for the first time, if we require sessions,
then the container uses the HttpSession
interface to create a session between an HTTP client and an HTTP server. We can think of this session as an object and will refer to it as a
HttpSession
object from now on. A user can have zero or one HttpSession
objects and is uniquely identified by, and can only access their own HttpSession
object.
Unlike URL rewriting, hidden fields and cookies, values stored in a HttpSession
object are not sent to the client but rather a unique session identifier that identifies each individual session
is sent. The unique session identifier is sent appended to the URL as a jsessionid parameter or a cookie is created called JSESSIONID containing the unique session identifier as its
value. We as programmers do not need to worry about the method involved to create the unique session identifier as this is done automatically by the container.
HttpSession
Interface OverviewTop
Using methods of the HttpSession
interface we can add and remove attributes, create and invalidate sessions and also set the life expectancy of a HttpSession
object. With the
methods available, this makes a HttpSession
object the most powerful of all the session management techniques to use and also allows us to use the HttpSession
object with the
other techniques we have seen such as URL rewriting and cookies.
The table below shows the declarations of some of the more common methods in the HttpSession
interface, which we cover on the site:
Method Declaration | Description |
---|---|
java.lang.Object getAttribute( java.lang.String name) | Returns value of bound object with specified name for this session, or null if bound object not found. |
java.util.Enumeration getAttributeNames() | Returns Enumeration of String objects containing attribute names of all objects bound to this session. |
java.lang.String getId() | Returns a String object containing the unique session identifier for this session. |
int getMaxInactiveInterval() | Maximum time interval, returned in seconds, that the servlet container will keep this session open between client requests. |
void invalidate() | Invalidate session and unbind any objects bound to it. |
boolean isNew() | Returns true if client does not yet know about, or chooses not to join the session. |
void removeAttribute(java.lang.String name) | Removes bound object with specified name from this session. |
void setAttribute | Binds object to this session using specified name . |
void setMaxInactiveInterval(int seconds) | Maximum time interval, specified in seconds , that the servlet container will keep this session open between client requests.If setMaxInactiveInterval() is passed 0 , the HttpSession object expires immediately.If setMaxInactiveInterval() is passed a negative value, the HttpSession object will never expire and will remain on the heap until the application is unloaded or the servlet container is shut down. |
Use the links in the table above to go to example code of the method in question. For more information on these and the other methods in the HttpSession
Interface follow the link to the official site at the end of the lesson.
Before we write a servlet demonstrating use of a HttpSession
object, let's take a look at the advantages and disadvantages of using this session management technique.
Advantages
A HttpSession
can be used for web applications with small, or large page volumes without any increase in workload or maintenance.
We can add, get or remove attributes from the HttpSession
giving us a great deal of flexibility.
If cookies are turned off we can use the unique session identifier from the HttpSession
object in conjunction with a form of URL rewriting to keep track of a session as shown in the following code and screenshot:
package controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class NoCookiesServlet extends HttpServlet {
private static final long serialVersionUID = 871964L;
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Create a form with display options
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
// Get a HttpSession
HttpSession session = req.getSession();
// Do we have a HttpSession
boolean isThisNewSession = session.isNew();
// Get Session id
String sessionId = session.getId();
// Session Expiry
int sessionExpiry = session.getMaxInactiveInterval();
// Write response
writer.println("<html><head><title>No Cookies</title></head><body>" +
"<a href='" + resp.encodeURL("/nocookies/NoCookies") + "'>click, no Cookies</a>" +
"<p>Was session new on entry? " + isThisNewSession + "</p>" +
"<p>Unique Session identifier: " + sessionId + "</p>" +
"<p>Session removal after " + sessionExpiry + " seconds inactivity</p>" +
"</body></html>");
}
}
As you can see from the screenshot a cookie named JSESSIONID has been created as a session cookie with a session id and path. We can then use methods from the HttpSession
interface, or check for this session cookie, to find out if this is a new visitor or one who already has a session.
Here's another screenshot showing what happens when we turn off cookies to the local host via pagespeed. This shows that when we click on the hyperlink after initial entry the session id is now passed in the URL. So this form of URL rewriting using a session can be used as a fallback if you use cookies and they are disabled.
Disadvantages
Attributes added to a HttpSession
are stored in container memory as objects, not on a client computer, so you have to be aware of memory considerations such as object size and the amount of objects being stored.
Garbage collection of memory stored objects needs to be done efficiently. This can be achieved using the setMaxInactiveInterval(int seconds)
method of the HttpSession
interface,
to set an inactive time limit on a session. You can also use the invalidate()
method of the HttpSession
interface, which invalidates the session and unbinds all objects belonging to it.
Objects added to a HttpSession
should be from a class that implements java.io.Serializable
, so that if serialization is necessary, we don't get an exception.
Terminating A SessionTop
Before we look at an example of using sessions it's worth discussing how we can terminate a session. You might think that a session ends when a user closes the browser but it persists. If that user
then reopens the browser and goes back to the same web application a new session is started and the old session sits there using up resources. So we need a way to manage our sessions. Sessions can be
terminated in a variety of ways using methods within the HttpSession
interface or the DD. We will look at the code to terminate sessions using methods of the HttpSession
interface first:
HttpSession.setMaxInactiveInterval(60); // Session will expire in 60 seconds
HttpSession.setMaxInactiveInterval(0); // Session will expire immediately
HttpSession.setMaxInactiveInterval(-60); // Session will never expire
HttpSession.invalidate(); // Immediately invalidate session and unbind any objects bound to it
The other way to terminate a session is via the DD using the top-level <session-config> element which is used to define the session timeout attributes for a web application.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
<session-config>
<session-timeout>60</session-timeout>
</session-config>
</web-app>
The <session-config> sub-level element is optional and defaults to 60
. The value you
enter for the <session-timeout> sub-level element must be an integer and is expressed as whole
minutes. Entering a negative value or 0
means the session will never time out. Entering a positive number denotes the period in minutes before the session is invalidated with a maximum value of Integer.MAX_VALUE รท 60
.
HttpSession
ExampleTop
Lets look at an example of creating an object within the HttpSession
, which we store as a key/value pair attribute (more on this shortly).
Within the _ServletsInt
folder create a new folder for the web application we will create in this lesson and call it bookchoice
Within the bookchoice
folder create separate folders to hold our DD, source files and our compiled byte code and called dd
, src
and classes
. Within the
src
folder create two subfolders called controller
and model
.
So after creating these folders your directory structure should look something like the following screenshot:
Coding The Book
ClassTop
We used the Book
class in an earlier example and it models a book title and some details and uses the JavaBeans standard of get<someProperty>
and set<someProperty>
. For a refresher
on JavaBeans you can look at the java section on Getters & Setters.
Cut and paste the following code into your text editor and save it in the c:\_ServletsInt\bookchoice\src\model directory as Book.java
.
package model;
public class Book {
private int num;
private String title;
private String rating;
// Getters and setters
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getRating() {
return rating;
}
public void setRating(String rating) {
this.rating = rating;
}
}
Compiling The Book
ClassTop
Open your command line editor:
Change to directory c:\_ServletsInt\bookchoice\src\model
Compile Book.java
using the java compiler with the -cp
and -d
options as below, making sure you change apache-tomcat-6.0.37
to wherever you downloaded Tomcat to.
javac -cp c:\apache-tomcat-6.0.37\lib\servlet-api.jar -d ..\..\classes Book.java
The following screenshot shows that we get a clean compile and also the Book
class now compiled into the classes\model
directory.
Coding The BookItem
ClassTop
The BookItem
Class models a book item that is to be added to a wish list and uses the JavaBeans standard of get<someProperty>
and set<someProperty>
. For a refresher
on JavaBeans you can look at the the java section on Getters & Setters.
Cut and paste the following code into your text editor and save it in the c:\_ServletsInt\bookchoice\src\model directory as BookItem.java
.
package model;
public class BookItem {
private Book book;
private int quantity;
// Constructor
public BookItem(Book book, int quantity) {
this.book = book;
this.quantity = quantity;
}
// Getters and setters
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
Compiling The BookItem
ClassTop
Open your command line editor:
Change to directory c:\_ServletsInt\bookchoice\src\model
Compile BookItem.java
using the java compiler with the -cp
and -d
options as below, making sure you change apache-tomcat-6.0.37
to wherever you downloaded Tomcat to.
javac -cp c:\apache-tomcat-6.0.37\lib\servlet-api.jar;..\..\classes -d ..\..\classes BookItem.java
The following screenshot shows that we get a clean compile and also the BookItem
class now compiled into the classes\model
directory.
Coding WishListServlet
Top
Ok with our model, the Book
and BookItem
classes coded and compiled we are now ready to code up a servlet that uses the HttpSession
object as its session management
technique. On deployment the init()
routine will run and create 4 books objects for later use.
After initialisation, when the WishListServlet
class below is run the doGet()
will be invoked and we use the HttpServletRequest
method encodeURI()
to get the URI and then use the String
method endsWith()
, to determine whether to
invoke the displayBookList()
method, the displayBookDetails()
method, or the displayWishList()
method.
On initial entry the displayBookList()
method runs and produces a list of books with links to details for each. On book detail selection the displayBookDetails()
method runs
and you can enter a quantity and add to the wish list via the link. On submission the doPost()
method is invoked or you can return to the booklist rather than adding to wish list.
Within the doPost()
method we get an existing HttpSession
object, or create a new HttpSession
object, using the getSession()
method of the
HttpServletRequest
interface. We then create or get our wish list parameter and then add a BookItem
to the wish list. Finally the displayWishList()
method is invoked to display the wish list.
Cut and paste the following code into your text editor and save it in the c:\_ServletsInt\bookchoice\src\controller directory as WishListServlet.java
.
package controller;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import model.Book;
import model.BookItem;
public class WishListServlet extends HttpServlet {
private static final long serialVersionUID = 871964L;
private static final String WISHLIST_ATTR = "wishList";
private List<Book> books = new ArrayList<Book>();
@Override
public void init() throws ServletException {
// Create some Book records for later use
Book book1 = new Book();
book1.setNum(1);
book1.setTitle("Wuthering Heights");
book1.setRating("Classic");
books.add(book1);
Book book2 = new Book();
book2.setNum(2);
book2.setTitle("The Railway Children");
book2.setRating("Good");
books.add(book2);
Book book3 = new Book();
book3.setNum(3);
book3.setTitle("Treasure Island");
book3.setRating("Great");
books.add(book3);
Book book4 = new Book();
book4.setNum(4);
book4.setTitle("Gullivers Travels");
book4.setRating("Classic");
books.add(book4);
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Get request URL so we can decide which method to invoke
String uri = req.getRequestURI();
if (uri.endsWith("/book")) {
// display book list
displayBookList(resp);
} else if (uri.endsWith("/bookDetails")) {
// display all book details
displayBookDetails(req, resp);
} else if (uri.endsWith("/wishList")) {
// display the wish list
displayWishList(req, resp);
}
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Add a Book to wish list
int bookNum = 0;
int quantity = 0;
String num = req.getParameter("num");
if (num != null) {
// Retrieve and convert book number to int type
try {
bookNum = Integer.parseInt(num);
// Retrieve and convert quantity to int type
String quantityStr = req.getParameter("qty");
quantity = Integer.parseInt(quantityStr);
} catch(NumberFormatException ex) {
}
}
Book book = getBook(bookNum);
if (book != null && quantity >= 0) {
BookItem bookItem = new BookItem(book, quantity);
HttpSession session = req.getSession();
// Unavoidable unchecked cast warning as getAttribute() returns Object
List<BookItem> wishList = (List<BookItem>) session.getAttribute(WISHLIST_ATTR);
if (wishList == null) {
wishList = new ArrayList<BookItem>();
session.setAttribute(WISHLIST_ATTR, wishList);
}
wishList.add(bookItem);
}
displayWishList(req, resp);
}
public void displayBookList(HttpServletResponse resp) throws IOException {
// Create a response
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.println("<html><head><title>Books</title></head>" +
"<body><h1>Books In Stock:</h1><ul>");
for (Book book : books) {
writer.println("<li>" + book.getTitle() + "(" + book.getRating() + ") (" +
"<a href='bookDetails?num=" + book.getNum() + "'>Book Details</a>)</li>");
}
writer.println("</ul><a href='wishList'>View Your Wish List</a></body></html>");
}
public void displayBookDetails(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// Create a response
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
int bookNum = 0;
// Retrieve and convert book number to int type
try {
bookNum = Integer.parseInt(req.getParameter("num"));
} catch(NumberFormatException ex) {
}
Book book = getBook(bookNum);
if (book != null) {
// We found the book so lets output to a table with quantity
writer.println("<html><head><title>Display Book Details</title></head>" +
"<body><h1>Display Book Details:</h1><form action='wishList' method='post'>" +
"<fieldset style='background-color:silver;border:1px solid black;" +
"padding:5px;text-align:left;width:300px;'>" +
"<legend>Edit Book Quantity Details</legend>");
writer.println("<input type=hidden name='num' value='" + bookNum + "'/>");
writer.println("<table><tr><td>Title:</td><td>" + book.getTitle() + "</td></tr>");
writer.println("<tr><td>Rating:</td><td>" + book.getRating() + "</td></tr>");
writer.println("<tr><td>Quantity:</td><td><input name='qty' value='1'></td></tr>");
writer.println("<br><tr><td><input type='submit' value='Add To Wish List'/></td>" +
"<td></td></tr></table></fieldset></form>");
writer.println("<br><a href='book'>Book List</a></body></html>");
} else {
writer.println("Sorry, the requested book was not found.");
writer.println("<tr><td><a href='book'>Book List</a></td><td></td></tr>");
}
}
public void displayWishList(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// Create a response
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.println("<html><head><title>Book Wish List</title></head>" +
"<body><h1>Book Wish List:</h1>");
HttpSession session = req.getSession();
// Unavoidable unchecked cast warning as getAttribute() returns Object
List<BookItem> wishList = (List<BookItem>) session.getAttribute(WISHLIST_ATTR);
if (wishList != null) {
writer.println("<form action='wishList' method='post'><table><tr>" +
"<th style='width:200px;text-align:left;'>Book Title</th>" +
"<th style='width:200px;text-align:left;'>Rating</th>" +
"<th style='width:200px;text-align:left;'>Quantity</td></th>");
for (BookItem bookwishlist : wishList) {
Book book = bookwishlist.getBook();
int quantity = bookwishlist.getQuantity();
if (quantity != 0) {
writer.println("<tr><td>" + book.getTitle() + "</td>" +
"<td>" + book.getRating() + "</td>" +
"<td>" + quantity + "</td></tr>");
}
}
writer.println("<tr><td></td><td></td></tr>");
writer.println("</table></form><a href='book'>Book List</a></body></html>");
} else {
writer.println("Sorry, the requested book(s) not found.");
writer.println("<br><br><a href='book'>Book List</a></body></html>");
}
}
public Book getBook(int bookNum) {
// Retrieve a book using number
for (Book book : books) {
if (book.getNum() == bookNum) {
return book;
}
}
return null;
}
}
Compiling WishListServlet
Top
Open your command line editor:
Change to directory c:\_ServletsInt\bookchoice\src\controller
Compile WishListServlet.java
using the java compiler with the -cp
and -d
options as below, making sure you change apache-tomcat-6.0.37
to wherever you downloaded Tomcat to.
javac -Xlint -cp c:\apache-tomcat-6.0.37\lib\servlet-api.jar;..\..\classes -d ..\..\classes WishListServlet.java
The following screenshot shows that we get a compile with warnings. We are using the javac -Xlint
compiler option to show these warnings which are unavoidable as the session.getAttribute()
method returns type Object which we then have to cast. The screenshot also displays the WishListServlet
class now compiled into the classes\controller
directory.
Coding The DDTop
There are no DD entries for this application that we haven't seen before.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
<servlet>
<servlet-name>bookWishList</servlet-name>
<servlet-class>controller.WishListServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>bookWishList</servlet-name>
<url-pattern>/book</url-pattern>
<url-pattern>/bookDetails</url-pattern>
<url-pattern>/wishList</url-pattern>
</servlet-mapping>
</web-app>
Save the web.xml
file in the DD
folder.
Tomcat DeploymentTop
Go to your Tomcat installation which in my case is:
C:\apache-tomcat-6.0.37\webapps\
Within the webapps
folder create a folder for our web application called bookchoice
Within the bookchoice
folder create the WEB-INF
folder.
Copy the web.xml
file and the classes
directory and contents from our development environment into the WEB-INF
folder.
After creating these folders and copying the files from development your Tomcat directory structure within the webapps\bookchoice
folder should look something like the following screenshot:
Testing Our ServletTop
Open a command prompt and change the directory to the location of our Tomcat bin directory and then type in startup
(Windows 7) or startup.sh
(Linux/Unix/OSX).
Press Enter and the Tomcat server should open up in a new window.
You can also start up Tomcat by double-clicking the startup
batch file within your Tomcat /bin
folder.
With Tomcat up and running type the following into your web browser address bar and press enter:
http://localhost:8080/bookchoice/book
The web browser should be directed to the WishListServlet
servlet class within Tomcat and execute it. On initial entry to the WishListServlet
class the doGet()
method will be invoked and will show a list of books wiht links that looks like the following screenshot:
If we were to click on the wish list now we would get the following screenshot as we haven't selected any books for the wish list yet.
Selecting a book details link from the book list will display the following screenshot:
This last screenshot below was taken after adding a few items to the wish list:
As you can see the state of the wish list is being stored in the HttpSession
object and being retrieved when required.
Java DocumentationTop
The following link will take you to the online version of documentation for the Servlet API Documentation .
Take a look at the documentation for the HttpSession
interface we have discussed, which you can find by clicking on the javax.servlet.http
package in the top left pane and then
clicking on the HttpSession
interface in the lower left pane.
Lesson 4 Complete
In our final lesson on session management we looked at the HttpSession
object.
What's Next?
In the next lesson we look at attributes and the different scopes they can be used in.