Internet Programming with Java Course

1.    The Servlet Life Cycle

The init Method

The init method is called when the servlet is first created and is not called again for each user request. So, it is used for one-time initializations, just as with the init method of applets. The servlet can be created when a user first invokes a URL corresponding to the servlet or when the server is first started, depending on how you have registered the servlet with the Web server. It will be created for the first user request if it is not explicitly registered but is instead just placed in one of the standard server directories. There are two versions of init: one that takes no arguments and one that takes a ServletConfig object as an argument. The first version is used when the servlet does not need to read any settings that vary from server to server. The method definition looks like this:

 

public void init() throws ServletException {

    // Initialization code...

}

 

The second version of init is used when the servlet needs to read server-specific settings before it can complete the initialization. For example, the servlet might need to know about database settings, password files, server-specific performance parameters, hit count files, or serialized cookie data from previous requests. The second version of init looks like this:

 

public void init(ServletConfig config)

throws ServletException {

    super.init(config);

    // Initialization code...

}

 

Notice two things about this code. First, the init method takes a Servlet- Config as an argument. ServletConfig has a getInitParameter method with which you can look up initialization parameters associated with the servlet. Just as with the getParameter method used in the init method of applets, both the input (the parameter name) and the output (the parameter value) are strings. Note that although you look up parameters in a portable manner, you set them in a server-specific way. For example, with Tomcat, you embed servlet properties in a file called web.xml, with the JSWDK you use servlets.properties, with the WebLogic application server you use weblogic.properties, and with the Java Web Server you set the properties interactively via the administration  The second thing to note about the second version of init is that the first line of the method body is a call to super.init. This call is critical! The ServletConfig object is used elsewhere in the servlet, and the init method of the superclass registers it where the servlet can find it later. So, you can cause yourself huge headaches later if you omit the super.init call.

The service Method

Each time the server receives a request for a servlet, the server spawns a new thread and calls service. The service method checks the HTTP request type (GET, POST, PUT, DELETE, etc.) and calls doGet, doPost, doPut, doDelete, etc., as appropriate. Now, if you have a servlet that needs to handle both POST and GET requests identically, you may be tempted to override service directly as below, rather than implementing both doGet and doPost.

 

public void service(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

    // Servlet Code

}

 

This is not a good idea. Instead, just have doPost call doGet (or vice versa), as below.

 

public void doGet(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

    // Servlet Code

}

 

public void doPost(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

    doGet(request, response);

}

 

Although this approach takes a couple of extra lines of code, it has five advantages over directly overriding service:

 

1. You can add support for other services later by adding doPut, doTrace, etc., perhaps in a subclass. Overriding service directly precludes this possibility.

2. You can add support for modification dates by adding a get- LastModified method. If you use doGet, the standard service method uses the getLastModified method to set Last-Modified headers and to respond properly to conditional GET requests (those containing an If-Modified-Since header).

3. You get automatic support for HEAD requests. The system just returns whatever headers and status codes doGet sets, but omits the page body. HEAD is a useful request method for custom HTTP clients. For example, link validators that check a page for dead hypertext links often use HEAD instead of GET in order to reduce server load.

4. You get automatic support for OPTIONS requests. If a doGet method exists, the standard service method answers OPTIONS requests by returning an Allow header indicating that GET, HEAD, OPTIONS, and TRACE are supported. 5. You get automatic support for TRACE requests. TRACE is a request method used for client debugging: it just returns the HTTP request headers back to the client.

The doGet, doPost, and doXxx Methods

These methods contain the real meat of your servlet. Ninety-nine percent of the time, you only care about GET and/or POST requests, so you override doGet and/or doPost. However, if you want to, you can also override doDelete for DELETE requests, doPut for PUT, doOptions for OPTIONS, and doTrace for TRACE. Recall, however, that you have automatic support for OPTIONS and TRACE, as described in the previous section on the service method. Note that there is no doHead method. That’s because the system automatically uses the status line and header settings of doGet to answer HEAD requests.

The SingleThreadModel Interface

