Working With Cookies

«« Previous
Next »»

Though you are probably already familiar with cookies, you might not know how to take advantage of them in your Java application. This lesson guides you through the concept of cookies and explains how to set a cookie handler so that your HTTP URL connections will use it.

Java SE provides one main class for this functionality, java.net.CookieHandler, and the following supporting classes and interfaces: java.net.CookieManager, java.net.CookiePolicy, java.net.CookieStore, and java.net.HttpCookie.

1. HTTP State Management With Cookies


The HTTP state management mechanism specifies a way to create a stateful session with HTTP requests and responses.

Generally, HTTP request/response pairs are independent of each other. However, the state management mechanism enables clients and servers that can exchange state information to put these pairs in a larger context, which is called a session. The state information used to create and maintain the session is called a cookie.

A cookie is a piece of data that can be stored in a browser's cache. If you visit a web site and then revisit it, the cookie data can be used to identify you as a return visitor. Cookies enable state information, such as an online shopping cart, to be remembered. A cookie can be short term, holding data for a single web session, that is, until you close the browser, or a cookie can be longer term, holding data for a week or a year.

2. CookieHandler Callback Mechanism


HTTP state management is implemented in Java SE through the java.net.CookieHandler class. A CookieHandler object provides a callback mechanism to provide an HTTP state management policy implementation in the HTTP protocol handler. That is, URLs that use HTTP as the protocol, new URL("http://example.com") for example, will use the HTTP protocol handler. This protocol handler calls back to the CookieHandler object, if set, to handle the state management.

The CookieHandler class is an abstract class that has two pairs of related methods. The first pair, getDefault() and setDefault(cookieHandler), are static methods that enable you to discover the current handler that is installed and to install your own handler.

No default handler is installed, and installing a handler is done on a system-wide basis. For applications running within a secure environment, that is, they have a security manager installed, you must have special permission to get and set the handler.

The second pair of related methods, put(uri, responseHeaders) and get(uri, requestHeaders), enable you to set and get all the applicable cookies to and from a cookie cache for the specified URI in the response/request headers, respectively. These methods are abstract, and a concrete implementation of a CookieHandler must provide the implementation.

Java Web Start and Java Plug-in have a default CookieHandler installed. However, if you are running a stand-alone application and want to enable HTTP state management, you must set a system-wide handler. The next two pages in this lesson show you how to do so.

3. Default CookieManager


java.net.CookieManager provides a concrete implementation of a CookieHandler and for most users is sufficient for handling HTTP state management. CookieManager separates the storage of cookies from the policy surrounding, accepting, and rejecting them. A CookieManager is initialized with a java.net.CookieStore and a java.net.CookiePolicy. CookieStore manages the storage of the cookies. CookiePolicy makes policy decisions on cookie acceptance and rejection.

The following code shows how to create and set a system-wide CookieManager:

java.net.CookieManager cm = new java.net.CookieManager();
java.net.CookieHandler.setDefault(cm);

The first line calls the default CookieManager constructor to create the instance. The second line calls the static setDefault method of CookieHandler to set the system-wide handler.

The default CookieManager constructor creates a new CookieManager instance with a default cookie store and accept policy. CookieStore is the place where any accepted HTTP cookie is stored. If not specified when created, a CookieManager instance will use an internal in-memory implementation. This implementation is not persistent and only lives for the lifetime of the Java Virtual Machine. Users requiring a persistent store must implement their own store.

The default cookie policy used by CookieManager is CookiePolicy.ACCEPT_ORIGINAL_SERVER, which only accepts cookies from the original server. So, the Set-Cookie response from the server must have a “domain” attribute set, and it must match the domain of the host in the URL. For more information, see java.net.HttpCookie.domainMatches. Users requiring a different policy must implement the CookiePolicy interface and pass it to the CookieManager constructor or set it to an already constructed CookieManager instance by using the setCookiePolicy(cookiePolicy) method.

When retrieving cookies from the cookie store, CookieManager also enforces the path-match rule from section 3.3.4 of RFC 2965 . So, a cookie must also have its “path” attribute set so that the path-match rule can be applied before the cookie is retrieved from the cookie store.

In summary, CookieManager provides the framework for handling cookies and provides a good default implementation for CookieStore. CookieManager is highly customizable by enabling you to set your own CookieStore, CookiePolicy, or both.

