|
@@ -1,12 +1,12 @@ |
1 |
1 |
|
// This Source Code is in the Public Domain per: http://litesoft.org/License.txt |
2 |
2 |
|
package org.litesoft.servlet.versionedstaticcontentfilter; |
3 |
3 |
|
|
4 |
|
- |
import java.io.*; |
5 |
|
- |
import java.util.*; |
|
4 |
+ |
import org.litesoft.servlet.*; |
|
5 |
+ |
|
6 |
6 |
|
import javax.servlet.*; |
7 |
7 |
|
import javax.servlet.http.*; |
8 |
|
- |
|
9 |
|
- |
import org.litesoft.servlet.*; |
|
8 |
+ |
import java.io.*; |
|
9 |
+ |
import java.util.*; |
10 |
10 |
|
|
11 |
11 |
|
/** |
12 |
12 |
|
* This is a filter which enforces proper caching of a "LiteSoft Versioned" web app |
|
@@ -60,23 +60,25 @@ |
60 |
60 |
|
* </ul> |
61 |
61 |
|
* |
62 |
62 |
|
* @see <a |
63 |
|
- |
* href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9">Cache-control |
64 |
|
- |
* directive</a> |
|
63 |
+ |
* href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9">Cache-control |
|
64 |
+ |
* directive</a> |
65 |
65 |
|
* @see <a |
66 |
|
- |
* href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21">Expires |
67 |
|
- |
* directive</a> |
|
66 |
+ |
* href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21">Expires |
|
67 |
+ |
* directive</a> |
68 |
68 |
|
* @see <a |
69 |
|
- |
* href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32">Pragma |
70 |
|
- |
* directive</a> |
|
69 |
+ |
* href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32">Pragma |
|
70 |
+ |
* directive</a> |
71 |
71 |
|
*/ |
72 |
72 |
|
public class VersionedStaticContentFilter implements Filter |
73 |
73 |
|
{ |
|
74 |
+ |
public static final String VERSION_PATH_ID = "/ver"; |
|
75 |
+ |
|
74 |
76 |
|
@Override |
75 |
77 |
|
public void init( FilterConfig filterConfig ) |
76 |
78 |
|
throws ServletException |
77 |
79 |
|
{ |
78 |
80 |
|
mServletContext = filterConfig.getServletContext(); |
79 |
|
- |
String zLocationAnchorPath = WebInfLocator.getLocationAnchorPath( this ); |
|
81 |
+ |
String zLocationAnchorPath = WebInfLocator.getLocationAnchorPath(); |
80 |
82 |
|
String zDevMode = System.getProperty( "DevMode" ); |
81 |
83 |
|
if ( zDevMode == null ) |
82 |
84 |
|
{ |
|
@@ -133,7 +135,8 @@ |
133 |
135 |
|
|
134 |
136 |
|
private interface Mode |
135 |
137 |
|
{ |
136 |
|
- |
void processGetRequest( String pRequestURI, HttpServletRequest pRequest, HttpServletResponse pResponse, ServletContext pServletContext, FilterChain pChain ) |
|
138 |
+ |
void processGetRequest( String pRequestURI, HttpServletRequest pRequest, HttpServletResponse pResponse, ServletContext pServletContext, |
|
139 |
+ |
FilterChain pChain ) |
137 |
140 |
|
throws IOException, ServletException; |
138 |
141 |
|
} |
139 |
142 |
|
|
|
@@ -166,56 +169,63 @@ |
166 |
169 |
|
public static class Production implements Mode |
167 |
170 |
|
{ |
168 |
171 |
|
@Override |
169 |
|
- |
public final void processGetRequest( String pRequestURI, HttpServletRequest pRequest, HttpServletResponse pResponse, ServletContext pServletContext, FilterChain pChain ) |
|
172 |
+ |
public final void processGetRequest( String pRequestURI, HttpServletRequest pRequest, HttpServletResponse pResponse, ServletContext pServletContext, |
|
173 |
+ |
FilterChain pChain ) |
170 |
174 |
|
throws IOException, ServletException |
171 |
175 |
|
{ |
172 |
|
- |
pRequestURI = pRequestURI.trim(); |
173 |
|
- |
if ( shouldNotCache( getLowerCaseExtension( pRequestURI ) ) ) |
|
176 |
+ |
String zURI = cleanRequest( pRequestURI = pRequestURI.trim() ); |
|
177 |
+ |
if ( shouldCacheExtensions( getLowerCaseExtension( zURI ) ) && isVersionedPath( zURI ) ) |
174 |
178 |
|
{ |
175 |
|
- |
noCache( pResponse ); |
|
179 |
+ |
cacheForever( pResponse ); |
176 |
180 |
|
} |
177 |
181 |
|
else |
178 |
182 |
|
{ |
179 |
|
- |
cacheForever( pResponse ); // Everything else is cache "forever". |
|
183 |
+ |
noCache( pResponse ); |
180 |
184 |
|
} |
181 |
|
- |
delegate( pRequestURI, adjustedURI4VersionedCommon( pRequestURI ), pRequest, pResponse, pServletContext, pChain ); |
|
185 |
+ |
delegate( pRequestURI, pRequest, pResponse, pServletContext, pChain ); |
182 |
186 |
|
} |
183 |
187 |
|
|
184 |
|
- |
protected boolean shouldNotCache( String pLowerCaseExtension ) |
|
188 |
+ |
private boolean isVersionedPath( String pCleanedRequestURI ) |
185 |
189 |
|
{ |
186 |
|
- |
return "html".equals( pLowerCaseExtension ); |
|
190 |
+ |
if ( pCleanedRequestURI.startsWith( VERSION_PATH_ID ) ) // Starts w/ == Happy Case! |
|
191 |
+ |
{ |
|
192 |
+ |
return true; |
|
193 |
+ |
} |
|
194 |
+ |
int zAt = pCleanedRequestURI.indexOf( VERSION_PATH_ID ); |
|
195 |
+ |
return (zAt != -1) && // Has it in path |
|
196 |
+ |
(0 == pCleanedRequestURI.substring( 0, zAt ).lastIndexOf( '/' )); // AND is first sub-path (first being the WAR/directory name) |
|
197 |
+ |
} |
|
198 |
+ |
|
|
199 |
+ |
protected boolean shouldCacheExtensions( String pLowerCaseExtension ) |
|
200 |
+ |
{ |
|
201 |
+ |
return !"html".equals( pLowerCaseExtension ); |
187 |
202 |
|
} |
188 |
203 |
|
|
189 |
|
- |
protected String getLowerCaseExtension( String pRequestURI ) |
|
204 |
+ |
protected String cleanRequest( String pRequestURI ) |
190 |
205 |
|
{ |
191 |
|
- |
String zURI; |
192 |
206 |
|
int at; |
193 |
|
- |
if (-1 != (at = pRequestURI.indexOf( '?' ))) |
|
207 |
+ |
if ( -1 != (at = pRequestURI.indexOf( '?' )) ) |
194 |
208 |
|
{ |
195 |
|
- |
zURI = pRequestURI.substring(0, at); |
|
209 |
+ |
return pRequestURI.substring( 0, at ); |
196 |
210 |
|
} |
197 |
|
- |
else if (-1 != (at = pRequestURI.indexOf( '#' ))) |
|
211 |
+ |
if ( -1 != (at = pRequestURI.indexOf( '#' )) ) |
198 |
212 |
|
{ |
199 |
|
- |
zURI = pRequestURI.substring(0, at); |
|
213 |
+ |
return pRequestURI.substring( 0, at ); |
200 |
214 |
|
} |
201 |
|
- |
else |
202 |
|
- |
{ |
203 |
|
- |
zURI = pRequestURI; |
204 |
|
- |
} |
205 |
|
- |
return (-1 == (at = zURI.lastIndexOf('.'))) ? "" : pRequestURI.substring( at + 1 ).toLowerCase(); |
|
215 |
+ |
return pRequestURI; |
206 |
216 |
|
} |
207 |
217 |
|
|
208 |
|
- |
protected void delegate( String pRequestURI, String pAdjustedURI, HttpServletRequest pRequest, HttpServletResponse pResponse, ServletContext pServletContext, FilterChain pChain ) |
|
218 |
+ |
protected String getLowerCaseExtension( String pCleanedRequestURI ) |
|
219 |
+ |
{ |
|
220 |
+ |
int at = pCleanedRequestURI.lastIndexOf( '.' ); |
|
221 |
+ |
return (-1 == at) ? "" : pCleanedRequestURI.substring( at + 1 ).toLowerCase(); |
|
222 |
+ |
} |
|
223 |
+ |
|
|
224 |
+ |
protected void delegate( String pRequestURI, HttpServletRequest pRequest, HttpServletResponse pResponse, |
|
225 |
+ |
ServletContext pServletContext, FilterChain pChain ) |
209 |
226 |
|
throws ServletException, IOException |
210 |
227 |
|
{ |
211 |
|
- |
if ( pAdjustedURI != null ) |
212 |
|
- |
{ |
213 |
|
- |
pRequest.getRequestDispatcher( pAdjustedURI ).forward( pRequest, pResponse ); |
214 |
|
- |
} |
215 |
|
- |
else |
216 |
|
- |
{ |
217 |
|
- |
pChain.doFilter( pRequest, pResponse ); // Goes to default servlet. |
218 |
|
- |
} |
|
228 |
+ |
pChain.doFilter( pRequest, pResponse ); // Goes to default servlet. |
219 |
229 |
|
} |
220 |
230 |
|
|
221 |
231 |
|
@Override |
|
@@ -228,10 +238,9 @@ |
228 |
238 |
|
public static class DevMode extends Production |
229 |
239 |
|
{ |
230 |
240 |
|
@Override |
231 |
|
- |
protected boolean shouldNotCache( String pLowerCaseExtension ) |
|
241 |
+ |
protected boolean shouldCacheExtensions( String pLowerCaseExtension ) |
232 |
242 |
|
{ |
233 |
|
- |
return super.shouldNotCache( pLowerCaseExtension ) |
234 |
|
- |
|| "js".equalsIgnoreCase( pLowerCaseExtension ) || "css".equalsIgnoreCase( pLowerCaseExtension ); |
|
243 |
+ |
return super.shouldCacheExtensions( pLowerCaseExtension ) && !"js".equals( pLowerCaseExtension ) && !"css".equals( pLowerCaseExtension ); |
235 |
244 |
|
} |
236 |
245 |
|
|
237 |
246 |
|
@Override |
|
@@ -251,10 +260,11 @@ |
251 |
260 |
|
} |
252 |
261 |
|
|
253 |
262 |
|
@Override |
254 |
|
- |
protected void delegate( String pRequestURI, String pAdjustedURI, HttpServletRequest pRequest, HttpServletResponse pResponse, ServletContext pServletContext, FilterChain pChain ) |
|
263 |
+ |
protected void delegate( String pRequestURI, HttpServletRequest pRequest, HttpServletResponse pResponse, |
|
264 |
+ |
ServletContext pServletContext, FilterChain pChain ) |
255 |
265 |
|
throws ServletException, IOException |
256 |
266 |
|
{ |
257 |
|
- |
File zFoundFile = findFile( pRequestURI, pAdjustedURI ); |
|
267 |
+ |
File zFoundFile = findFile( pRequestURI ); |
258 |
268 |
|
if ( zFoundFile == null ) |
259 |
269 |
|
{ |
260 |
270 |
|
pResponse.sendError( HttpServletResponse.SC_NOT_FOUND ); |
|
@@ -299,11 +309,11 @@ |
299 |
309 |
|
} |
300 |
310 |
|
} |
301 |
311 |
|
|
302 |
|
- |
private File findFile( String pRequestURI, String pAdjustedURI ) |
|
312 |
+ |
private File findFile( String pRequestURI ) |
303 |
313 |
|
{ |
304 |
314 |
|
for ( File zPath : mStaticFileSearchPaths ) |
305 |
315 |
|
{ |
306 |
|
- |
File zFound = checkPath( zPath, pRequestURI, pAdjustedURI ); |
|
316 |
+ |
File zFound = checkPath( zPath, pRequestURI ); |
307 |
317 |
|
if ( zFound != null ) |
308 |
318 |
|
{ |
309 |
319 |
|
System.out.println( "VersionedStaticContentFilter.FindFile (200): " + pRequestURI + " -> " + zFound.getPath() ); |
|
@@ -326,38 +336,6 @@ |
326 |
336 |
|
} |
327 |
337 |
|
} |
328 |
338 |
|
|
329 |
|
- |
private static String adjustedURI4VersionedCommon( String pRequestURI ) |
330 |
|
- |
{ |
331 |
|
- |
int at = pRequestURI.indexOf( "/common/" ); |
332 |
|
- |
if ( at != -1 ) |
333 |
|
- |
{ |
334 |
|
- |
String zBeforeSlashCommon = pRequestURI.substring( 0, at ); |
335 |
|
- |
int zSlashVat = zBeforeSlashCommon.lastIndexOf( "/v" ); |
336 |
|
- |
if ( (zSlashVat != -1) && isDigits( zBeforeSlashCommon.substring( zSlashVat + 2 ) ) ) |
337 |
|
- |
{ |
338 |
|
- |
return pRequestURI.substring( at ); |
339 |
|
- |
} |
340 |
|
- |
} |
341 |
|
- |
return null; |
342 |
|
- |
} |
343 |
|
- |
|
344 |
|
- |
private static boolean isDigits( String pString ) |
345 |
|
- |
{ |
346 |
|
- |
if ( pString.length() == 0 ) |
347 |
|
- |
{ |
348 |
|
- |
return false; |
349 |
|
- |
} |
350 |
|
- |
for ( int i = 0; i < pString.length(); i++ ) |
351 |
|
- |
{ |
352 |
|
- |
char c = pString.charAt( i ); |
353 |
|
- |
if ( (c < '0') || ('9' < c) ) |
354 |
|
- |
{ |
355 |
|
- |
return false; |
356 |
|
- |
} |
357 |
|
- |
} |
358 |
|
- |
return true; |
359 |
|
- |
} |
360 |
|
- |
|
361 |
339 |
|
private static void closeQuietly( Closeable pClosable ) |
362 |
340 |
|
{ |
363 |
341 |
|
if ( pClosable != null ) |
|
@@ -384,12 +362,6 @@ |
384 |
362 |
|
} |
385 |
363 |
|
} |
386 |
364 |
|
|
387 |
|
- |
private static File checkPath( File pPath, String pSubPath1, String pSubPath2 ) |
388 |
|
- |
{ |
389 |
|
- |
File zFound = checkPath( pPath, pSubPath1 ); |
390 |
|
- |
return (zFound != null) ? zFound : checkPath( pPath, pSubPath2 ); |
391 |
|
- |
} |
392 |
|
- |
|
393 |
365 |
|
private static File checkPath( File pPath, String pSubPath ) |
394 |
366 |
|
{ |
395 |
367 |
|
if ( pSubPath == null || pSubPath.length() == 0 ) |