Chapter 5. Saving Data Between Requests

In this chapter

Web programmers often need to keep track of data between user requests. Given what you already know about JSP, servlets, and HTML forms, you already have the ability to keep track of data between requests. You can store user data in hidden form variables.

Storing Data in Hidden Form Variables

Although not the most elegant solution, form variables are used by many applications to store user data. The idea is that every time the user submits a form, it contains some hidden variables that contain information about the user. These variables might be a login ID and password or the user's name. The big hassle with this method is that all your forms must preserve these hidden variables and continue to pass them around.

The best way to illustrate this technique is with an example. Listing 5.1 shows a login form that calls a JSP to handle the login.

Example 5.1. Source Code for Login.html

<HTML>
<BODY bgcolor="#ffffff">
<H1>Login</H1>
Please log in

<FORM action="Login.jsp" method="POST">

<TABLE>
<TR><TD>User Name:<TD><INPUT type="text" name="username">
<TR><TD>Password:<TD><INPUT type="password" name="password">
</TABLE>
<P>
<INPUT type="submit" value="Login!">
</FORM>
</BODY>
</HTML>

The goal of this example is to preserve the username across multiple pages. Obviously the Login.jsp page will have the username because it's passed as a form variable from Login.html. Listing 5.2 shows Login.jsp. Notice that it inserts the username as a hidden form variable.

Example 5.2. Source Code for Login.jsp

<HTML>
<BODY bgcolor="#ffffff">

<%
// Get the login information
    String userName = request.getParameter("username");
    String password = request.getParameter("password");

%>
Welcome, <%=userName%>!
<FORM action="/servlet/usingjsp.ColorServlet" method="POST">

<%-- Save the username in a hidden form variable --%>
<INPUT type="hidden" name="username" value="<%=userName%>">

<P>
Please enter your favorite color:
<SELECT name="color">
    <OPTION value="blue" SELECTED>Blue</OPTION>
    <OPTION value="red">Red</OPTION>
    <OPTION value="green">Green</OPTION>
    <OPTION value="yellow">Yellow</OPTION>
    <OPTION value="mauve">Mauve</OPTION>
</SELECT>
<P>
<INPUT type="submit" value="Choose color!">
</FORM>
</BODY>
</HTML>
					

Figure 5.1 shows the output from Login.jsp.

A page can contain hidden form variables that the user can't see.

Figure 5.1. A page can contain hidden form variables that the user can't see.

Just so you don't think servlets have been cut out of the loop here, the third part of this example is a servlet that receives the hidden form variable along with the user's color choice. Listing 5.3 shows the servlet.

Example 5.3. Source Code for ColorServlet.java

package usingjsp;

import javax.servlet.*;
import java.io.*;

