Session Management Part 1S2C Home « Session Management Part 1

In the last lesson we saw how our HTML form was sent back to the server after entry because we didn't put a path in the action attribute. We learnt that omitting the action attribute or leaving it as an empty string sends the submitted form to the same URL used for the request. This could give the impression that our client was in some sort of conversational state with the server. This isn't true of course as the language of the web, which as we have learned is HTTP, is stateless and so there is no way for the HTTP protocol to remember from which client the request was made and inform the server.

What many non-trivial applications require is a way for a web application to recognize requests made by new or returning visitors so we can process client requests appropriately. By using session management we can provide ways to let the server and more importantly the container know, which clients made a particular request through some sort of session tracking mechanism.

In the first of three lessons on conversational state we look at two ways to get around the statelessness of HTTP and the pros and cons of the session management techniques used to do this. First we will look at URL rewriting and then look at the use of hidden fields to achieve conversational state within our applications. Both of these methods of session management are mainly suitable for applications with small page counts as the work involved increases with each new page we add to an application.

URL RewritingTop

The first session management technique we will look at is URL rewriting which involves appending one or more tokens as a query string to a URL, beginning with a ? and delimited by &. This is normally in the form of one or more key/value pairs and the following code shows an example of a URL where a resource called nameform will be looked up on the server and firstname and lastname keys have been sent with values of john and smith.


http://jsptutor.co.uk/nameform?firstname=john&lastname=smith

Before we write a servlet to demonstrate URL rewriting, let's take a look at the advantages and disadvantages of using this session management technique.

Advantages

URL rewriting is not dependant upon storing information on the client's computer as for instance using cookies is, which is a session management technique we will look at in the next lesson.

URL rewriting can be used in combination with cookies, as a fallback for session tracking, if cookies are disabled by the user.

Disadvantages

The length of the URL sent by some of the older browsers is limited in size, so this may be an issue if you send a lot of information across in the URL.

Any information that is passed across in the URL is visible, which might not be the ideal situation if sensitive information such as passwords need to be passed across to the server.

All links need to pass across the URL rewriting information to the server which can be a hit to performance as well as development and maintenance overheads; so the more links we need to pass across the greater the hit and workload.

The container creates a HttpSession object on initial entry to a website, so if you need to keep track of a user's session id then then this will only work for URL rewriting with dynamic pages, not normal HTML static pages.

If the space character or the special characters listed below form part of the query string of a URL they have to be encoded first, unless they are being used as delimiters.


; , / ? : @ & = + $ #
 

URL Rewriting ExampleTop

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

Within the numbers 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 a subfolder called controller.

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

numbers directory structure

Coding NumbersServletTop

We are now ready to code up a servlet that uses URL rewriting as its session management technique. On initial entry to the NumbersServlet class below the doGet() will be invoked and as this is the first time in, the numType parameter will be null and so the displayHomePage() method will run. After this the URL will get rewritten dependant upon the odds or evens option chosen by the user and the displayNumbers() method will be invoked and display the users number choice. After this the URL will get rewritten dependant upon user choices and the appropriate details displayed.

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


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;

public class NumbersServlet extends HttpServlet {

    private static final long serialVersionUID = 871964L;
    private String[] oddNumbers = {"1", "3", "5", "7", "9", "11", "13", "15", "17", "19"};
    private String[] evenNumbers = {"2", "4", "6", "8", "10", "12", "14", "16", "18", "20"};

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException { 
        // Check to see if we passed a number type across in URL and display appropriate page
        String numType = req.getParameter("numType");
        if (numType != null && (numType.equals("odds") || numType.equals("evens"))) { 
            // display odd or even numbers
            displayNumbers(req, resp, numType);
        } else {
            // display home
            displayHomePage(req, resp);
        }
    }