Normally, the system makes a single instance of your servlet and then creates a new thread for each user request, with multiple simultaneous threads running if a new request comes in while a previous request is still executing. This means that your doGet and doPost methods must be careful to synchronize access to fields and other shared data, since multiple threads may be trying to access the data simultaneously. If you want to prevent this multithreaded access, you can have your servlet implement the SingleThreadModel interface, as below.

 

public class YourServlet extends HttpServlet

implements SingleThreadModel {

    ...

}

 

If you implement this interface, the system guarantees that there is never more than one request thread accessing a single instance of your servlet. It does so either by queuing up all the requests and passing them one at a time to a single servlet instance, or by creating a pool of multiple instances, each of which handles one request at a time. This means that you don’t have to worry about simultaneous access to regular fields (instance variables) of the servlet. You do, however, still have to synchronize access to class variables (static fields) or shared data stored outside the servlet. Synchronous access to your servlets can significantly hurt performance (latency) if your servlet is accessed extremely frequently. So think twice before using the SingleThreadModel approach.

The destroy Method

The server may decide to remove a previously loaded servlet instance, perhaps because it is explicitly asked to do so by the server administrator, or perhaps because the servlet is idle for a long time. Before it does, however, it calls the servlet’s destroy method. This method gives your servlet a chance to close database connections, halt background threads, write cookie lists or hit counts to disk, and perform other such cleanup activities. Be aware, however, that it is possible for the Web server to crash. After all, not all Web servers are written in reliable programming languages like Java; some are written in languages (such as ones named after letters of the alphabet) where it is easy to read or write off the ends of arrays, make illegal typecasts, or have dangling pointers due to memory reclamation errors. Besides, even Java technology won’t prevent someone from tripping over the power cable running to the computer. So, don’t count on destroy as the only mechanism for saving state to disk. Activities like hit counting or accumulating lists of cookie values that indicate special access should also proactively write their state to disk periodically.

 

2.    An Example Using Initialization Parameters

Listing 1 shows a servlet that reads the message and repeats initializationparameters when initialized. Remember that, although servlets read init parameters in a standard way, developers set init parameters in a server-specific manner. Please refer to your server documentation for authoritative details.

Because the process of setting init parameters is server-specific, it is a good idea to minimize the number of separate initialization entries that have to be specified. This will limit the work you need to do when moving servlets that use init parameters from one server to another. If you need to read a large amount of data, I recommend that the init parameter itself merely give the location of a parameter file, and that the real data go in that file.

 

Listing 1 - ShowMessage.java

 

package coreservlets;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

 

/** Example using servlet initialization. Here, the message

* to print and the number of times the message should be

* repeated is taken from the init parameters.

*/

public class ShowMessage extends HttpServlet {

    private String message;

    private String defaultMessage = "No message.";

    private int repeats = 1;

 

    public void init(ServletConfig config)

    throws ServletException {

        // Always call super.init

        super.init(config);

        message = config.getInitParameter("message");

        if (message == null) {

            message = defaultMessage;

        }

        try {

            String repeatString = config.getInitParameter("repeats");

            repeats = Integer.parseInt(repeatString);

        } catch(NumberFormatException nfe) {

            // NumberFormatException handles case where repeatString

            // is null *and* case where it is something in an

            // illegal format. Either way, do nothing in catch,

            // as the previous value (1) for the repeats field will

            // remain valid because the Integer.parseInt throws

            // the exception *before* the value gets assigned

            // to repeats.

        }

    }

 

    public void doGet(HttpServletRequest request,

    HttpServletResponse response)

    throws ServletException, IOException {

        response.setContentType("text/html");

        PrintWriter out = response.getWriter();

        String title = "The ShowMessage Servlet";

        out.println(ServletUtilities.headWithTitle(title) +

        "<BODY BGCOLOR=\"#FDF5E6\">\n" +

        "<H1 ALIGN=CENTER>" + title + "</H1>");

        for(int i=0; i<repeats; i++) {

            out.println(message + "<BR>");

        }

        out.println("</BODY></HTML>");

    }

}

 

Listing 2 shows the setup file used to supply initialization parameters to servlets used with Tomcat 3.0. The idea is that you first associate a name with the servlet class file, then associate initialization parameters with that name (not with the actual class file). The setup file is located in install_dir/webpages/WEB-INF.

 