public class ColorServlet extends GenericServlet
{ 
    public void service(ServletRequest request,
        ServletResponse response)
        throws IOException
    { 
// Tell the Web server that the response is HTML
        response.setContentType("text/html");

// Get the PrintWriter for writing out the response
        PrintWriter out = response.getWriter();

// Fetch the username and color parameters
String userName = request.getParameter("username");
        String color = request.getParameter("color");

// Write the HTML back to the browser
        out.println("<HTML>");
        out.println("<BODY bgcolor= "#ffffff">");
        out.println("Well, I see that "+userName+
            "'s favorite color is "+color+".");
        out.println("</BODY>");
        out.println("</HTML>");
    }
}

Security Concerns with Hidden Variables

One of the reasons you don't see hidden variables used very often in commercial Web sites, especially e-commerce sites, is that they are inherently insecure. Suppose Login.jsp actually verified the username and password. You would assume that the username passed to ColorServlet is valid. There is nothing to stop someone from inserting their own username and even a bizarre color by passing it in the URL for the servlet. Figure 5.2 shows an example of how the system can be tricked. Notice that the phony username and color are embedded into the URL.

A malicious user can send phony values for hidden form variables.

Figure 5.2. A malicious user can send phony values for hidden form variables.

Changing a username is bad enough, but imagine what could happen if someone did this with a bank account or credit card number! Certainly this technique needs some work. You could get clever and choose a variable name that is far less obvious than "username". Suppose, for example, that you changed the name of the hidden variable to "xpq7564HHgk". Surely no one would guess the variable name! Unfortunately, all someone needs to do is ask the browser to display the source for the page, as shown in Figure 5.3.

Because a user can view the source to a page, you can't safely hide anything in the page.

Figure 5.3. Because a user can view the source to a page, you can't safely hide anything in the page.

Another big concern with hidden form variables is that the user might accidentally bookmark the secure information. When you submit a form using an HTTP GET method (setting method="get" in your <form> tag), the form variables all appear in the URL and will be part of a bookmark if the user bookmarks the page. Even hidden form variables show up in the URL. If you use hidden form variables, even storing a single value, make sure you use an HTTP POST to keep the hidden information from being bookmarked.

Now, suppose you decide to store information on the server instead of trying to save all the data in hidden form variables. When a request comes in from a browser, how will you know what information goes with which client browser? To solve this problem, the browser must identify itself somehow.

You need to store one piece of data on the client: a key that uniquely identifies the browser and helps you remember where the user's data is stored. In other words, you get some data from the client, you store it in a hash table using some random key, and then send that key back to the browser in a hidden field. The next time the user fills out a form, the browser sends the server the hidden key field along with the other form variables. Your code on the server takes that key, looks in the hash table, and pulls out the user's data.

Storing data this way is much more secure than putting the data straight into hidden fields. Now if someone wants to break the system and tamper with the data, the best he can do is change the key. If the key value is large enough, it's unlikely that the hacker will be able to guess someone else's random key. He certainly won't be able to send you phony data, because the data stays on the server.

Listing 5.4 shows you a Java class that implements the key generation and data storage for keeping track of user data.

Example 5.4. Source Code for UserDataTable.java

package usingjsp;

import java.util.*;

public class UserDataTable
{ 
    protected static Hashtable userData = new Hashtable();
    protected static Random keyGenerator = new Random();
/** Creates a new table for a user's data and returns the key for
 *  the user's data.
 */
    public static String createUserData()
    { 
// Create a random key for the user's data
        String userKey = ""+keyGenerator.nextLong();

// Create a hash table for the user's data and store it
        userData.put(userKey, new Hashtable());

        return userKey;
    } 

/** Returns the user data table for a particular user */
    public static Hashtable getUserData(String userKey)
    { 
        if (userKey == null) return null;
        return (Hashtable) userData.get(userKey);
    } 

/** Destroys the user data table for a particular user */
    public static void clearUserData(String userKey)
    { 
        if (userKey == null) return;
        userData.remove(userKey);
    }
}
						

To make use of this new UserDataTable class, you need to change the Login.jsp file to store the username in the user data table instead of a hidden frame. Listing 5.5 shows how Login.jsp can support this new storage method with very few changes.

Example 5.5. Source Code for Login2.jsp

<%@ page language="java" import="java.util.*,usingjsp.*" %>

<HTML>
<BODY bgcolor="#ffffff">

<%
// Get the login information
    String userName = request.getParameter("username");
    String password = request.getParameter("password");

    String userKey = UserDataTable.createUserData();

    Hashtable userData = UserDataTable.getUserData(userKey);
    