    public void displayHomePage(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {    
        // Create a response
        resp.setContentType("text/html");
        PrintWriter writer = resp.getWriter();
        writer.print("<html><head><title>Odd or Even Numbers Selection</title></head>" + 
                "<body><h1>Select odd or even:</h1>" +
                "<a href='?numType=odds'>odds</a><br>" +
                "<a href='?numType=evens'>evens</a>" +
                "</body></html>");
    }

    public void displayNumbers(HttpServletRequest req, HttpServletResponse resp, String numType) 
            throws ServletException, IOException {
        int pageNum = 1;
        String pageReq = req.getParameter("pageNum");
        
        if (pageReq != null) { 
            // Retrieve and convert page number to int type
            try {
                pageNum = Integer.parseInt(pageReq);
            } catch(NumberFormatException ex) {
            } 
            // Stop ArrayIndexOutOfBoundsException in for loop if user overtypes URL page number
            if (pageNum > 2) {
                pageNum = 1;
            }     
        }
        String[] dispNumbers = new String[10];
        
        if (numType.equals("odds")) {
            dispNumbers = oddNumbers;
        } else {
            dispNumbers = evenNumbers;
        } 
        // Create a response
        resp.setContentType("text/html");
        PrintWriter writer = resp.getWriter();
        writer.print("<html><head><title>Display Numbers</title></head>" + 
                "<body><h1>Display Numbers:</h1>" +
                "<a href='oddsandevens'>Select Numbers</a><br>" +
                "<strong>Page " + pageNum + "</strong><br>");
        int startIdx = pageNum * 5 - 5; 
        // Cycle through selected array and send to HTML
        for (int i = startIdx; i < startIdx + 5; i++) {
            writer.println(dispNumbers[i] + "<br>");
        }    
        writer.println("<a href='?numType=" + numType + "&pageNum=1'>Page 1</a>");
        writer.print(" <a href='?numType=" + numType + "&pageNum=2'>Page 2</a>");
        writer.print("</body></html>");
    }
}

Compiling NumbersServletTop

Open your command line editor:

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

Compile NumbersServlet.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 NumbersServlet.java

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

compile NumbersServlet class

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>numbers</servlet-name>
      <servlet-class>controller.NumbersServlet</servlet-class>
   </servlet>
   <servlet-mapping>
      <servlet-name>numbers</servlet-name>
      <url-pattern>/oddsandevens</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 numbers

Within the numbers 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\numbers folder should look something like the following screenshot:

Tomcat directory structure1
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/numbers/oddsandevens

The web browser should be directed to the NumbersServlet servlet class within Tomcat and execute it. On initial entry to the NumbersServlet the doGet() will be invoked and as this is the first time in the numType parameter will be null and so the displayHomePage() method will run and produce a screen that looks like the following:

run NumbersServlet class

After this the URL will get rewritten dependant upon the odds or evens option chosen by the user and the displayNumbers() method will be invoked and display the users number choice. The following screenshot shows the screen after the odds hyperlink has been clicked

Odds option NumbersServlet class

This last screenshot shows the screen after the Page 2 hyperlink has been pressed.

page2 NumbersServlet class 2

Hidden FieldsTop

The second session management technique we will look at is hidden fields which involves putting hidden fields into a HTML form rather than appending values to the URL as we did with URL rewriting. The hidden fields get passed over to the server along with the defaults and other values entered on the HTML form and can then be used to track the session.

Before we write a servlet to demonstrate using hidden fields for session tracking, let's take a look at the advantages and disadvantages of using this session management technique.

Advantages

Because the information is passed across in the HTML form and not the URL, you can pass across far more information to the server than with the URL rewriting technique.

The hidden fields can contain special characters that don't have to be encoded unlike URL rewriting.

All information that is passed across to the server in the HTML form is not visible, so sensitive information such as passwords can be passed across, within the HTML form, to the server.

Hidden fields are not dependant upon storing information on the client's computer as for instance using cookies is, which is a session management technique we will look at in the next lesson.

Disadvantages

All pages within the application need to pass the hidden fields to the server which can be a hit to performance as well as development and maintenance overheads; so the more pages we need to pass the hidden fields to, the greater the hit and workload

Hidden Fields ExampleTop

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

Within the bookstore 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:

bookstore directory structure

Coding The Book ClassTop

The Book Class 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 find information in the Java section at Encapsulation - Getters & Setters.

Cut and paste the following code into your text editor and save it in the   c:\_ServletsInt\bookstore\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\bookstore\src\model

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

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

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

compile Book class

Coding BookStoreServletTop

Ok with our model, the Book class coded and compiled we are now ready to code up a servlet that uses hidden fields as its session management technique. On deployment the init() routine will run and create 3 books objects for later use. After initialisation, when the BookStoreServlet 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 detemine whether to invoke the displayBookList() method, or the displayEditBookForm() method.

When the form is submitted we use action='updateBook' attribute along with method='post' attribute so that the /updateBook mapping in the DD below will send us to the doPost() method where we can action the HTML form.

Cut and paste the following code into your text editor and save it in the   c:\_ServletsInt\bookstore\src\controller directory as BookStoreServlet.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 model.Book;

public class BookStoreServlet extends HttpServlet {

