Book Home Java Servlet Programming Search this book

3.6. Last Modified Times

By now, we're sure you've learned that servlets handle GET requests with the doGet() method. And that's almost true. The full truth is that not every request really needs to invoke doGet(). For example, a web browser that repeatedly accesses PrimeSearcher should need to call doGet() only after the searcher thread has found a new prime. Until that time, any call to doGet() just generates the same page the user has already seen, a page probably stored in the browser's cache. What's really needed is a way for a servlet to report when its output has changed. That's where the getLastModified() method comes in.

Most web servers, when they return a document, include as part of their response a Last-Modified header. An example Last-Modified header value might be:

Tue, 06-May-98 15:41:02 GMT

This header tells the client the time the page was last changed. That information alone is only marginally interesting, but it proves useful when a browser reloads a page.

Most web browsers, when they reload a page, include in their request an If-Modified-Since header. Its structure is identical to the Last-Modified header:

Tue, 06-May-98 15:41:02 GMT

This header tells the server the Last-Modified time of the page when it was last downloaded by the browser. The server can read this header and determine if the file has changed since the given time. If the file has changed, the server must send the newer content. If the file hasn't changed, the server can reply with a simple, short response that tells the browser the page has not changed and it is sufficient to redisplay the cached version of the document. For those familiar with the details of HTTP, this response is the 304 "Not Modified" status code.

This technique works great for static pages: the server can use the file system to find out when any file was last modified. For dynamically generated content, though, such as that returned by servlets, the server needs some extra help. By itself, the best the server can do is play it safe and assume the content changes with every access, effectively eliminating the usefulness of the Last-Modified and If-Modified-Since headers.

The extra help a servlet can provide is implementing the getLastModified() method. A servlet should implement this method to return the time it last changed its output. Servers call this method at two times. The first time the server calls it is when it returns a response, so that it can set the response's Last-Modified header. The second time occurs in handling GET requests that include the If-Modified-Since header (usually reloads), so it can intelligently determine how to respond. If the time returned by getLastModified() is equal to or earlier than the time sent in the If-Modified-Since header, the server returns the "Not Modified" status code. Otherwise, the server calls doGet() and returns the servlet's output.[7]

[7] A servlet can directly set its Last-Modified header inside doGet(), using techniques discussed in Chapter 5, "Sending HTML Information". However, by the time the header is set inside doGet(), it's too late to decide whether or not to call doGet().

Some servlets may find it difficult to determine their last modified time. For these situations, it's often best to use the "play it safe" default behavior. Many servlets, however, should have little or no problem. Consider a "bulletin board" servlet where people post carpool openings or the need for racquetball partners. It can record and return when the bulletin board's contents were last changed. Even if the same servlet manages several bulletin boards, it can return a different modified time depending on the board given in the parameters of the request. Here's a getLastModified() method for our PrimeSearcher example that returns when the last prime was found.

public long getLastModified(HttpServletRequest req) {
  return lastprimeModified.getTime() / 1000 * 1000;
}

Notice that this method returns a long value that represents the time as a number of milliseconds since midnight, January 1, 1970, GMT. This is the same representation used internally by Java to store time values. Thus, the servlet uses the getTime() method to retrieve lastprimeModified as a long.

Before returning this time value, the servlet rounds it down to the nearest second by dividing by 1000 and then multiplying by 1000. All times returned by getLastModified() should be rounded down like this. The reason is that the Last-Modified and If-Modified-Since headers are given to the nearest second. If getLastModified() returns the same time but with a higher resolution, it may erroneously appear to be a few milliseconds later than the time given by If-Modified-Since. For example, let's assume PrimeSearcher found a prime exactly 869127442359 milliseconds since the beginning of the Disco Decade. This fact is told to the browser, but only to the nearest second:

Thu, 17-Jul-97 09:17:22 GMT

Now let's assume that the user reloads the page and the browser tells the server, via the If-Modified-Since header, the time it believes its cached page was last modified:

Thu, 17-Jul-97 09:17:22 GMT

Some servers have been known to receive this time, convert it to exactly 869127442000 milliseconds, find that this time is 359 milliseconds earlier than the time returned by getLastModified(), and falsely assume that the servlet's content has changed. This is why, to play it safe, getLastModified() should always round down to the nearest thousand milliseconds.

The HttpServletRequest object is passed to getLastModified() in case the servlet needs to base its results on information specific to the particular request. The generic bulletin board servlet can make use of this to determine which board was being requested, for example.



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.