4. Custom CookieManager


Two aspects of the CookieManager class can be customized, the CookiePolicy and the CookieStore.

CookiePolicy

For convenience, CookiePolicy defines the following pre-defined policies for accepting cookies:

◈ CookiePolicy.ACCEPT_ORIGINAL_SERVER only accepts cookies from the original server.
◈ CookiePolicy.ACCEPT_ALL accepts all cookies.
◈ CookiePolicy.ACCEPT_NONE accepts no cookies.
◈ You can also define your own cookie policy by implementing the shouldAccept method of CookiePolicy. You can then use this CookiePolicy by passing it to the multi-argument CookieManager constructor or by calling the setCookiePolicy(cookiePolicy) method to change an already existing cookie manager.

The following is an example of a cookie policy that rejects cookies from domains that are on a blacklist, before applying the CookiePolicy.ACCEPT_ORIGINAL_SERVER policy:

import java.net.*;

public class BlacklistCookiePolicy implements CookiePolicy {
    String[] blacklist;

    public BlacklistCookiePolicy(String[] list) {
        blacklist = list;
    }

    public boolean shouldAccept(URI uri, HttpCookie cookie)  {
        String host;
        try {
            host =  InetAddress.getByName(uri.getHost()).getCanonicalHostName();
        } catch (UnknownHostException e) {
            host = uri.getHost();
        }

        for (int i = 0; i<blacklist.length; i++) {
    if (HttpCookie.domainMatches(blacklist[i], host)) {
                return false;
            }
        }

        return CookiePolicy.ACCEPT_ORIGINAL_SERVER.shouldAccept(uri, cookie);
    }
}

When you create a BlacklistCookiePolicy instance, you pass it an array of strings representing the domains that you do not want to accept cookies from. Then, you set this BlacklistCookiePolicy instance as the cookie policy for your CookieManager. For example:

String[] list = new String[]{ ".example.com" };
CookieManager cm = new CookieManager(null, new BlacklistCookiePolicy(list));
CookieHandler.setDefault(cm);

The sample code will not accept cookies from hosts such as the following:

host.example.com
domain.example.com

However, this sample code will accept cookies from hosts such as the following:

example.com
example.org
myhost.example.org

CookieStore

A CookieStore is an interface that represents a storage area for cookies. CookieManager adds the cookies to the CookieStore for every HTTP response and retrieves cookies from the CookieStore for every HTTP request.
You can implement this interface to provide your own CookieStore and pass it to the CookieManager during creation. You cannot set the CookieStore after the CookieManager instance has been created. However, you can get a reference to the cookie store by calling CookieManager.getCookieStore(). Doing so can be useful as it enables you to leverage the default in-memory CookieStore implementation that is provided by Java SE and complement its functionality.

For example, you might want to create a persistent cookie store that would save cookies so that they can be used even if the Java Virtual Machine is restarted. Your implementation would work similar to the following:

1. Any cookies that were previously saved are read in.
2. During runtime, cookies are stored and retrieved from memory.
3. Cookies are written out to persistent storage before exiting.

The following is an incomplete example of this cookie store. This example shows you how to leverage the Java SE default in-memory cookie store and how you might extend its functionality.

import java.net.*;
import java.util.*;

public class PersistentCookieStore implements CookieStore, Runnable {
    CookieStore store;

    public PersistentCookieStore() {
        // get the default in memory cookie store
        store = new CookieManager().getCookieStore();

        // todo: read in cookies from persistant storage
        // and add them store

        // add a shutdown hook to write out the in memory cookies
        Runtime.getRuntime().addShutdownHook(new Thread(this));
    }

    public void run() {
        // todo: write cookies in store to persistent storage
    }

    public void add(URI uri, HttpCookie cookie) {
        store.add(uri, cookie);
    }

    public List<HttpCookie> get(URI uri) {
        return store.get(uri);
    }

    public List<HttpCookie> getCookies() {
        return store.getCookies();
    }
 
    public List<URI> getURIs() {
        return store.getURIs();
    }

    public boolean remove(URI uri, HttpCookie cookie) {
        return store.remove(uri, cookie);
    }

    public boolean removeAll()  {
        return store.removeAll();
    }
}

«« Previous
Next »»