Overriding the getLastModified() method in your HttpServlet

I wanted to learn how to the getLastModified() method in HttpServlet class works so i tried playing around with it and this is what i found. These are some of the methods related to getLastModified() from the default implementation of HttpServlet.java class


protected long getLastModified(HttpServletRequest req) {
return -1;
}

protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
}
else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}

private void maybeSetLastModified(HttpServletResponse resp,
long lastModified) {
if (resp.containsHeader(HEADER_LASTMOD))
return;
if (lastModified >= 0)
resp.setDateHeader(HEADER_LASTMOD, lastModified);
}



  1. getLastModified(): The default implementation of this method returns -1 as value, so if you dont override this method then the default implementation wont set Last-Modified header

  2. service() : The default implementation of service() method calls the getLastModified() method in two cases one is when it gets GET request and other is when it gets HEAD request. In both cases it checks if the value returned is not -1 then it sets the Last-Modified header with value returned by the method. In case of GET method it takes the value returned by getLastModified() method and compares it to value of the If-Modified-Since, to check if the response is actually changed, if no it wont even call the doGet() method of the servlet instead it will return HTTP 304 Not Modified response to browser

  3. maybeSetLastModified(): method checks if the Last-Modified is already set in the response if not it checks if its value is greater than 0 and sets it



Now i changed my ResourceServingServlet so that it returns value of getLastModified() equal to current time - 10 hours like this

package com.webspherenotes.performance;

import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Servlet implementation class ResourceServingServlet
*/
public class ResourceServingServlet extends HttpServlet {
private static final long serialVersionUID = 1L;


protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Entering ResourceServingServlet.doGet()");

System.out.println("Request path " + request.getPathInfo());
System.out.println("Query String " + request.getQueryString());
printRequestHeaders(request);
response.setContentType("application/javascript");
getServletContext().getRequestDispatcher("/js/test.js").include(request, response);
System.out.println("Exiting ResourceServingServlet.doGet()");
}

private void printRequestHeaders(HttpServletRequest request){
System.out.println("************** Request Header ****************");
Enumeration headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()){
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
System.out.println(headerName + " = " + headerValue);
}
System.out.println("************** *********** ****************");
}

protected long getLastModified(HttpServletRequest req) {
System.out.println("Inside ResourceServingServlet.getLastModified() ");
long modifiedDate = System.currentTimeMillis() - (3600*1000*1);
System.out.println("Returning long time " + new Date(modifiedDate) );
return modifiedDate;
}

}


If i clean my browser cache and go to the index.jsp page for the first time at say 14th of July 19.27 GMT it will make a request to get test.js file and with value of Last-Modified 14th of July 18.27 GMT, after that whenever i make request to test.js it will send a If-Modified-Since header with value equal to July 18.27 GMT, asking ResourceServingServlet, if its response has changed after 18.27, if no the servlet will return with only headers without body like this





Setting the value of Last-Modified in case when you want to return a large HTML can give you a big performance boost because it wont have to execute the same doGet() method logic again and again and calculate and transmit the same response back to client again and again

No comments: