Saving Client State with Cookies and Java

Cookies serve as a facility for servers to send information to a client.   This information is then housed on the client, from which the server can later retrieve the information. In this article, we will study the concept of saving client state with cookies using Java Servlets. I’ll walk you through an end to end example where you will store and retrieve data using cookies.

This article is not intended to teach you about Java Servlets;  I am assuming that you have a decent background knowledge of the subject.  If this is not the case, you may want to take a look at the articles referenced in the resources section of the article (last page)before moving on.

You will need an application server to run the servlets you create. To do my experimentation, I used IBM WebSphere Studio Application Developer. This powerful product acts as integrated development environment (IDE) in which you can build, test, and deploy your application. Embedded within the product is also a WebSphere Test Environment, in which we can deploy our web applications for testing.  Of course, you can also deploy to Apache Tomcat or some other application server, but within Application Developer, you never have to leave your development environment. You can learn more about IBM WebSphere Studio Application Developer here.

{mospagebreak title=Some Background Information About Cookies}

Before we delve deeper into the handling of cookies, let’s cover some basic background information about the subject.  Java Servlets (which reside on the server) send cookies to clients by adding fields to their HTTP response headers.  Similarly, clients return cookies to servers by adding fields to HTTP request headers.  When a client application (e.g., a Web browser) receives a cookie from a web server, the client application stores the cookie locally. Upon subsequent request to the web server, the cookie will be returned.  You can learn more about cookies by reading the Netscape specification (see resources section).

Client State with Cookies in Java

Figure 1: Cookie Transmission Over the Network 

The server can send multiple cookies to the client.  Each cookie is sent as a separate response header.  The same is true for the client talking back to the server, except that there, we are dealing with multiple request headers.  It is important to note that you can have cookies with the same name.  For example, I might have an application where I send the server two cookies with headers both named Citizenship.    Let’s say I have dual citizenship; in such a case, I might send two cookies with two different country names as values. 

What Can I Store in A Cookie?

The javax.servlet.http.Cookie object allows you to set a name and an associated value.  Also, it allows you to set a comment as an optional attribute.  You can see the setters associate to the Cookie object in the figure below.  Refer to the javadoc of the javax.servlet.http.Cookie class for a complete breakdown of the class’s API.

Client State with Cookies in Java

Figure 2: The Setters Associated to the CookieObject 

When you call the constructor of a Cookie, you set its name and value.  The name of the cookie must be an HTTP/1.1 “token.”  A token is a string that does not contain characters listed in RFC 2068.  Your safe bet is to use an alphanumeric string as your token.  Values of your cookie can be any string.  However, if you want to be prudent and stick to specification, the original Netscape cookie specification prohibits the use of the following characters:

[ ] ( ) = , ” / ? @ : ;

Note that you can override the value you set for a cookie via the constructor later by calling the Cookie object’s  setValue method. 

Studying Some Working Code

The code mentioned throughout this article for demonstrative purposes can be downloaded here.  The code is bundled as an enterprise application archive (EAR) so you can deploy it to your application server of choice. 

{mospagebreak title=Setting Cookies on Your Client}

CookieSetterServlet is one of the servlets you will find in the EAR.  You can find the code for this servlet in Listing 1. 

[code]
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax
 .servlet
 .http
 .HttpServletRequest;
import javax
 .servlet
 .http
 .HttpServletResponse;

 

