001 /*
002 * Copyright 2006 Mat Gessel <mat.gessel@gmail.com>
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package asquare.gwt.tk.server;
017
018 import java.io.IOException;
019 import java.util.regex.Pattern;
020
021 import javax.servlet.*;
022 import javax.servlet.http.HttpServletRequest;
023 import javax.servlet.http.HttpServletResponse;
024
025 /**
026 * This is a filter which enforces proper caching of the generated GWT script
027 * files. It requires that serve your GWT application via a Java servlet
028 * container.
029 * <p>
030 * To use, add the jar to <code>WEB-INF/lib</code> and add the
031 * following to your deployment descriptor (web.xml):
032 *
033 * <pre>
034 * <filter>
035 * <filter-name>GWTCacheFilter</filter-name>
036 * <filter-class>asquare.gwt.tk.server.GWTCacheFilter</filter-class>
037 * <description>Enforces proper caching of GWT script files</description>
038 * </filter>
039 *
040 * <filter-mapping>
041 * <filter-name>GWTCacheFilter</filter-name>
042 * <url-pattern>*.html</url-pattern>
043 * </filter-mapping></pre>
044 *
045 * By default, files ending in <code>.cache.html</code> are cached and files
046 * ending in <code>.nocache.html</code> are not cached. You can override the
047 * defaults by specifying file name patterns in filter init-params. The pattern
048 * is parsed as a JDK regular expression. The defaults are below:
049 *
050 * <pre>
051 * <init-param>
052 * <param-name>forceDontCache</param-name>
053 * <param-value>.+\.nocache.html</param-value>
054 * </init-param>
055 * <init-param>
056 * <param-name>forceCache</param-name>
057 * <param-value>.+\.cache.html</param-value>
058 * </init-param></pre>
059 *
060 * <p>
061 * Usage notes
062 * <ul>
063 * <li>You can verify that the filter is being applied with Firefox's Web
064 * Developer Extension. Click Tools > Web Developer > Information > View
065 * Response Headers.
066 * <li>If you are running an Apache httpd/Jk/Tomcat server configuration you
067 * need to ensure that Tomcat is serving HTML files, otherwise the filter will
068 * not be applied.
069 * <li>One reason that this filter exists is that you cannot use <code>*.nocache.html</code> or
070 * <code>*.cache.html</code> for url patterns. According to the 2.3 servlet
071 * spec, an extension is defined as the characters after the <strong>last</strong>
072 * period.
073 * <li>The header is modified <em>before</em> passing control down the filter chain.
074 * </ul>
075 *
076 * @see <a
077 * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9">Cache-control
078 * directive</a>
079 * @see <a
080 * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21">Expires
081 * directive</a>
082 * @see <a
083 * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32">Pragma
084 * directive</a>
085 */
086 public class GWTCacheFilter implements Filter
087 {
088 /**
089 * The name of the filter init-param which specifies files not to cache.
090 * The name is <code>{@value}</code>.
091 */
092 public static final String INITPARAM_FORCEDONTCACHE = "forceDontCache";
093
094 /**
095 * The name of the filter init-param which specifies files to cache.
096 * The name is <code>{@value}</code>.
097 */
098 public static final String INITPARAM_FORCECACHE = "forceCache";
099
100 /**
101 * The default value of the <code>forceCache</code> init-param.
102 * The value is <code>{@value}</code>.
103 */
104 public static final String DEFAULT_FORCEDONTCACHE = ".+\\.nocache.html";
105
106 /**
107 * The default value of the <code>forceDontCache</code> init-param.
108 * The value is <code>{@value}</code>.
109 */
110 public static final String DEFAULT_FORCECACHE = ".+\\.cache.html";
111
112 private Pattern forceDontCachePattern;
113 private Pattern forceCachePattern;
114
115 /*
116 * (non-Javadoc)
117 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
118 */
119 public void init(FilterConfig filterConfig) throws ServletException
120 {
121 String forceDontCachePatternString = filterConfig.getInitParameter(INITPARAM_FORCEDONTCACHE);
122 String forceCachePatternString = filterConfig.getInitParameter(INITPARAM_FORCECACHE);
123 if (forceDontCachePatternString == null)
124 {
125 forceDontCachePatternString = DEFAULT_FORCEDONTCACHE;
126 }
127 if (forceCachePatternString == null)
128 {
129 forceCachePatternString = DEFAULT_FORCECACHE;
130 }
131 forceDontCachePattern = Pattern.compile(forceDontCachePatternString);
132 forceCachePattern = Pattern.compile(forceCachePatternString);
133 }
134
135 /*
136 * (non-Javadoc)
137 * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
138 */
139 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
140 {
141 if (request instanceof HttpServletRequest)
142 {
143 HttpServletRequest hRequest = (HttpServletRequest) request;
144 if (forceDontCachePattern.matcher(hRequest.getRequestURL()).matches())
145 {
146 HttpServletResponse hResponse = (HttpServletResponse) response;
147 hResponse.addHeader("Cache-Control", "no-cache no-store must-revalidate");
148 hResponse.addHeader("Pragma", "no-cache"); // HTTP/1.0
149 hResponse.setDateHeader("Expires", 0l);
150 }
151 else if (forceCachePattern.matcher(hRequest.getRequestURL()).matches())
152 {
153 HttpServletResponse hresponse = (HttpServletResponse) response;
154
155 // the w3c spec requires a maximum age of 1 year
156 hresponse.addHeader("Cache-Control", "max-age=31536000");
157 hresponse.setDateHeader("Expires", System.currentTimeMillis() + 31536000000l);
158 }
159 }
160 chain.doFilter(request, response);
161 }
162
163 /*
164 * (non-Javadoc)
165 * @see javax.servlet.Filter#destroy()
166 */
167 public void destroy()
168 {
169 }
170 }