    userData.put("username", userName);
%>
Welcome, <%=userName%>!
<FORM action="/servlet/usingjsp.ColorServlet2" method="POST">

<%-- Save the username in a hidden form variable --%>
<INPUT type="hidden" name="userkey" value="<%=userKey%>">

<P>
Please enter your favorite color:
<SELECT name="color">
    <OPTION value="blue" SELECTED>Blue</OPTION>
    <OPTION value="red">Red</OPTION>
    <OPTION value="green">Green</OPTION>
    <OPTION value="yellow">Yellow</OPTION>
    <OPTION value="mauve">Mauve</OPTION>
</SELECT>
<P>
<INPUT type="submit" value="Choose color!">
</FORM>
</BODY>
</HTML>
						

The main difference between Login.jsp and Login2.jsp is that Login2.jsp creates a user data table and stores the key for that table in a hidden field. Notice that Login2.jsp does not send the username back to the browser in a hidden field. All you need now is to modify the ColorServlet program to fetch the username from the user data table instead of from the hidden field. Listing 5.6 shows the modified ColorServlet that uses the user data table.

Example 5.6. Source Code for ColorServlet2.java

package usingjsp;


import javax.servlet.*;
import java.io.*;
import java.util.*;

public class ColorServlet2 extends GenericServlet
{ 
    public void service(ServletRequest request,
        ServletResponse response)
        throws IOException
    { 
// Tell the Web server that the response is HTML
response.setContentType("text/html");

// Get the PrintWriter for writing out the response
        PrintWriter out = response.getWriter();

// Fetch the user key and color parameters
        String userKey = request.getParameter("userkey");
        String color = request.getParameter("color");

        Hashtable userData = UserDataTable.getUserData(userKey);

// If the user data table isn't found, someone probably monkeyed with
// the key value.
        if (userData == null)
        { 
            out.println("<HTML><BODY>");
            out.println("<H1>Sorry</H1>");
            out.println("The system is experiencing problems.");
            out.println("</BODY></HTML>");
            return;
        } 

// Now try to get the username from the table
String userName = (String) userData.get("username");

// Write the HTML back to the browser
        out.println("<HTML>");
        out.println("<BODY bgcolor= "#ffffff">");
        out.println("Well, I see that "+userName+
            "'s favorite color is "+color+".");
        out.println("</BODY>");
        out.println("</HTML>");
    }
}
						

This method of storing data makes one assumption that you can't overlook: The servlet and Java Server Page must be running within the same Java Virtual Machine (JVM). The key to preserving the data is that the UserDataTable class uses static data. If the servlet runs in one JVM while the Java Server Page runs in another JVM, they will be using separate copies of the UserDataTable class, and thus will not share any data. If you ever decide to use a technique like this, make sure that everything is running within the same JVM. Most servlet and JSP engines on the market today run within a single JVM.

The hidden form variable approach seems to work fairly well except for one thing: the hidden variable itself. First of all, putting the hidden variable in every form is a hassle. Second, it requires the browser to use forms only as a method of accessing server-side pages. If you use a hyperlink, there are no form variables to pass. You must either rewrite your hyperlinks to include something like "?userkey=9345837479" or find a better alternative. That alternative is the session object.

Storing Data in a session Object

One thing you need to realize when dealing with the Web is that there is no permanent connection between the browser and the Web server. If you have done database programming or socket programming, you are familiar with the concept of a session: an active connection between two participants.

The HTTP protocol used by the browser and the Web server is not session-oriented. When the browser needs a page from the Web server, it opens a connection, retrieves the page, and then closes the connection. Because there is no active connection, the Web server has no idea what is happening on the browser. The browser could crash or the entire client computer could be turned off, and the Web server would be oblivious.

That being said, servlets and Java Server Pages do have a notion of a session. Near the end of the discussion on hidden form variables, you saw a technique in which data is stored on the server, referenced by a single key that was stored on the client. Servlets and Java Server Pages use a very similar technique, which is implemented by the HttpSession object.

The beauty of the HttpSession object is that it does not rely on hidden form variables and works even if a servlet or JSP is accessed via a hyperlink. The reason HttpSession is able to work without hidden form variables is that it uses something called a cookie to store the user's session key. A cookie contains a piece of data that the server sends to the browser and that the browser sends back to the server with each request. You will learn much more about cookies in Chapter 8, "More About Saving Data."

→ To learn more about cookies, see "Storing Data in a Cookie,".

Using the session Object in a JSP

The Java Server Pages API has several built-in objects. You have already seen two of them: request and out. The next important one is called session, and it's an instance of HttpSession. The three methods that you use the most in the session object are getAttribute, setAttribute, and removeAttribute. The declarations for these methods are

public void setAttribute(String name, Object value)
    throws IllegalStateException
public Object getAttribute(String name)
    throws IllegalStateException
public void removeAttribute(String name, Object value)
    throws IllegalStateException

These methods act much like the get and put methods in the Hashtable class. That is, setAttribute associates a name with a value, and getAttribute returns the value associated with a name or null if there is no value associated. For example, to store some data in a session, you would do something like this:

session.setAttribute("someKey", "here is my data");

To retrieve the data back out of the session, you would do something like this:

String myData = (String) session.getAttribute("someKey");

Note

The getAttribute, setAttribute, and removeAttribute methods were added to the Servlet API in version 2.2. Prior to version 2.2, these methods were called getValue, putValue, and removeValue (with the same parameters). Although getValue, putValue, and removeValue are supported under version 2.2 of the Servlet API, they are deprecated. Only use them if your servlets must run under Servlet API version 2.1 or earlier.

IllegalStateException is thrown when you try to get or set an attribute on an invalid session. A session becomes invalid either when you call its invalidate method or after the session has timed out. The servlet engine keeps track of how long it has been since a session has been accessed; after a certain period of inactivity, the session is marked as invalid. You can configure the amount of time it takes to time out a session, either on a per-session basis or for all sessions.

Note

The Servlet API specifies a way for you to control the timeout period on a per-session basis. Most servlet engines also provide a way for you to specify a default timeout length, but they are not required to by the Servlet API.

Listing 5.7 shows the Login.jsp page modified to support the session object instead of the UserDataTable class. Notice that it no longer needs to use the hidden form variable.

Example 5.7. Source Code for Login3.jsp

<%@ page language="java" import="java.util.*,usingjsp.*" %>

<HTML>
<BODY bgcolor="#ffffff">

<%
// Get the login information
    String userName = request.getParameter("username");
    String password = request.getParameter("password");

// Store the username in the session
session.setAttribute("username", userName);
%>
Welcome, <%=userName%>!
<FORM action="/servlet/usingjsp.ColorServlet3" method="POST">

<P>
Please enter your favorite color:
<SELECT name="color">
    <OPTION value="blue" SELECTED>Blue</OPTION>
    <OPTION value="red">Red</OPTION>
    <OPTION value="green">Green</OPTION>
    <OPTION value="yellow">Yellow</OPTION>
    <OPTION value="mauve">Mauve</OPTION>
</SELECT>
<P>
<INPUT type="submit" value="Choose color!">
</FORM>
</BODY>
</HTML>

Source Code for Login3.jspJSP (Java Server Pages)objectssessionsession object (JSP)objectsJSPsessionlistingsLogin3.jsp If you are having trouble storing or retrieving your session information, see "Storing and Retrieving Data," in the "Troubleshooting" section at the end of this chapter.

Using the session Object in a Servlet

You have probably guessed this already, but the session object you use in a servlet is identical to the one you use in a Java Server Page. The only difference is that it isn't already conveniently sitting around in a variable named session. Instead, you must get the session object from the requestobject.

Note

Java Server Pages also get the session object from the request object. The only reason you don't notice is that the JSP compiler automatically generates code to fetch the session object and put it in a variable named session.

To get the session object from the request object, just call the getSession method:

HttpSession session = request.getSession();

Inside a servlet, you use the same getAttribute and setAttribute methods to update session variables as you do in a Java Server Page. After all, the session object is an instance of HttpSession in both a servlet and a JSP.

Caution

Because the servlet engine needs to send a session cookie back to the browser, make sure you get the session object before you start sending a response back to the browser. Otherwise, it might be too late for the servlet engine to send back the cookie, because the cookie must be sent back in the header portion of the response. In a JSP, the session object is usually available immediately. You really only need to worry about this inside a servlet.

Listing 5.8 shows the modifications necessary to ColorServlet to make it use HttpSession instead of the UserDataClass. Also notice that unlike the previous versions of ColorServlet, this one is a subclass of HttpServlet (the others were subclasses of GenericServlet). The method signature on the service method is a little different now, too, because the request and response parameters are now instances of HttpServletRequest and HttpServletResponse.

Example 5.8. Source Code for ColorServlet3.java

package usingjsp;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;

public class ColorServlet3 extends HttpServlet
{ 
    public void service(HttpServletRequest request,
        HttpServletResponse response)
        throws IOException
    { 
// Tell the Web server that the response is HTML
response.setContentType("text/html");

// Get the PrintWriter for writing out the response
        PrintWriter out = response.getWriter();

// Fetch the color parameter
        String color = request.getParameter("color");

// Get the username from the session
HttpSession session = request.getSession();

        String userName = (String) session.getAttribute("username");

// Write the HTML back to the browser
        out.println("<HTML>");
        out.println("<BODY bgcolor= "#ffffff">");
        out.println("Well, I see that "+userName+
            "'s favorite color is "+color+".");
        out.println("</BODY>");
        out.println("</HTML>");
    }
}

Note

Because the HttpSession object relies specifically on features of the HTTP protocol, you can only use it in servlets that are subclasses of HttpServlet.

How Sessions Work

Now that you see that servlets and Java Server Pages can support sessions, you can take a step back and look at how the sessions work. When the servlet engine creates a session, it sends a session identifier (also referred to as a session key earlier in this chapter) back to the browser in the form of a cookie. Again, the cookie is just a piece of information that the browser sends back to the server whenever it asks the server for a page.

Usually, for a session, the cookie disappears when the Web browser is shut down. As you will see in Chapter 8, a browser can save cookies to disk, so when the browser starts up again it still knows about the cookies it had when it shut down. Because sessions are typically short-lived, and because shutting the browser down is an action that warrants the termination of a session, the session cookie is usually not saved to disk. Remember, the server has no idea when the Web browser shuts down. Figure 5.4 illustrates the interaction between the browser and the servlet engine as it relates to cookies and sessions.

The server sends the session identifier in a cookie, which the browser passes back.

Figure 5.4. The server sends the session identifier in a cookie, which the browser passes back.

Note

You must enable cookies in your browser for these examples to work.

When the browser asks the server for a page, the server looks at the session cookie, and then finds the session corresponding to that session identifier. Occasionally, the servlet engine looks through its sessions and gets rid of those that haven't been accessed in a long time. If it didn't do this, eventually the servlet engine would be wasting a lot of memory holding onto sessions that could never be accessed again because the cookies associated with those sessions are long gone (people shut down their browsers eventually, and that kills the session cookies).

Forcing a New Session

When you call the getSession method to retrieve the current session, the request object automatically creates a session if one doesn't already exist. In some JSP implementations, the session is created automatically even if you never use it. Most of the time, you don't really care when the session has been created. Other times, however, you need to explicitly reset the existing session and start over.

Suppose, for example, that you have implemented an online shopping site. A user logs on, visits a few pages, and selects several items to buy. You store these items in the user's session as they travel from page to page. Now, suppose the user decides that she doesn't want any of those items, and rather than go through the trouble of removing them from her shopping cart, she decides to just log in to your site again.

If a user comes back into your login page, you probably want to start her over with a clean slate. Although you could design a site that is smart enough to figure out what you were last doing and send you back to where you left off, most people assume that when they come in through the "front door," they are starting over.

Note

Users might go back to the login screen and walk away from the computer, thinking their order is now gone. Imagine their surprise if another user could walk up to the computer, log back in, and have the previous user's order, complete with credit card number.

The getSession method in the request object enables you to control the creation of new sessions. When you ask for a session, you can ask that the request object not create a new session if one doesn't already exist. The following segment of code automatically invalidates the previous session and then creates a new one:

// Get the old session, but don't create a session if
// one didn't already exist (passing true would allow
// creation of a new one).
    HttpSession oldSess = request.getSession(false);

// If there was an old session, invalidate it
    if (oldSess != null)
    { 
        oldSess.invalidate();
    } 

// Now create a fresh new session
    HttpSession session = request.getSession(true);

This code works for both JSP and servlets, except that for a JSP, you shouldn't re-declare a session. Instead, the last line should just read

     session = request.getSession(true);

Handling Session Termination

A session can be terminated in two ways: You force the termination by calling the invalidate method on the session, or the servlet engine times the session out. Depending on the kind of data you store in the session, you might need to perform some kind of cleanup of the session data. For example, you might have a database connection stored in the session, or a connection to an RMI or CORBA service on another machine. Although Java's garbage collector eventually eliminates these resources, you shouldn't keep them open any longer than you need to.

A session object has a callback mechanism to notify an object when it has been associated with a session and when it is no longer associated with a session. That is, when you call session.setAttribute("someName", someObject), the session object can notify the object that it is being associated with a session. When the session terminates, the session object can notify the object that it is no longer associated with the session.

This notification is on an object-by-object basis. Although it might seem strange at first, the notification technique is actually very flexible. You can write objects that are session-aware and can perform their own cleanup. If you are using standard objects, such as a JDBC Connection object, you can create a special session cleanup object that releases your database connection.

The HttpSessionBindingListener Interface

The HttpSessionBindingListener interface defines notification methods that the session object uses to notify objects when they are added to or removed from a session. There are two methods in the interface:

public void valueBound(HttpSessionBindingEvent event);
public void valueUnbound(HttpSessionBindingEvent event);

As you might have guessed, valueBound is called when an object is added to a session; valueUnbound is called when the object is removed from a session. Listing 5.9 shows an example class that listens for valueBound and valueUnbound messages and counts the number of sessions that are bound to it.

Example 5.9. Source Code for BindListener.java

package usingjsp; 

import javax.servlet.http.*;

/** Counts the number of sessions that are bound to this object. */

public class BindListener implements HttpSessionBindingListener
{ 
// The current session count
    protected int numSessions;

    public BindListener()
    { 
        numSessions = 0;
    } 

// Every time this object is added to a session,
// valueBound is called
    public synchronized void valueBound(HttpSessionBindingEvent event)
    { 
        numSessions++;
    } 

// Every time this object is removed from a session,
// valueUnbound is called
    public synchronized void valueUnbound(HttpSessionBindingEvent event)
    { 
        numSessions--;
    } 

// Returns the current number of bound sessions
    public int getNumSessions()
    {
        return numSessions;
    }
}
						

To test the BindListener class, you need to observe what happens when you access it from multiple sessions and also see what happens when you invalidate a session containing a BindListener object. You should expect to see the session count go up whenever the object is added to a session, and you should see the count go down when the object is removed from a session, or when the session it belongs to is invalidated.

Listing 5.10 shows a test harness JSP that exercises the BindListener class. By selecting various hyperlinks, you can remove the BindListener object from the session or invalidate the session.

Example 5.10. Source Code for BindTest.jsp

<%@ page language="java" import="usingjsp.BindListener" %>

<HTML>
<BODY bgcolor="#ffffff">

<%-- Set up a static BindListener shared by all instances of this JSP.

     There is probably only one instance, but just in case the server creates

     multiple instances, this page can handle it. --%>
<%!
    protected static BindListener listener = new BindListener();
%>

<%

    BindListener l = null;

// Allow the browser to pass a "removeListener" parameter to remove
// a listener from the session

    if (request.getParameter("removeListener") != null)
    { 
        session.removeAttribute("listener");
    } 

// Allow the browser to pass a "resetSession parameter to clear out
// the session
    else if (request.getParameter("resetSession") != null)
    { 
// See if there is already a session
        HttpSession oldSession = request.getSession(false);

// If there was already a session, invalidate
        if (oldSession != null)
        { 
            l = (BindListener)
                oldSession.getAttribute("listener");
            oldSession.invalidate();
// Tell the user that the session was reset and show that the
// bind counts have been updated. Make sure that there was a
// listener on the old session, too.

            if (l != null)
           { 
%>
Your current session was reset. The listener now has <%=l.getNumSessions()%>
active sessions.<P>
<%
            }  else { 
%>
Your old session didn't have a listener.<P>
<%
            } 

            l = null;

        }
    }
    else
    { 
// See if the listener is already in the session
        l = (BindListener)
            session.getAttribute("listener");

// If not, add the global copy of the listener to the session
        if (l == null)
        { 
// Put the global listener variable into the session
            session.setAttribute("listener", listener);
            l = listener;
        } 
    } 
%>
<%
    if (l != null)
    { 
%>
You have a listener bound to your session.
<%
    }  else { 
%>
You do not have a listener bound to your session.
<%
    } 
%>
There are currently <%=listener.getNumSessions()%> sessions holding onto the
bind listener.
<P>
<TABLE>
<TR>
<TD>
<A href="BindTest.jsp">Refresh Form</A>
<TD>
<A href="BindTest.jsp?removeListener">Remove Listener</A>
<TD>
<A href="BindTest.jsp?resetSession">Reset Session</A>
</TABLE>
</BODY>
</HTML>
						

Figure 5.5 shows several browser sessions running BindTest.jsp.

The BindListener object keeps track of how many sessions it belongs to.

Figure 5.5. The BindListener object keeps track of how many sessions it belongs to.

Handling Sessions Without Cookies

Normally, JSP and servlet sessions rely on the HTTP cookie mechanism to preserve the session identifier between requests. Cookies are really nice for doing things such as sessions and online ordering. Unfortunately, cookies have also been abused. Many Web sites store personal information in cookies, and many Web users don't like their personal information being sent to another Web server without their knowledge. To put it simply, cookie abuse has given cookies a bad name.

Many users now disable cookies within their browsers. You might think that with cookies disabled, the only way to keep track of session information would be the hidden field technique discussed at the beginning of this chapter. Fortunately, there is another solution.

If you knew the session ID, you could pass it as a parameter to all your servlets and Java Server Pages. The HttpSession object contains a getId method, so you could pass it around. Now all you need is a way to take a session ID and find the session with that ID. In version 2.1 of the Servlet API, there was a way to locate a session by ID (the getSession method in the HttpSessionContext object). Unfortunately, the HttpSessionContext class has been deprecated for version 2.2 of the Servlet API, meaning the class might be removed from future versions of the Servlet API.

You don't need to go through the trouble of tracking the session ID, however. The Servlet API provides a way for you to insert a session ID into a URL. The idea is, for every URL in your Web application that refers to a servlet or a JSP, you insert the session ID as a parameter to that servlet or JSP. Because the session ID is normally stored in a cookie, you only need pass the session ID as a parameter when cookies are disabled.

Note

Many Web sites that do session-oriented work require you to enable cookies. Although it's nice to be able to support sessions without cookies, users generally find them acceptable for applications such as online shopping. If you decide to require cookies, you might need to put a note on your Web site explaining the necessity of cookies.

The HttpServletResponse object (the response object in a JSP) contains two methods to help you pass the session ID around to different pages:

public String encodeURL(String url);
public String encodeRedirectURL(String url);

If you need to do session tracking but the browser doesn't support cookies, encodeURL and encodeRedirectURL return a modified URL containing the session ID as a parameter for that URL. If the browser supports cookies, the URL is returned unmodified. Listing 5.11 shows a JSP that presents a form, handles the submission of the form, and puts the form results into a session. It calls encodeURL and encodeRedirectURL to make sure that sessions are supported even with cookies turned off.

Example 5.11. Source Code for RewriteDemo.jsp

<HTML>
<BODY>

<H1>URL Rewriting Demo</H1>

<%-- See if the session already contains the name. 
    If so, say "Hello" to the user --%>

<%
    String name = (String) session.getAttribute("name");


