| You are receiving this e-mail because you elected to receive e-mail from Sun Microsystems, Inc. To update your communications preferences, please see the link at the bottom of this message. We respect your privacy and post our privacy policy prominently on our Web site http://sun.com/privacy/ |
![]() |
||||||
|
||||||
| In this Issue | ||
Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE). This issue covers: » Cookie Management with CookieHandler » Tech Tips Quiz These tips were developed using the Java 2 Platform Standard Edition Development Kit 5.0 (JDK 5.0). You can download JDK 5.0 at http://java.sun.com/j2se/1.5.0/download.jsp. This issue of the Core Java Technologies Tech Tips is written by John Zukowski, president of JZ Ventures, Inc. See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms. For more Java technology content, visit these sites: java.sun.com - The latest Java platform releases, tutorials, and newsletters. java.net - A web forum where enthusiasts of Java technology can collaborate and build solutions together. java.com - Hot games, cool apps -- Experience the power of Java technology. |
||
| COOKIE MANAGEMENT WITH COOKIEHANDLER | ||
|
For the Java platform, access to objects through Uniform Resource Locator (URLs) is managed by a series of protocol handlers. The first part of the URL identifies what protocol to use. For example, if you start a URL with
As part of the implementation of the http protocol handler, J2SE 5.0 adds a There is no default handler installed with J2SE 5.0. However, you can register a handler so that an application can remember cookies and send them back for your http connections.
Returning to the
For applications that have an installed security manager, getting and setting the handler requires special permission. To clear the actual handler, pass in null as the handler. As mentioned earlier, there is no default handler. The second pair of methods allows you to get and set cookies from or to a cookie cache that you manage:
The
This looks simple, and creating the handler actually is simple. But defining the cache takes a little more work. To demonstrate, let's use a custom
import java.io.*;
import java.net.*;
import java.util.*;
public class Fetch {
public static void main(String args[]) throws Exception {
if (args.length == 0) {
System.err.println("URL missing");
System.exit(-1);
}
String urlString = args[0];
CookieHandler.setDefault(new ListCookieHandler());
URL url = new URL(urlString);
URLConnection connection = url.openConnection();
Object obj = connection.getContent();
url = new URL(urlString);
connection = url.openConnection();
obj = connection.getContent();
}
}
This program first creates and installs a
Now let's look at how to manage this, initially through the String urlString = ...; URL url = new URL(urlString); URLConnection connection = url.openConnection(); InputStream is = connection.getInputStream(); // .. read content from stream
Part of the information available from the connection might be a series of headers. These depend on the protocol in use. To discover the headers, you can use the
To demonstrate, the following program lists all the headers for a given URL:
import java.net.*;
import java.util.*;
public class ListHeaders {
public static void main(String args[]) throws Exception {
if (args.length == 0) {
System.err.println("URL missing");
}
String urlString = args[0];
URL url = new URL(urlString);
URLConnection connection = url.openConnection();
Map<String,List<String>> headerFields =
connection.getHeaderFields();
Set<String> set = headerFields.keySet();
Iterator<String> itor = set.iterator();
while (itor.hasNext()) {
String key = itor.next();
System.out.println("Key: " + key + " / " +
headerFields.get(key));
}
}
}
The Key: <key> / [<value>] So if you enter: >> java ListHeaders http://java.sun.com You should see something like the following displayed: Key: Set-Cookie / [SUN_ID=192.168.0.1:269421125489956; EXPIRES=Wednesday, 31- Dec-2025 23:59:59 GMT; DOMAIN=.sun.com; PATH=/] Key: Set-cookie / [JSESSIONID=688047FA45065E07D8792CF650B8F0EA;Path=/] Key: null / [HTTP/1.1 200 OK] Key: Transfer-encoding / [chunked] Key: Date / [Wed, 31 Aug 2005 12:05:56 GMT] Key: Server / [Sun-ONE-Web-Server/6.1] Key: Content-type / [text/html;charset=ISO-8859-1] (Long lines in the result shown above were manually split.) This displays only the headers for that URL, it doesn't display the HTML page located there. Notice that the displayed information includes the web server used by the site and the date and time of the local system. Also notice the two "Set-Cookie" lines. These are the headers related to cookies. The cookies can be saved from the headers, and then sent with the next request.
Now let's create a
Start with the
List<String> setCookieList =
responseHeaders.get("Set-Cookie");
After you have the list of cookies, loop through each and store them. If the cookie already exists, replace the existing copy:
if (setCookieList != null) {
for (String item : setCookieList) {
Cookie cookie = new Cookie(uri, item);
// Remove cookie if it already exists in cache
// New one will replace it
for (Cookie existingCookie : cache) {
...
}
System.out.println("Adding to cache: " + cookie);
cache.add(cookie);
}
}
The "cache" here can be anything from a database to a
Essentially, that is all there is to the
The
To implement the
// Retrieve all the cookies for matching URI
// Put in comma-separated list
StringBuilder cookies = new StringBuilder();
for (Cookie cookie : cache) {
// Remove cookies that have expired
if (cookie.hasExpired()) {
cache.remove(cookie);
} else if (cookie.matches(uri)) {
if (cookies.length() > 0) {
cookies.append(", ");
}
cookies.append(cookie.toString());
}
}
Again, the
The next part of the
// Map to return
Map<String, List<String>> cookieMap =
new HashMap<String, List<String>>(requestHeaders);
// Convert StringBuilder to List, store in map
if (cookies.length() > 0) {
List<String> list =
Collections.singletonList(cookies.toString());
cookieMap.put("Cookie", list);
}
return Collections.unmodifiableMap(cookieMap);
Here is the complete
import java.io.*;
import java.net.*;
import java.util.*;
public class ListCookieHandler extends CookieHandler {
// "Long" term storage for cookies, not serialized so only
// for current JVM instance
private List<Cookie> cache = new LinkedList<Cookie>();
/**
* Saves all applicable cookies present in the response
* headers into cache.
* @param uri URI source of cookies
* @param responseHeaders Immutable map from field names to
* lists of field
* values representing the response header fields returned
*/
public void put(
URI uri,
Map<String, List<String>> responseHeaders)
throws IOException {
System.out.println("Cache: " + cache);
List<String> setCookieList =
responseHeaders.get("Set-Cookie");
if (setCookieList != null) {
for (String item : setCookieList) {
Cookie cookie = new Cookie(uri, item);
// Remove cookie if it already exists
// New one will replace
for (Cookie existingCookie : cache) {
if((cookie.getURI().equals(
existingCookie.getURI())) &&
(cookie.getName().equals(
existingCookie.getName()))) {
cache.remove(existingCookie);
break;
}
}
System.out.println("Adding to cache: " + cookie);
cache.add(cookie);
}
}
}
/**
* Gets all the applicable cookies from a cookie cache for
* the specified uri in the request header.
*
* @param uri URI to send cookies to in a request
* @param requestHeaders Map from request header field names
* to lists of field values representing the current request
* headers
* @return Immutable map, with field name "Cookie" to a list
* of cookies
*/
public Map<String, List<String>> get(
URI uri,
Map<String, List<String>> requestHeaders)
throws IOException {
// Retrieve all the cookies for matching URI
// Put in comma-separated list
StringBuilder cookies = new StringBuilder();
for (Cookie cookie : cache) {
// Remove cookies that have expired
if (cookie.hasExpired()) {
cache.remove(cookie);
} else if (cookie.matches(uri)) {
if (cookies.length() > 0) {
cookies.append(", ");
}
cookies.append(cookie.toString());
}
}
// Map to return
Map<String, List<String>> cookieMap =
new HashMap<String, List<String>>(requestHeaders);
// Convert StringBuilder to List, store in map
if (cookies.length() > 0) {
List<String> list =
Collections.singletonList(cookies.toString());
cookieMap.put("Cookie", list);
}
System.out.println("Cookies: " + cookieMap);
return Collections.unmodifiableMap(cookieMap);
}
}
The last piece of the puzzle is the
public Cookie(URI uri, String header) {
String attributes[] = header.split(";");
String nameValue = attributes[0].trim();
this.uri = uri;
this.name = nameValue.substring(0, nameValue.indexOf('='));
this.value = nameValue.substring(nameValue.indexOf('=')+1);
this.path = "/";
this.domain = uri.getHost();
for (int i=1; i < attributes.length; i++) {
nameValue = attributes[i].trim();
int equals = nameValue.indexOf('=');
if (equals == -1) {
continue;
}
String name = nameValue.substring(0, equals);
String value = nameValue.substring(equals+1);
if (name.equalsIgnoreCase("domain")) {
String uriDomain = uri.getHost();
if (uriDomain.equals(value)) {
this.domain = value;
} else {
if (!value.startsWith(".")) {
value = "." + value;
}
uriDomain =
uriDomain.substring(uriDomain.indexOf('.'));
if (!uriDomain.equals(value)) {
throw new IllegalArgumentException(
"Trying to set foreign cookie");
}
this.domain = value;
}
} else if (name.equalsIgnoreCase("path")) {
this.path = value;
} else if (name.equalsIgnoreCase("expires")) {
try {
this.expires = expiresFormat1.parse(value);
} catch (ParseException e) {
try {
this.expires = expiresFormat2.parse(value);
} catch (ParseException e2) {
throw new IllegalArgumentException(
"Bad date format in header: " + value);
}
}
}
}
}
The other methods in the class just return the stored data or check for expiration:
public boolean hasExpired() {
if (expires == null) {
return false;
}
Date now = new Date();
return now.after(expires);
}
public String toString() {
StringBuilder result = new StringBuilder(name);
result.append("=");
result.append(value);
return result.toString();
}
A "match" shouldn't be found if the cookie expired:
public boolean matches(URI uri) {
if (hasExpired()) {
return false;
}
String path = uri.getPath();
if (path == null) {
path = "/";
}
return path.startsWith(this.path);
}
Note that the
Here is the definition of the entire
import java.net.*;
import java.text.*;
import java.util.*;
public class Cookie {
String name;
String value;
URI uri;
String domain;
Date expires;
String path;
private static DateFormat expiresFormat1
= new SimpleDateFormat("E, dd MMM yyyy k:m:s 'GMT'", Locale.US);
private static DateFormat expiresFormat2
= new SimpleDateFormat("E, dd-MMM-yyyy k:m:s 'GMT'", Locale.US);
/**
* Construct a cookie from the URI and header fields
*
* @param uri URI for cookie
* @param header Set of attributes in header
*/
public Cookie(URI uri, String header) {
String attributes[] = header.split(";");
String nameValue = attributes[0].trim();
this.uri = uri;
this.name =
nameValue.substring(0, nameValue.indexOf('='));
this.value =
nameValue.substring(nameValue.indexOf('=')+1);
this.path = "/";
this.domain = uri.getHost();
for (int i=1; i < attributes.length; i++) {
nameValue = attributes[i].trim();
int equals = nameValue.indexOf('=');
if (equals == -1) {
continue;
}
String name = nameValue.substring(0, equals);
String value = nameValue.substring(equals+1);
if (name.equalsIgnoreCase("domain")) {
String uriDomain = uri.getHost();
if (uriDomain.equals(value)) {
this.domain = value;
} else {
if (!value.startsWith(".")) {
value = "." + value;
}
uriDomain = uriDomain.substring(
uriDomain.indexOf('.'));
if (!uriDomain.equals(value)) {
throw new IllegalArgumentException(
"Trying to set foreign cookie");
}
this.domain = value;
}
} else if (name.equalsIgnoreCase("path")) {
this.path = value;
} else if (name.equalsIgnoreCase("expires")) {
try {
this.expires = expiresFormat1.parse(value);
} catch (ParseException e) {
try {
this.expires = expiresFormat2.parse(value);
} catch (ParseException e2) {
throw new IllegalArgumentException(
"Bad date format in header: " + value);
}
}
}
}
}
public boolean hasExpired() {
if (expires == null) {
return false;
}
Date now = new Date();
return now.after(expires);
}
public String getName() {
return name;
}
public URI getURI() {
return uri;
}
/**
* Check if cookie isn't expired and if URI matches,
* should cookie be included in response.
*
* @param uri URI to check against
* @return true if match, false otherwise
*/
public boolean matches(URI uri) {
if (hasExpired()) {
return false;
}
String path = uri.getPath();
if (path == null) {
path = "/";
}
return path.startsWith(this.path);
}
public String toString() {
StringBuilder result = new StringBuilder(name);
result.append("=");
result.append(value);
return result.toString();
}
}
Now that you have all the pieces, you can run the earlier
>> java Fetch http://java.sun.com
Cookies: {Connection=[keep-alive], Host=[java.sun.com],
User-Agent=[Java/1.5.0_04], GET / HTTP/1.1=[null],
Content-type=[application/x-www-form-urlencoded],
Accept=[text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2]}
Cache: []
Adding to cache: SUN_ID=192.168.0.1:235411125667328
Cookies: {Connection=[keep-alive], Host=[java.sun.com],
User-Agent=[Java/1.5.0_04], GET / HTTP/1.1=[null],
Cookie=[SUN_ID=192.168.0.1:235411125667328],
Content-type=[application/x-www-form-urlencoded],
Accept=[text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2]}
Cache: [SUN_ID=192.168.0.1:235411125667328]
(Long lines in the result shown above were manually split.)
The lines beginning with "Cache:" show the stored cache. Notice how the
For more information on working with cookies and URL connections, see the Custom Networking trail in The Java Tutorial. This is based on J2SE 1.4, so there is no information yet in the Tutorial on the |
||
| TECH TIPS QUIZ | ||
|
Over the years, the Core Java Technologies Tech Tips have covered a wide variety of Java programming language topics. Here's a short quiz that tests your knowledge of some topics covered in past Tech Tips. You can find the answers at the end of the quiz.
Answers
Back to Top |
||
|
Comments? Send your feedback on the Tech Tips: http://developers.sun.com/contact/feedback.jsp?category=sdn
Subscribe to the following newsletters for the latest information about technologies and products in other Java platforms:
ARCHIVES: You'll find the Core Java Technologies Tech Tips archives at: http://java.sun.com/developer/JDCTechTips/index.html IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies: http://www.sun.com/share/text/termsofuse.html http://www.sun.com/privacy/ http://developer.java.sun.com/berkeley_license.html © 2005 Sun Microsystems, Inc. All Rights Reserved. For information on Sun's trademarks see: http://sun.com/suntrademarks Java, J2EE, J2SE, J2ME, and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. Sun Microsystems, Inc. 10 Network Circle, MPK10-209 Menlo Park, CA 94025 US |