litesoft
@ 364
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
// This Source Code is in the Public Domain per: http://litesoft.org/License.txt package org.litesoft.servlet.versionedstaticcontentfilter; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; /** * This is a filter which enforces proper caching of a "LiteSoft Versioned" web app * w/ "common" redirection. It requires that you serve your GWT application via a * Java servlet container. * <p/> * Many thanks to Mat Gessel <mat.gessel@gmail.com> for providing an implementation of * the standard GWT Caching mechanism as a basis. * <p/> * To use, add the jar to <code>WEB-INF/lib</code> and add the * following to your deployment descriptor (web.xml): * <p/> * <pre> * <filter> * <filter-name>VersionedStaticContentFilter</filter-name> * <filter-class>org.litesoft.servlet.versionedstaticcontentfilter.VersionedStaticContentFilter</filter-class> * </filter> * * <filter-mapping> * <filter-name>VersionedStaticContentFilter</filter-name> * <url-pattern>/*</url-pattern> * </filter-mapping></pre> * * A "LiteSoft Versioned" web app is one where the majority of the DOM is built via * JavaScript (e.g. a GWT app) and that the path has a version number in it (e.g. /v1/). * This means that everything can be cached "forever" EXCEPT certain JavaScript (e.g. * the versioned bootstrap JavaScript). So this code set the caching headers to "forever" * for everything EXCEPT files that end with ".nocache.js". * * Additionally, it maps requests to a common path under the versioned path to just the * common path (e.g. "/v1/common/images/fred.jpg" becomes "/common/images/fred.jpg"). This * makes it so that when the app is "versioned", all the common resources do not need to be * duplicated for each version. Note: this also means that older versions will be accessing * the newer resources, but as the proper behavior of the older versions (in a "LiteSoft * Versioned" web app) are to immediately forward to the current version, there should be * little to no deleterious effects. * * Usage notes * <ul> * <li>You can verify that the filter is being applied with Firefox's Web * Developer Extension. Click Tools > Web Developer > Information > View * Response Headers. * <li>If you are running an Apache httpd/Jk/Tomcat server configuration you * need to ensure that Tomcat is serving HTML files, otherwise the filter will * not be applied. * <li>One reason that this filter exists is that you cannot use <code>*.nocache.html</code> or * <code>*.cache.html</code> for url patterns. According to the 2.3 servlet * spec, an extension is defined as the characters after the <strong>last</strong> * period. * <li>The header is modified <em>before</em> passing control down the filter chain. * </ul> * * @see <a * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9">Cache-control * directive</a> * @see <a * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21">Expires * directive</a> * @see <a * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32">Pragma * directive</a> */ @SuppressWarnings({"UnusedDeclaration"}) public class VersionedStaticContentFilter implements Filter { @Override public void init( FilterConfig filterConfig ) throws ServletException { } @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException { if ( (request instanceof HttpServletRequest) && (response instanceof HttpServletResponse) ) { HttpServletResponse zResponse = (HttpServletResponse) response; HttpServletRequest zRequest = (HttpServletRequest) request; String zURI = zRequest.getRequestURI(); if ( zURI.endsWith( ".nocache.js" ) ) { zResponse.setHeader( "Cache-Control", "no-cache no-store must-revalidate" ); zResponse.setHeader( "Pragma", "no-cache" ); // HTTP/1.0 zResponse.setDateHeader( "Expires", 86400000 ); // January 2, 1970 } else { // Everything else is cache "forever". // // the w3c spec requires a maximum age of 1 year // Firefox 3+ needs 'public' to cache this resource when received via SSL zResponse.setHeader( "Cache-Control", "public max-age=31536000" ); // necessary to overwrite "Pragma: no-cache" header zResponse.setHeader( "Pragma", "temp" ); zResponse.setHeader( "Pragma", "" ); zResponse.setDateHeader( "Expires", System.currentTimeMillis() + 31536000000l ); } // System.out.println( "VersionedStaticContentFilter.doFilter: " + zURI ); int at = zURI.indexOf( "/common/" ); if ( (at != -1) && zURI.startsWith( "/v" ) ) { if ( isDigits( zURI.substring( 2, at ) ) ) { request.getRequestDispatcher( zURI.substring( at ) ).forward( request, response ); return; } } } chain.doFilter( request, response ); // Goes to default servlet. } private boolean isDigits( String pString ) { if ( pString.length() == 0 ) { return false; } for ( int i = 0; i < pString.length(); i++ ) { char c = pString.charAt( i ); if ( (c < '0') || ('9' < c) ) { return false; } } return true; } @Override public void destroy() { } } |