Listing 3 shows the properties file used to supply initialization parameters to servlets in the JSWDK. As with Tomcat, you first associate a name with the servlet class, then associate the initialization parameters with the name. The properties file is located in install_dir/webpages/WEB-INF.

 

Listing 2 - web.xml (for Tomcat)

 

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app

PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application

2.2//EN"

"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">

<web-app>

    <servlet>

        <servlet-name>ShowMsg</servlet-name>

        <servlet-class>coreservlets.ShowMessage</servlet-class>

        <init-param>

            <param-name>message</param-name>

            <param-value>Shibboleth</param-value>

        </init-param>

        <init-param>

            <param-name>repeats</param-name>

            <param-value>5</param-value>

        </init-param>

    </servlet>

</web-app>

 

Listing 3 - servlets.properties

 

# servlets.properties used with the JSWDK

# Register servlet via servletName.code=servletClassFile

# You access it via http://host/examples/servlet/servletName

ShowMsg.code=coreservlets.ShowMessage

# Set init params via

# servletName.initparams=param1=val1,param2=val2,...

ShowMsg.initparams=message=Shibboleth,repeats=5

# Standard setting

jsp.code=com.sun.jsp.runtime.JspServlet

# Set this to keep servlet source code built from JSP

jsp.initparams=keepgenerated=true

 

3.    Sessions. The Need for Session Tracking

HTTP is a “stateless” protocol: each time a client retrieves a Web page, it opens a separate connection to the Web server, and the server does not automatically maintain contextual information about a client. Even with servers that support persistent (keep-alive) HTTP connections and keep a socket open for multiple client requests that occur close together in time, there is no built-in support for maintaining contextual information. This lack of context causes a number of difficulties. For example, when clients at an on-line store add an item to their shopping carts, how does the server know what’s already in them? Similarly, when clients decide to proceed to checkout, how can the server determine which previously created shopping carts are theirs? There are three typical solutions to this problem: cookies, URL-rewriting, and hidden form fields.

Cookies

You can use HTTP cookies to store information about a shopping session, and each subsequent connection can look up the current session and then extract information about that session from some location on the server machine. For example, a servlet could do something like the following:

 

String sessionID = makeUniqueString();

Hashtable sessionInfo = new Hashtable();

Hashtable globalTable = findTableStoringSessions();

globalTable.put(sessionID, sessionInfo);

Cookie sessionCookie = new Cookie("JSESSIONID", sessionID);

sessionCookie.setPath("/");

response.addCookie(sessionCookie);

 

Then, in later requests the server could use the globalTable hash table to associate a session ID from the JSESSIONID cookie with the sessionInfo hash table of data associated with that particular session. This is an excellent solution and is the most widely used approach for session handling. Still, it would be nice to have a higher-level API that handles some of these details. Even though servlets have a high-level and easy-to-use interface to cookies  a number of relatively tedious details still need to be handled in this case: • Extracting the cookie that stores the session identifier from the other cookies (there may be many cookies, after all) • Setting an appropriate expiration time for the cookie (sessions that are inactive for 24 hours probably should be reset)

• Associating the hash tables with each request

• Generating the unique session identifiers

Besides, due to real and perceived privacy concerns over cookies, some users disable them. So, it would be nice to have alternative implementation approaches in addition to a higher-level protocol.

URL-Rewriting

With this approach, the client appends some extra data on the end of each URL that identifies the session, and the server associates that identifier with data it has stored about that session. For example, with http://host/path/file.html;jsessionid=1234, the session information is attached as jsessionid=1234. This is also an excellent solution, and even has the advantage that it works when browsers don’t support cookies or when the user has disabled them. However, it has most of the same problems as cookies, namely, that the server-side program has a lot of straightforward but tedious processing to do. In addition, you have to be very careful that every URL that references your site and is returned to the user (even by indirect means like Location fields in server redirects) has the extra information appended. And, if the user leaves the session and comes back via a bookmark or link, the session information can be lost.

Hidden Form Fields

HTML forms can have an entry that looks like the following:

 

<INPUT TYPE="HIDDEN" NAME="session" VALUE="...">

 

This entry means that, when the form is submitted, the specified name and value are included in the GET or POST data. For details, see Section 16.9 (Hidden Fields). This hidden field can be used to store information about the session but it has the major disadvantage that it only works if every page is dynamically generated.

4.    Session Tracking in Servlets

Servlets provide an outstanding technical solution: the HttpSession API. This high-level interface is built on top of cookies or URL-rewriting. In fact, most servers use cookies if the browser supports them, but automatically revert to URL-rewriting when cookies are unsupported or explicitly disabled. But, the servlet author doesn’t need to bother with many of the details, doesn’t have to explicitly manipulate cookies or information appended to the URL, and is automatically given a convenient place to store arbitrary objects that are associated with each session.

5.    The Session Tracking API

Using sessions in servlets is straightforward and involves looking up the session object associated with the current request, creating a new session object when necessary, looking up information associated with a session, storing information in a session, and discarding completed or abandoned sessions. Finally, if you return any URLs to the clients that reference your site and URL-rewriting is being used, you need to attach the session information to the URLs.

Looking Up the HttpSession Object Associated with the Current Request

You look up the HttpSession object by calling the getSession method of HttpServletRequest. Behind the scenes, the system extracts a user ID from a cookie or attached URL data, then uses that as a key into a table of previously created HttpSession objects. But this is all done transparently to the programmer: you just call getSession. If getSession returns null, this means that the user is not already participating in a session, so you can create a new session. Creating a new session in this case is so commonly done that there is an option to automatically create a new session if one doesn’t already exist. Just pass true to getSession. Thus, your first step usually looks like this:

 

HttpSession session = request.getSession(true);

 

If you care whether the session existed previously or is newly created, you can use isNew to check.

Looking Up Information Associated with a Session

HttpSession objects live on the server; they’re just automatically associated with the client by a behind-the-scenes mechanism like cookies or URL-rewriting. These session objects have a built-in data structure that lets you store any number of keys and associated values. In version 2.1 and earlier of the servlet API, you use session.getValue("attribute") to look up a previously stored value. The return type is Object, so you have to do a typecast to whatever more specific type of data was associated with that attribute name in the session. The return value is null if there is no such attribute, so you need to check for null before calling methods on objects associated with sessions. In version 2.2 of the servlet API, getValue is deprecated in favor of get- Attribute because of the better naming match with setAttribute (in version 2.1 the match for getValue is putValue, not setValue). Nevertheless, since not all commercial servlet engines yet support version 2.2, I’ll use getValue in my examples. Here’s a representative example, assuming ShoppingCart is some class you’ve defined to store information on items being purchased

 

HttpSession session = request.getSession(true);

ShoppingCart cart = (ShoppingCart)session.getValue("shoppingCart");

if (cart == null) {

    // No cart already in session

    cart = new ShoppingCart();

    session.putValue("shoppingCart", cart);

}

doSomethingWith(cart);

 

In most cases, you have a specific attribute name in mind and want to find the value (if any) already associated with that name. However, you can also discover all the attribute names in a given session by calling getValueNames, which returns an array of strings. This method is your only option for finding attribute names in version 2.1, but in servlet engines supporting version 2.2 of the servlet specification, you can use getAttributeNames. That method is more consistent in that it returns an Enumeration, just like the getHeader- Names and getParameterNames methods of HttpServletRequest. Although the data that was explicitly associated with a session is the part you care most about, there are some other pieces of information that are sometimes useful as well. Here is a summary of the methods available in the HttpSession class.

 

public Object getValue(String name)

public Object getAttribute(String name)

 

These methods extract a previously stored value from a session object. They return null if there is no value associated with the given name. Use getValue in version 2.1 of the servlet API. Version 2.2 supports both methods, but getAttribute is preferred and getValue is deprecated.

 

public void putValue(String name, Object value)

public void setAttribute(String name, Object value)

 

These methods associate a value with a name. Use putValue with version 2.1 servlets and either setAttribute (preferred) or putValue (deprecated) with version 2.2 servlets. If the object supplied to putValue or setAttribute implements the HttpSessionBindingListener interface, the object’s valueBound method is called after it is stored in the session. Similarly, if the previous value implements HttpSessionBindingListener, its valueUnbound method is called.

 

public void removeValue(String name)

public void removeAttribute(String name)

 

These methods remove any values associated with the designated name.If the value being removed implements HttpSessionBindingListener, its valueUnbound method is called. With version 2.1 servlets,use removeValue. In version 2.2, removeAttribute is preferred, butremoveValue is still supported (albeit deprecated) for backward compatibility.

public String[] getValueNames()

public Enumeration getAttributeNames()

These methods return the names of all attributes in the session. Use getValueNames in version 2.1 of the servlet specification. In version 2.2, getValueNames is supported but deprecated; use getAttributeNames instead.

 

public String getId()

 

This method returns the unique identifier generated for each session. It is sometimes used as the key name when only a single value is associated with a session, or when information about sessions is being logged.

 

public boolean isNew()

 

This method returns true if the client (browser) has never seen the session, usually because it was just created rather than being referenced by an incoming client request. It returns false for preexisting sessions.

 

public long getCreationTime()

 

This method returns the time in milliseconds since midnight, January 1, 1970 (GMT) at which the session was first built. To get a value useful for printing out, pass the value to the Date constructor or the setTimeInMillis method of GregorianCalendar.

 

public long getLastAccessedTime()

 

This method returns the time in milliseconds since midnight, January 1, 1970 (GMT) at which the session was last sent from the client.

 

public int getMaxInactiveInterval()

public void setMaxInactiveInterval(int seconds)

 

These methods get or set the amount of time, in seconds, that a session should go without access before being automatically invalidated. A negative value indicates that the session should never time out. Note that the time out is maintained on the server and is not the same as the cookie expiration date, which is sent to the client.

 

public void invalidate()

 

This method invalidates the session and unbinds all objects associated with it.

Associating Information with a Session

As discussed in the previous section, you read information associated with asession by using getValue (in version 2.1 of the servlet specification) or getAttribute (in version 2.2 ). To specify information in version 2.1 servlets, you use putValue, supplying a key and a value. Use setAttribute in version 2.2. This is a more consistent name because it uses the get/set notation of JavaBeans. To let your values perform side effects when they are stored in a session, simply have the object you are associating with the session implement the HttpSessionBindingListener interface. Now, every time putValue or setAttribute is called on one of those objects, its valueBound method is called immediately afterward. Be aware that putValue and setAttribute replace any previous values; if you want to remove a value without supplying a replacement, use remove- Value in version 2.1 and removeAttribute in version 2.2. These methods trigger the valueUnbound method of any values that implement HttpSessionBindingListener. Sometimes you just want to replace previous values; see the referringPage entry in the example below for an example. Other times, you want to retrieve a previous value and augment it; for an example, see the previousItems entry below. This example assumes a ShoppingCart class with an addItem method to store items being ordered, and a Catalog class with a static getItem method that returns an item, given an item identifier.

 

HttpSession session = request.getSession(true);

session.putValue("referringPage", request.getHeader("Referer"));

ShoppingCart cart = (ShoppingCart)session.getValue("previousItems");

if (cart == null) {

    // No cart already in session

    cart = new ShoppingCart();

    session.putValue("previousItems", cart);

}

String itemID = request.getParameter("itemID");

if (itemID != null) {

    cart.addItem(Catalog.getItem(itemID));

}

Terminating Sessions

Sessions will automatically become inactive when the amount of time between client accesses exceeds the interval specified by getMaxInactiveInterval. When this happens, any objects bound to the HttpSession object automatically get unbound. When this happens, your attached objects will automatically be notified if they implement the HttpSessionBindingListener interface. Rather than waiting for sessions to time out, you can explicitly deactivate a session through the use of the session’s invalidate method.

 

A Servlet Showing Per-Client Access Counts