    private static final long serialVersionUID = 871964L;
    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);
    }

    @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("/editBook")) {
            // display editable book form
            displayEditBookForm(req, resp);
        }
    }

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException { 
        int bookNum = 0;
        // Update a Book object
        String num = req.getParameter("num");
        
        if (num != null) { 
            // Retrieve and convert book number to int type
            try {
                bookNum = Integer.parseInt(num);
                Book book = getBook(bookNum);
                if (book != null) {
                    book.setTitle(req.getParameter("title"));
                    book.setRating(req.getParameter("rating"));
                }    
            } catch(NumberFormatException ex) {
            }
        }
        displayBookList(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='editBook?num=" + book.getNum() + "'>Edit Book</a>)</li>"); 
        }
        writer.println("</ul></body></html>");
    }

    public void displayEditBookForm(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 hidden field
            writer.println("<html><head><title>Edit Book</title></head>" + 
                "<body><h1>Edit Book:</h1><form action='updateBook' method='post'>" +
                "<fieldset style='background-color:silver;border:1px solid black;" +
                "padding:5px;text-align:left;width:220px;'>" +
                "<legend>Edit Book Details</legend>");
            writer.println("<input type=hidden name='num' value='" + bookNum + "'/>");    
            writer.println("<table><tr><td>Title:</td><td><input name='title' value='" + 
                 book.getTitle() + "'/></td></tr>"); 
            writer.println("<tr><td>Rating:</td><td><input name='rating' value='" + 
                 book.getRating() + "'/></td></tr>"); 
            writer.println("<tr><td><input type='submit' value='Update Book'/></td><td></td></tr>"); 
            writer.println("<tr><td><a href='book'>Book List</a></td><td></td></tr>"); 
            writer.println("</table></form></body></html>");
        } else {
            writer.println("Sorry, the requested book was not found.");
        }
    }

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

Compiling BookStoreServletTop

Open your command line editor:

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

Compile BookStoreServlet.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 BookStoreServlet.java

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

compile BookStoreServlet class

Coding The DDTop

There are no DD entries for this application that we haven't seen before but there is a slight difference in this DD than in those we have seen before. Notice how within the <servlet-mapping> top-level element that there are three <url-pattern> sub-level elements which all map to the BookStoreServlet servlet. This is why we do the check within the doGet() method using the HttpServletRequest method encodeURI(). This method will get the URI and we then use the String method endsWith() to detemine whether to invoke the displayBookList() method, or the displayEditBookForm() method. When the form is submitted we use action='updateBook' attribute along with method='post' attribute so that the /updateBook mapping will send us to the doPost() method where we can action the HTML form.


<?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>bookStore</servlet-name>
      <servlet-class>controller.BookStoreServlet</servlet-class>
   </servlet>
   <servlet-mapping>
      <servlet-name>bookStore</servlet-name>
      <url-pattern>/book</url-pattern>
      <url-pattern>/editBook</url-pattern>
      <url-pattern>/updateBook</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 bookstore

Within the bookstore folder create the WEB-INF folder.

Copy the web.xml file and the classes directories 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\bookstore folder should look something like the following screenshot:

Tomcat directory structure2
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/bookstore/book

The web browser should be directed to the BookStoreServlet servlet class within Tomcat and execute it. On initial entry to BookStoreServlet the doGet() will be invoked and we use the HttpServletRequest method encodeURI() to get the URI and then use the String method endsWith(), to detemine whether to invoke the displayBookList() method, or the displayEditBookForm() method. Because of what we entered on the address line the displayBookList() method will run and produce a screen that looks like the following:

run BookStoreServlet class

The following screenshot shows the screen after we clicked on the hyperlink for The Railway Children.

Select 2 BookStoreServlet class

This last screenshot shows the screen after we changed the rating to Classic and hit the Update Book button.

Updated BookStoreServlet List

The changes we made appear in the book list and you can make further changes to make sure things get updated. Of course we are not persisting anything to a database as we would in the real world so if you redeploy the application then all changes will be lost and the list recreated via the init() method. Another point is there is no synchronisation for updating book details as there would need to be if this were a real application.

Lesson 2 Complete

In this lesson we looked at two session management techniques, URL rewriting and hidden fields, that can be used when the application doesn't contain lots of pages.

What's Next?

In the next lesson we look at the use of cookies as a session management technique.

go to home page Homepage go to top of page Top