DWR provides a convenient mechanism to execute server side java classes from javascript running in the browser. We use it extensively while developing Dekoh applications. Recently I noticed that when we use DWR, there are many requests to java-script files from the browser. It appeared that some DWR scripts are not being cached by the browser. I fired up firefox live http headers and noticed that inded some scripts were being downloaded with every page. We use DWR version 1.1.4. The headers for the repeat downloads looked like:
GET /dekohportal/dwr/interface/locationchooser.js HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: en,as;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Cache-Control: max-age=0 HTTP/1.x 200 OK Server: Pramati Server/5.0SP3 [Servlet/2.4 JSP/2.0] Date: Tue, 24 Jul 2007 05:29:20 GMT Transfer-Encoding: chunked Connection: Keep-Alive
Notice that in the response headers, there is no Last-Modified header. This is the reason why the browser is reloading the script on every page load. This script is a DWR interface script. When you expose java methods using DWR (through a create tag in the dwr-*.xml), DWR creates an interface javascript. This file implements javascript methods which invoke the remote java methods (using DWREngine._execute). The interface script does not change unless the methods exposed in the dwr-*.xml are changed and the application is restarted. Hence the script should have been cacheable. These scripts are generated by DefaultInterfaceProcessor in DWR. To enable caching, we need to change the InterfaceProcessor to add the Last-Modified header in the response and read the If-Modified-Since header in the request. DWR provides a nice mechanism to plugin custom implementation of processors. So instead of chaging the DefaultInterfaceProcessor (which is shipped with DWR), I subclassed the default implementation to support these new headers and plugged it in to our runtime. The code for custom interface processor looks like:
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import uk.ltd.getahead.dwr.impl.DefaultInterfaceProcessor; import uk.ltd.getahead.dwr.impl.HtmlConstants; /** * This class is the InterfaceProcessor for DWRHandler which * will add the functionality to add las modified header and * check if modified since header. * @author <a href="mailto:[email protected]" mce_href="mailto:[email protected]">Venkat Gunnu</a> * @since Jul 19, 2007 */ public class HttpCachingInterfaceProcessor extends DefaultInterfaceProcessor { //Store the application startup time. This will be the time we will set //as the Last-Modified time for all the interface scripts private final long lastUpdatedTime = (System.currentTimeMillis() / 1000) * 1000; public void handle(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { long ifModifiedSince = req.getDateHeader(HtmlConstants.HEADER_IF_MODIFIED); if (ifModifiedSince < lastUpdatedTime) { //If the browser does not have the script in the cache or the cached copy is stale //set the Last-Modified date header and send the new script file //Note: If the browser does not have the script in its cache ifModifiedSince will be -1 resp.setDateHeader(HtmlConstants.HEADER_LAST_MODIFIED, lastUpdatedTime); super.handle(req, resp); } else { //If the browser has current version of the file, dont send the script. Just say it has not changed resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } }
To plugin this new Interface Processor, we need to add init parameter for DWRServlet in web.xml. The snippet from web.xml would look like:
<servlet><servlet-name>dwr-invoker</servlet-name> <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class> <init-param> <param-name>interface</param-name> <param-value>util.dwr.HttpCachingInterfaceProcessor</param-value> </init-param> ... </servlet>
After adding the custom inerface processor following are the headers for the dwr interface request.
GET /dekohportal/dwr/interface/locationchooser.js HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en,as;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-aliveHTTP/1.x 200 OK
Server: Pramati Server/5.0SP3 [Servlet/2.4 JSP/2.0]
Date: Tue, 24 Jul 2007 05:49:43 GMT
Last-Modified: Tue, 24 Jul 2007 05:49:33 GMT
Transfer-Encoding: chunked
Connection: Keep-Alive
Notice that now for the first request, DWR is sending the Last-Modfied header. When we make the second request, the browser sends an “If-Modified-Since” header and DWR now sends a 304 NOT_MODIFIED response.
GET /dekohportal/dwr/interface/locationchooser.js HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: en,as;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive If-Modified-Since: Tue, 24 Jul 2007 05:49:33 GMT HTTP/1.x 304 NOT_MODIFIED Server: Pramati Server/5.0SP3 [Servlet/2.4 JSP/2.0] Date: Tue, 24 Jul 2007 05:50:42 GMT Content-Length: 0 Connection: Keep-Alive
Thus, by plugging in a custom interface processor, we can enable caching of interface java-scripts in DWR.
原文:http://blogs.dekoh.com/dev/2007/07/24/caching-dwr-interface-java-script-files/