Listing 4 presents a simple servlet that shows basic information about the client’s session. When the client connects, the servlet uses request.getSession(true) to either retrieve the existing session or, if there was no session, to create a new one. The servlet then looks for an attribute of type Integer called accessCount. If it cannot find such an attribute, it uses 0 as the number of previous accesses. This value is then incremented and associated with the session by putValue. Finally, the servlet prints a small HTML table showing information about the session. Figures 1 and 2 show the servlet on the initial visit and after the page was reloaded several times.

 

Listing 4 - ShowSession.java

 

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

import java.net.*;

import java.util.*;

 

/**

* Simple example of session tracking. See the shopping

* cart example for a more detailed one.

*/

public class ShowSession extends HttpServlet {

 

    public void doGet(HttpServletRequest request,

    HttpServletResponse response)

    throws ServletException, IOException {

        response.setContentType("text/html");

        PrintWriter out = response.getWriter();

        String title = "Session Tracking Example";

        HttpSession session = request.getSession(true);

        String heading;

        // Use getAttribute instead of getValue in version 2.2.

        Integer accessCount = (Integer)session.getValue("accessCount");

        if (accessCount == null) {

            accessCount = new Integer(0);

            heading = "Welcome, Newcomer";

        } else {

            heading = "Welcome Back";

            accessCount = new Integer(accessCount.intValue() + 1);

        }

 

        // Use setAttribute instead of putValue in version 2.2.

        session.putValue("accessCount", accessCount);

 

        out.println(ServletUtilities.headWithTitle(title) +

        "<BODY BGCOLOR=\"#FDF5E6\">\n" +

        "<H1 ALIGN=\"CENTER\">" + heading + "</H1>\n" +

        "<H2>Information on Your Session:</H2>\n" +

        "<TABLE BORDER=1 ALIGN=\"CENTER\">\n" +

        "<TR BGCOLOR=\"#FFAD00\">\n" +

        " <TH>Info Type<TH>Value\n" +

        "<TR>\n" +

        " <TD>ID\n" +

        " <TD>" + session.getId() + "\n" +

        "<TR>\n" +

        " <TD>Creation Time\n" +

        " <TD>" +

        new Date(session.getCreationTime()) + "\n" +

        "<TR>\n" +

        " <TD>Time of Last Access\n" +

        " <TD>" +

        new Date(session.getLastAccessedTime()) + "\n" +

        "<TR>\n" +

        " <TD>Number of Previous Accesses\n" +

        " <TD>" + accessCount + "\n" +

        "</TABLE>\n" +

        "</BODY></HTML>");

    }

 

    /** Handle GET and POST requests identically. */

    public void doPost(HttpServletRequest request,

    HttpServletResponse response)

    throws ServletException, IOException {

        doGet(request, response);

    }

}

 

Figure 1

 

Figure 2

6.    The Servlet Cookie API

To send cookies to the client, a servlet should create one or more cookies with designated names and values with new Cookie(name, value), set any optional attributes with cookie.setXxx (readable later by cookie.getXxx), and insert the cookies into the response headers with response.addCookie(cookie). To read incoming cookies, a servlet should call request.getCookies, which returns an array of Cookie objects corresponding to the cookies the browser has associated with your site (this is null if there are no cookies in the request). In most cases, the servlet loops down this array until it finds the one whose name (getName) matches the name it had in mind, then calls getValue on that Cookie to see the value associated with that name. Each of these topics is discussed in more detail in the following sections.

Creating Cookies

You create a cookie by calling the Cookie constructor, which takes two strings: the cookie name and the cookie value. Neither the name nor the value should contain white space or any of the following characters: [ ] ( ) = , " / ? @ : ;

Cookie Attributes

Before adding the cookie to the outgoing headers, you can set various characteristics of the cookie by using one of the following setXxx methods, where Xxx is the name of the attribute you want to specify. Each setXxx method has a corresponding getXxx method to retrieve the attribute value. Except for name and value, the cookie attributes apply only to outgoing cookies from the server to the client; they aren’t set on cookies that come from the browser to the server. See Appendix A (Servlet and JSP Quick Reference) for a summarized version of this information.

 

public String getComment()

public void setComment(String comment)

 

These methods look up or specify a comment associated with the cookie. With version 0 cookies (see the upcoming subsection on getVersion and setVersion), the comment is used purely for informational purposes on the server; it is not sent to the client.

 