public class CookieSetterServlet
 extends HttpServlet
 implements Servlet
{
 public void doGet(
  HttpServletRequest request,
  HttpServletResponse response)
  throws ServletException, IOException
 {
  // grab the user's favorite cookie type from the request
  String favoriteCookieType =
   request.getParameter(
    "FavoriteCookiePreference");
  // report the value to the console
  System.out.println(
   "Setting favorite cookie type to: "
    + favoriteCookieType);
  // create a new Cookie object. 
  // set the token name to "FavoriteCookieType"
  // set the value to the value extracted from the request
  Cookie favoriteCookie =
   new Cookie(
    "FavoriteCookieType",
    favoriteCookieType);
  // set the value of the comment
  favoriteCookie.setComment(
   "Houses User's Favorite Cookie Type");
  // add the cookie to the response
  response.addCookie(
   favoriteCookie);
  // provide some visual feedback to the user
  PrintWriter out =
   response.getWriter();
  out.println(
   " "
    + "");
  out.println(
   "

I am now going to "remember" that you like "
    + favoriteCookieType
    + " cookies, using Cookies.

");
  out.println("");
 }
 public void doPost(
  HttpServletRequest request,
  HttpServletResponse response)
  throws ServletException, IOException
 {
  doGet(request, response);
 }
}
[/code]
Listing 1: CookieSetterServlet.java

The servlet grabs a parameter from the request named FavoriteCookiePreference.    Note that the HTML file, favoritecookie.html calls the servlet and feeds the servlet with request data (see Figure 3).

Client State with Cookies in Java

 
Figure 3: favoritecookie.html Feeds
CookieSetterServletwith Request Data

Our code to create a cookie on the server side and send it back to the client is a straightforward process.  We simply create a new Cookie object by calling the constructor with the name of the cookie we want to create and its associated value (the value we collected from our request). 

In the code, we also set a comment for the cookie.  Finally, the resulting Cookie object is packaged to be shipped back to the client by calling the Response object’s addCookie method, which takes our new Cookie as an argument.

For some visual feedback, the servlet reports to the user what was extracted from the request (see Figure 4).


Client State with Cookies in Java

Figure 4: Output of the CookieSetterServlet
Servlet, After Sending the Cookie to the Client

It is important to note that we added the cookie to our Response object before using our PrintWriter object. This order is mandatory if you want things to work. Your cookie is going to be sent back to the client as a header; headers must be written before accessing the PrintWriter.

It is also important to note that the cookies that a server places on a client are associated only to that server. 

Coming up, we’ll use a different servlet to grab our cookie’s value from the client.  Note that since this second servlet will reside on the same server as our first servlet, we are able to access the client cookie we established (which we named FavoriteCookieType).

{mospagebreak title=Grabbing Cookies From Your Client}

Extracting cookies from your client is also a straightforward process, courtesy of the Java APIs provided to us.  The acquisition process of the cookie we set using the CookieSetterServlet is shown in Listing 2. 

[code]
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax
 .servlet
 .http
 .HttpServletRequest;
import javax
 .servlet
 .http
 .HttpServletResponse;

 

public class CookieSetterServlet
 extends HttpServlet
 implements Servlet
{
 public void doGet(
  HttpServletRequest request,
  HttpServletResponse response)
  throws ServletException, IOException
 {
  // grab the user's favorite cookie type from the request
  String favoriteCookieType =
   request.getParameter(
    "FavoriteCookiePreference");
  // report the value to the console
  System.out.println(
   "Setting favorite cookie type to: "
    + favoriteCookieType);
  // create a new Cookie object. 
  // set the token name to "FavoriteCookieType"
  // set the value to the value extracted from the request
  Cookie favoriteCookie =
   new Cookie(
    "FavoriteCookieType",
    favoriteCookieType);
  // set the value of the comment
  favoriteCookie.setComment(
   "Houses User's Favorite Cookie Type");
  // add the cookie to the response
  response.addCookie(
   favoriteCookie);
  // provide some visual feedback to the user
  PrintWriter out =
   response.getWriter();
  out.println(
   " "
    + "");
  out.println(
   "

I am now going to "remember" that you like "
    + favoriteCookieType
    + " cookies, using Cookies.

");
  out.println("");
 }
 public void doPost(
  HttpServletRequest request,
  HttpServletResponse response)
  throws ServletException, IOException
 {
  doGet(request, response);
 }
}
[/code]

Listing 2: CookieSetterServlet.java

To obtain the cookie from the client, we obtain our cookies as an array of Cookie objects from the Request object.   We then have to cycle through the array, looking for the name of the cookie of our interest.  Once we find the correct cookie, we extract its value and present the value to the user (see Figure 5).

Client State with Cookies in Java

Figure 5: Output of the CookieGrabberServlet, which
Reports the Value of the Cookie Seen on the Client 

{mospagebreak title=Throwing JavaScript Into the (Cookie Dough) Mix}

Since our cookies are stored on our client, our client Internet browser can access them using JavaScript.  We do just that in the file grabcookievaluewithjavascript.html whose code is shown in Listing 3 below. 

[code]
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<META name="GENERATOR" content="IBM WebSphere Studio">
<META http-equiv="Content-Style-Type" content="text/css">
<LINK href="theme/Master.css" rel="stylesheet" type="text/css">
<TITLE>JavaScript Cookie Demo</TITLE>
<SCRIPT language="JavaScript">
<!--
 function GetCookieValue(offset)
 {
  var endstr = document.cookie.indexOf (";", offset);
  if (endstr == -1)
      endstr = document.cookie.length;
  document.write(document.cookie.substring(offset, endstr));
  return unescape(document.cookie.substring(offset, endstr));
 }
 

 function ReportCookieValue (cookieName)
 {
  var arg = cookieName + "=";
    var alen = arg.length;
  var clen = document.cookie.length;
  var i = 0;
    while (i < clen)
    {
     

      var j = i + alen;
      var sub = document.cookie.substring(i,j);
      if (sub == arg)
      {
         return GetCookieValue(j);
        }
      else i = document.cookie.indexOf(" ", i) + 1;
      if (i == 0) break;
     

    }    
    return null;
 }
// -->
</SCRIPT>
</HEAD>
<BODY>
<p></p>
<h2>According to JavaScript, you like <script language="JavaScript">

 

<!--//ReportCookieValue("FavoriteCookieType")//-->

 

</script> Cookies .</h2>
<p></p>

 

</BODY>
</HTML>
[/code]

Listing 3: grabcookiewithjavascript.html

The output of our JavaScript cookie extraction is shown below:

Client State with Cookies in Java

 
Figure 6: Extracting our Cookie Value on the Client via JavaScript 

Being able to access your cookies via JavaScript represents a powerful functionality. By doing this, you can have forms preloaded with what is resident in client cookie data. As an exercise, you should try and modify favoritecookie.html to initially present the pull down value that was resident in your cookie.

{mospagebreak title=Cleaning Up: Getting Rid of Cookie Crumbs}

To delete a cookie, you can use the setMaxAge method of the Cookie class. Using a method argument of zero, the cookie on the client side will be effectively deleted.  This is demonstrated in CookieCrumbDeleterServlet.java which is shown in Listing 4.

[code]
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax
 .servlet
 .http
 .HttpServletRequest;
import javax
 .servlet
 .http
 .HttpServletResponse;
public class CookieCrumbDeleterServlet
 extends HttpServlet
 implements Servlet
{
 public void doGet(
  HttpServletRequest request,
  HttpServletResponse response)
  throws ServletException, IOException
 {
  Cookie[] cookies =
   request.getCookies();
  String cookieExtract = null;
  Cookie cookieToDelete = null;
  for (int i = 0;
   i < cookies.length;
   i++)
  {
   if (cookies[i]
    .getName()
    .equals("FavoriteCookieType"))
   {
    cookieToDelete =
     cookies[i];
    // mark for deletion by client by setting max age to zero
    cookieToDelete
     .setMaxAge(
     0);
   }
  }
  //  add the cookie to the response back to the client
  response.addCookie(
   cookieToDelete);
  response.setContentType(
   "text/html");
  PrintWriter out =
   response.getWriter();
  out.println(
   " "
    + "");
  out.println(
   "

I just deleted your cookie.

");
  out.println("");
 }
 public void doPost(
  HttpServletRequest request,
  HttpServletResponse response)
  throws ServletException, IOException
 {
  doGet(request, response);
 }
}
[/code]

Listing 3: CookieCrumbDeleterServlet.java

Conclusion

Giving your clients a “memory” is a highly needed feature in many web applications.  One can leverage techniques such as persisting to a database, but in many cases, one can leverage the facility of cookies which most popular browsers support.  A word of caution though, many browsers give users the option to block cookies (see Figure below of Microsoft Internet Explorer’s Advanced Privacy Settings).

Client State with Cookies in Java

Figure 7: Internet Explorer Allows Users the Ability to Block Cookies 

Consequently, when one designs their web applications and considers the user of cookie technology, they should take the ability for clients to block cookies and delete cookies into major consideration.

Background Information: Fundamentals of Java Servlets 

Resources
Servlet API
Handing Cookies Using the java.net.* API
Netscape Cookie Specification

Google+ Comments

Google+ Comments