    if (name != null)
    { 
// This user already has a session, show the name and show the list of
// items they have entered

        out.println("Hello, "+name+"!");
%>

        <A href="<%=response.encodeURL("RewriteDemo2.jsp")%>">
            Click here to continue</A>
<%

    }
// If name is passed in as a parameter, it must be as a response to
// the form input. Put it in the session and redirect it to the second
// page.
    else if (request.getParameter("name") != null)
    {
        session.setAttribute("name",
           request.getParameter("name"));
        response.sendRedirect(response.encodeRedirectURL(
            "RewriteDemo2.jsp"));
    } 
    else
    { 
%>
<FORM ACTION="<%=response.encodeURL("RewriteDemo.jsp")%>">
Please enter your name: <INPUT type=text name="name">
<P>
<INPUT type="submit" value="Login!">
</FORM>
<%
    } 
%>

</BODY>
</HTML>
					

Figure 5.6 shows RewriteDemo2.jsp, which RewriteDemo.jsp redirects the user to. Notice that the address line contains an embedded session ID. The session ID appears because the browser in this situation has cookies turned off.

The session ID can be embedded in a URL.

Figure 5.6. The session ID can be embedded in a URL.

The session ID can be embedded in a URL. If you are having trouble with URL rewriting, see "URL Rewriting" in the "Troubleshooting" section at the end of this chapter.

Disabling Cookies in Internet Explorer

To disable cookies in Internet Explorer, you must change the security level for your Internet zone. Internet explorer groups Web sites into four zones: Local Intranet, Internet, Trusted Sites, and Restricted Sites. To change the security settings for a zone, go to the IE menu and select Tools-Internet Options and then click the security tab. You should see a dialog box like the one shown in Figure 5.7.

To change the cookie settings, go to the Security tab in the Internet Options.

Figure 5.7. To change the cookie settings, go to the Security tab in the Internet Options.

You can change the setting on each zone to a different level (high, medium, medium-low, and low). The high setting disables cookies. If you are testing with the Web site on your own machine, you must change the setting for the Local Intranet zone. If you are testing with another machine, you will probably need to change the setting for the Internet zone. Just move the slider on the security dialog box up to high.

Disabling Cookies in Netscape

TO disable cookies in Netscape, click the Edit menu and select Preferences. Then, select Advanced in the dialog box. You should see a dialog box similar to the one shown in Figure 5.8. Click the Do Not Accept or Send Cookies button.

To change the cookie settings in Netscape, go to the Preferences dialog box and select Advanced.

Figure 5.8. To change the cookie settings in Netscape, go to the Preferences dialog box and select Advanced.

To change the cookie settings in Netscape, go to the Preferences dialog box and select Advanced. If you are having trouble disabling or enabling cookies in your browser, see "Cookie Settings" in the "Troubleshooting" section at the end of this chapter.

Unfortunately, to make full use of URL rewriting, you must pass all your pages through the URL rewriting process. In other words, if you have a static HTML page with links to Java Server Pages or servlets that needs session information, you must turn these static HTML pages into Java Server Pages that use encodeURL to rewrite the HREF values for all the hyperlinks. So, in your HTML file where you have a line like

<a href="CallMe.jsp">

the JSP file would read

<a href="<%=response.encodeURL("CallMe.jsp")%>">

You also need to change the action attributes in each of your <FORM> tags. A <FORM> tag with an action of "HandleForm.jsp" appears in the JSP like this:

<form action="<%=response.encodeURL("HandleForm.jsp")%>">

Note

Modifying your Web site to rewrite all your forms and hyperlinks is a difficult task. Try to design your site so that you can minimize the amount of rewriting necessary.

Storing Application-Wide Data

The HttpSession class stores data items on a per-user basis. Sometimes, however, you have data that you need to share between various servlets and Java Server Pages that doesn't need to be stored for each user. For example, if you are writing a database application, you might need to share a database connection. From a Java Server Page, you can store data in the application object. The methods for storing data in the application object are identical to the ones for the session object:

public void setAttribute(String name, Object value)
public Object getAttribute(String name)
public void removeAttribute(String name, Object value)

From a JSP, if you want to store information in the application object with a name of myInformation, you make a call like this:

application.setAttribute("myInformation", "Here is the info");

To get the information back out of the application object, you call getAttribute:

String theInfo = (String) application.getAttribute("myInformation");

The application object is really an object that implements the ServletContext interface. The servlet context is also available from within the servlet. If your servlet is a subclass of GenericServlet or HttpServlet, as most are, you can call the getServletContext method:

ServletContext context = getServletContext();

context.setAttribute("myInformation", "Here is the info");

Remember that your servlet doesn't have to be a subclass of GenericServlet or HttpServlet. You could choose to write your own class that implements the Servlet interface. If you need to get hold of the servlet context in these cases, the context is contained in the ServletConfig object that is passed to your servlet's init method. You can always call getServletConfig().getServletContext() to get the servlet context.

Why Do You Need the application Object?

From all appearances, the application object seems like overkill. What is the difference between storing something in the application object and storing it in a static variable somewhere? You will see in Chapter 28, "Packaging a JSP Application," that it's possible to group a set of Java Server Pages and servlets into an application. The servlet engine knows what application a particular JSP or servlet belongs to. You could, for example, have a set of Java Server Pages deployed in a server under an application called "QA" and an identical set of JSPs under an application called "Beta" . These two applications, although running in the same server and the same Java Virtual Machine, would have different application objects (that is, different ServletContext objects).

Now you can see how this differs from a static variable. If you tried to store a data item in a static variable, you would circumvent the notion of separate applications. There is only one copy of a static variable within a single Java Virtual Machine. You can't say that the "QA" application gets its own copy of a static variable and the "Beta" application gets another unless you are somehow able to run each application in a separate virtual machine. Because you can't count on a servlet engine to support multiple JVMs, you shouldn't rely on static variables for application-level data sharing.

Troubleshooting

Storing and Retrieving Data

Q1:

I stored data in a session; why can't I find it in the session object?

A1:

There are many possibilities for this. The most common problem is that you mistyped the name of the item you want to retrieve. You might have stored it as name and you are trying to retrieve it as Name. If possible, create a Java class that contains a number of public static final string constants defining the names of the items you want to store in the session, and use these constants instead of hard-coding strings into the servlets and JSPs. Another possibility for this problem is that the browser has cookies disabled. Make sure you have cookies enabled or that you use URL rewriting to pass the session ID along.

Q2:

Why do I get a compile error when I retrieve an item from a session?

A2:

The session.getAttribute method returns items of type Object. Even if you store a String object or another type, the return value is always Object. You must cast the result to the appropriate type when you retrieve it from the session.

Cookie Settings

Q1:

I turned off cookies in Internet Explorer; why don't I see the session ID when I use URL rewriting?

A1:

Make sure you have turned off cookies for the security zone you are using. If you are accessing your local machine, you need to change the security setting for the Local Intranet zone, at least if you use http://localhost as the first part of your URL. Internet Explorer will not always notice that a URL is really part of the local intranet, however. It may consider http://localhost as being in the local domain, and think that http://zinger.wutka.com is in the Internet zone, even though it is the same machine as localhost. Try setting the security for the Internet and Local Intranet zones to be high.

URL Rewriting

Q1:

Why does the browser seem to lose the session ID while navigating through my site using URL rewriting?

A1:

The problem is most likely the presence of an HTML page somewhere along the way. In order for URL rewriting to work, you must rewrite the URL for every link the browser might access. You must convert any static HTML pages into Java Server Pages, changing the hyperlinks so they use the URL rewriting routines.

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

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