public String getDomain()

public void setDomain(String domainPattern)

 

These methods get or set the domain to which the cookie applies. Normally, the browser only returns cookies to the exact same hostname that sent them. You can use setDomain method to instruct the browser to return them to other hosts within the same domain. To prevent servers setting cookies that apply to hosts outside their domain, the domain specified is required to start with a dot (e.g.,.prenhall.com), and must contain two dots for noncountry domains like .com, .edu and .gov; and three dots for country domains like .co.uk and .edu.es. For instance, cookies sent from a servlet at bali.vacations.com would not normally get sent by the browser to pages at mexico.vacations.com. If the site wanted this to happen, the servlets could specify cookie.setDomain(".vacations.com").

 

public int getMaxAge()

public void setMaxAge(int lifetime)

 

These methods tell how much time (in seconds) should elapse before the cookie expires. A negative value, which is the default, indicates that the cookie will last only for the current session (i.e., until the user quits the browser) and will not be stored on disk. See the LongLivedCookie

class (Listing 8.4), which defines a subclass of Cookie with a maximum age automatically set one year in the future. Specifying a value of 0instructs the browser to delete the cookie.

 

public String getName()

public void setName(String cookieName)

This pair of methods gets or sets the name of the cookie. The name and the value are the two pieces you virtually always care about. However, since the name is supplied to the Cookie constructor, you rarely need to call setName. On the other hand, getName is used on almost every cookie received on the server. Since the getCookies method of HttpServletRequest returns an array of Cookie objects, it is common to loop down this array, calling getName until you have a particular name, then check the value with getValue.

 

public String getPath()

public void setPath(String path)

 

These methods get or set the path to which the cookie applies. If you don’t specify a path, the browser returns the cookie only to URLs in or below the directory containing the page that sent the cookie. For example, if the server sent the cookie from http://ecommerce.site.com/toys/specials.html, the browser would send the cookie back when connecting to http://ecommerce.site.com/toys/bikes/beginners.html, but not to http://ecommerce.site.com/cds/classical.html. The setPath method can be used to specify something more general. For example, someCookie.setPath("/") specifies that all pages on the server should receive the cookie. The path specified must include the current page; that is, you may specify a more general path than the default, but not a more specific one. So, for example, a servlet at http://host/store/cust-service/request could specify a path of /store/ (since /store/ includes /store/cust-service/) but not a path of /store/cust-service/returns/ (since this directory does not include /store/cust-service/).

 

public boolean getSecure()

public void setSecure(boolean secureFlag)

 

This pair of methods gets or sets the boolean value indicating whether the cookie should only be sent over encrypted (i.e., SSL) connections. The default is false; the cookie should apply to all connections.

 

public String getValue()

public void setValue(String cookieValue)

 

The getValue method looks up the value associated with the cookie; the setValue method specifies it. Again, the name and the value are the two parts of a cookie that you almost always care about, although in a few cases, a name is used as a boolean flag and its value is ignored (i.e., the existence of a cookie with the designated name is all that matters).

 

public int getVersion()

public void setVersion(int version)

 

These methods get/set the cookie protocol version the cookie complies with. Version 0, the default, follows the original Netscape specification (http://www.netscape.com/newsref/std/cookie_spec.html). Version 1, not yet widely supported, adheres to RFC 2109 (retrieve RFCs from the archive sites listed at http://www.rfc-editor.org/).

Placing Cookies in the Response Headers

The cookie is inserted into a Set-Cookie HTTP response header by means of the addCookie method of HttpServletResponse. The method is called addCookie, not setCookie, because any previously specified SetCookie headers are left alone and a new header is set. Here's an example:

 

Cookie userCookie = new Cookie("user", "uid1234");

userCookie.setMaxAge(60*60*24*365); // 1 year

response.addCookie(userCookie);

Reading Cookies from the Client

To send cookies to the client, you create a Cookie, then use addCookie to send a Set-Cookie HTTP response header. To read the cookies that come back from the client, you call getCookies on the HttpServletRequest. This call returns an array of Cookie objects corresponding to the values that came in on the Cookie HTTP request header. If there are no cookies in the request, getCookies returns null. Once you have this array, you typically loop down it, calling getName on each Cookie until you find one matching the name you have in mind. You then call getValue on the matching Cookie and finish with some processing specific to the resultant value. This is such a common process that Section 8.5 presents two utilities that simplify retrieving a cookie or cookie value that matches a designated cookie name.

 

7.    Examples of Setting and Reading Cookies

Listing 5 and Figure 3 show the SetCookies servlet, a servlet that sets six cookies. Three have the default expiration date, meaning that they should apply only until the user next restarts the browser. The other three use setMaxAge to stipulate that they should apply for the next hour, regardless of whether the user restarts the browser or reboots the computer to initiate a new browsing session.

Listing 6 shows a servlet that creates a table of all the cookies sent to it in the request. Figure 4 shows this servlet immediately after the SetCookies servlet is visited. Figure 5 shows it after SetCookies is visited then the browser is closed and restarted.

 

Listing 5 - SetCookies.java

 

package coreservlets;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

 

/** Sets six cookies: three that apply only to the current

* session (regardless of how long that session lasts)

* and three that persist for an hour (regardless of

* whether the browser is restarted).

*/

public class SetCookies extends HttpServlet {

    public void doGet(HttpServletRequest request,

    HttpServletResponse response)

    throws ServletException, IOException {

        for(int i=0; i<3; i++) {

            // Default maxAge is -1, indicating cookie

            // applies only to current browsing session.

            Cookie cookie = new Cookie("Session-Cookie " + i, "Cookie-Value-S" + i);

            response.addCookie(cookie);

            cookie = new Cookie("Persistent-Cookie " + i, "Cookie-Value-P" + i);

            // Cookie is valid for an hour, regardless of whether

            // user quits browser, reboots computer, or whatever.

            cookie.setMaxAge(3600);

            response.addCookie(cookie);

        }

        response.setContentType("text/html");

        PrintWriter out = response.getWriter();

        String title = "Setting Cookies";

        out.println

        (ServletUtilities.headWithTitle(title) +

        "<BODY BGCOLOR=\"#FDF5E6\">\n" +

        "<H1 ALIGN=\"CENTER\">" + title + "</H1>\n" +

        "There are six cookies associated with this page.\n" +

        "To see them, visit the\n" +

        "<A HREF=\"/servlet/coreservlets.ShowCookies\">\n" +

        "<CODE>ShowCookies</CODE> servlet</A>.\n" +

        "<P>\n" +

        "Three of the cookies are associated only with the\n" +

        "current session, while three are persistent.\n" +

        "Quit the browser, restart, and return to the\n" +

        "<CODE>ShowCookies</CODE> servlet to verify that\n" +

        "the three long-lived ones persist across sessions.\n" +

        "</BODY></HTML>");

    }

}

Figure 3

 

Listing 6 - ShowCookies.java

 

package coreservlets;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

 

/** Creates a table of the cookies associated with

* the current page.

*/

public class ShowCookies extends HttpServlet {

    public void doGet(HttpServletRequest request,

    HttpServletResponse response)

    throws ServletException, IOException {

        response.setContentType("text/html");

        PrintWriter out = response.getWriter();

        String title = "Active Cookies";

        out.println(ServletUtilities.headWithTitle(title) +

        "<BODY BGCOLOR=\"#FDF5E6\">\n" +

        "<H1 ALIGN=\"CENTER\">" + title + "</H1>\n" +

        "<TABLE BORDER=1 ALIGN=\"CENTER\">\n" +

        "<TR BGCOLOR=\"#FFAD00\">\n" +

        " <TH>Cookie Name\n" +

        " <TH>Cookie Value");

        Cookie[] cookies = request.getCookies();

        Cookie cookie;

        for(int i=0; i<cookies.length; i++) {

            cookie = cookies[i];

            out.println("<TR>\n" +

            " <TD>" + cookie.getName() + "\n" +

            " <TD>" + cookie.getValue());

        }

        out.println("</TABLE></BODY></HTML>");

    }

}

 

Figure 4

 

 

Figure 5