1 5 package com.opensymphony.oscache.web.filter; 6 7 import com.opensymphony.oscache.base.Cache; 8 import com.opensymphony.oscache.base.EntryRefreshPolicy; 9 import com.opensymphony.oscache.base.NeedsRefreshException; 10 import com.opensymphony.oscache.web.ServletCacheAdministrator; 11 12 import org.apache.commons.logging.Log; 13 import org.apache.commons.logging.LogFactory; 14 15 import java.io.IOException ; 16 17 import javax.servlet.*; 18 import javax.servlet.http.HttpServletRequest ; 19 import javax.servlet.http.HttpServletResponse ; 20 import javax.servlet.jsp.PageContext ; 21 22 32 public class CacheFilter implements Filter, ICacheKeyProvider, ICacheGroupsProvider { 33 public static final String HEADER_LAST_MODIFIED = "Last-Modified"; 35 public static final String HEADER_CONTENT_TYPE = "Content-Type"; 36 public static final String HEADER_CONTENT_ENCODING = "Content-Encoding"; 37 public static final String HEADER_EXPIRES = "Expires"; 38 public static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since"; 39 public static final String HEADER_CACHE_CONTROL = "Cache-Control"; 40 public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"; 41 42 public static final int FRAGMENT_AUTODETECT = -1; 44 public static final int FRAGMENT_NO = 0; 45 public static final int FRAGMENT_YES = 1; 46 47 public static final int NOCACHE_OFF = 0; 49 public static final int NOCACHE_SESSION_ID_IN_URL = 1; 50 51 public static final long LAST_MODIFIED_OFF = 0; 53 public static final long LAST_MODIFIED_ON = 1; 54 public static final long LAST_MODIFIED_INITIAL = -1; 55 56 public static final long EXPIRES_OFF = 0; 58 public static final long EXPIRES_ON = 1; 59 public static final long EXPIRES_TIME = -1; 60 61 public static final long MAX_AGE_NO_INIT = Long.MIN_VALUE; 63 public static final long MAX_AGE_TIME = Long.MAX_VALUE; 64 65 private final static String REQUEST_FILTERED = "__oscache_filtered"; 67 68 private EntryRefreshPolicy expiresRefreshPolicy; 70 71 private final Log log = LogFactory.getLog(this.getClass()); 73 74 private FilterConfig config; 76 private ServletCacheAdministrator admin = null; 77 private int cacheScope = PageContext.APPLICATION_SCOPE; private int fragment = FRAGMENT_AUTODETECT; private int time = 60 * 60; private String cron = null; private int nocache = NOCACHE_OFF; private long lastModified = LAST_MODIFIED_INITIAL; private long expires = EXPIRES_ON; private long cacheControlMaxAge = -60; private ICacheKeyProvider cacheKeyProvider = this; private ICacheGroupsProvider cacheGroupsProvider = this; 88 91 public void destroy() { 92 } 94 95 107 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { 108 if (log.isInfoEnabled()) { 109 log.info("<cache>: filter in scope " + cacheScope); 110 } 111 112 if (isFilteredBefore(request) || !isCacheable(request)) { 114 chain.doFilter(request, response); 115 return; 116 } 117 request.setAttribute(REQUEST_FILTERED, Boolean.TRUE); 118 119 HttpServletRequest httpRequest = (HttpServletRequest ) request; 120 121 boolean fragmentRequest = isFragment(httpRequest); 123 124 Cache cache; 126 if (cacheScope == PageContext.SESSION_SCOPE) { 127 cache = admin.getSessionScopeCache(httpRequest.getSession(true)); 128 } else { 129 cache = admin.getAppScopeCache(config.getServletContext()); 130 } 131 132 String key = cacheKeyProvider.createCacheKey(httpRequest, admin, cache); 134 135 try { 136 ResponseContent respContent = (ResponseContent) cache.getFromCache(key, time, cron); 137 138 if (log.isInfoEnabled()) { 139 log.info("<cache>: Using cached entry for " + key); 140 } 141 142 boolean acceptsGZip = false; 143 if ((!fragmentRequest) && (lastModified != LAST_MODIFIED_OFF)) { 144 long clientLastModified = httpRequest.getDateHeader(HEADER_IF_MODIFIED_SINCE); 146 if ((clientLastModified != -1) && (clientLastModified >= respContent.getLastModified())) { 149 ((HttpServletResponse ) response).setStatus(HttpServletResponse.SC_NOT_MODIFIED); 150 return; 151 } 152 153 acceptsGZip = respContent.isContentGZiped() && acceptsGZipEncoding(httpRequest); 154 } 155 156 respContent.writeTo(response, fragmentRequest, acceptsGZip); 157 } catch (NeedsRefreshException nre) { 160 boolean updateSucceeded = false; 161 162 try { 163 if (log.isInfoEnabled()) { 164 log.info("<cache>: New cache entry, cache stale or cache scope flushed for " + key); 165 } 166 167 CacheHttpServletResponseWrapper cacheResponse = new CacheHttpServletResponseWrapper((HttpServletResponse ) response, fragmentRequest, time * 1000L, lastModified, expires, cacheControlMaxAge); 168 chain.doFilter(request, cacheResponse); 169 cacheResponse.flushBuffer(); 170 171 if (isCacheable(cacheResponse)) { 173 String [] groups = cacheGroupsProvider.createCacheGroups(httpRequest, admin, cache); 175 cache.putInCache(key, cacheResponse.getContent(), groups, expiresRefreshPolicy, null); 177 updateSucceeded = true; 178 } 179 } finally { 180 if (!updateSucceeded) { 181 cache.cancelUpdate(key); 182 } 183 } 184 } 185 } 186 187 237 public void init(FilterConfig filterConfig) { 238 config = filterConfig; 240 admin = ServletCacheAdministrator.getInstance(config.getServletContext()); 241 242 try { 244 time = Integer.parseInt(config.getInitParameter("time")); 245 } catch (Exception e) { 246 log.info("Could not get init parameter 'time', defaulting to one hour."); 247 } 248 249 expiresRefreshPolicy = new ExpiresRefreshPolicy(time); 251 252 try { 254 String scopeString = config.getInitParameter("scope"); 255 256 if (scopeString.equals("session")) { 257 cacheScope = PageContext.SESSION_SCOPE; 258 } else if (scopeString.equals("application")) { 259 cacheScope = PageContext.APPLICATION_SCOPE; 260 } else if (scopeString.equals("request")) { 261 cacheScope = PageContext.REQUEST_SCOPE; 262 } else if (scopeString.equals("page")) { 263 cacheScope = PageContext.PAGE_SCOPE; 264 } 265 } catch (Exception e) { 266 log.info("Could not get init parameter 'scope', defaulting to 'application'."); 267 } 268 269 cron = config.getInitParameter("cron"); 271 272 try { 274 String fragmentString = config.getInitParameter("fragment"); 275 276 if (fragmentString.equals("no")) { 277 fragment = FRAGMENT_NO; 278 } else if (fragmentString.equals("yes")) { 279 fragment = FRAGMENT_YES; 280 } else if (fragmentString.equalsIgnoreCase("auto")) { 281 fragment = FRAGMENT_AUTODETECT; 282 } 283 } catch (Exception e) { 284 log.info("Could not get init parameter 'fragment', defaulting to 'auto detect'."); 285 } 286 287 try { 289 String nocacheString = config.getInitParameter("nocache"); 290 291 if (nocacheString.equals("off")) { 292 nocache = NOCACHE_OFF; 293 } else if (nocacheString.equalsIgnoreCase("sessionIdInURL")) { 294 nocache = NOCACHE_SESSION_ID_IN_URL; 295 } 296 } catch (Exception e) { 297 log.info("Could not get init parameter 'nocache', defaulting to 'off'."); 298 } 299 300 try { 302 String lastModifiedString = config.getInitParameter("lastModified"); 303 304 if (lastModifiedString.equals("off")) { 305 lastModified = LAST_MODIFIED_OFF; 306 } else if (lastModifiedString.equals("on")) { 307 lastModified = LAST_MODIFIED_ON; 308 } else if (lastModifiedString.equalsIgnoreCase("initial")) { 309 lastModified = LAST_MODIFIED_INITIAL; 310 } 311 } catch (Exception e) { 312 log.info("Could not get init parameter 'lastModified', defaulting to 'initial'."); 313 } 314 315 try { 317 String expiresString = config.getInitParameter("expires"); 318 319 if (expiresString.equals("off")) { 320 expires = EXPIRES_OFF; 321 } else if (expiresString.equals("on")) { 322 expires = EXPIRES_ON; 323 } else if (expiresString.equalsIgnoreCase("time")) { 324 expires = EXPIRES_TIME; 325 } 326 } catch (Exception e) { 327 log.info("Could not get init parameter 'expires', defaulting to 'on'."); 328 } 329 330 try { 332 String cacheControlMaxAgeString = config.getInitParameter("max-age"); 333 334 if (cacheControlMaxAgeString.equals("no init")) { 335 cacheControlMaxAge = MAX_AGE_NO_INIT; 336 } else if (cacheControlMaxAgeString.equals("time")) { 337 cacheControlMaxAge = MAX_AGE_TIME; 338 } else { 339 cacheControlMaxAge = Long.parseLong(cacheControlMaxAgeString); 340 if (cacheControlMaxAge >= 0) { 341 cacheControlMaxAge = - cacheControlMaxAge; 343 } else { 344 log.warn("Init parameter 'max-age' must be at least a positive integer, defaulting to 'time'. "); 345 cacheControlMaxAge = 60; 346 } 347 } 348 } catch (Exception e) { 349 log.info("Could not get init parameter 'max-age', defaulting to 'time'."); 350 } 351 352 ICacheKeyProvider cacheKeyProvider = (ICacheKeyProvider)instantiateFromInitParam("ICacheKeyProvider", ICacheKeyProvider.class, this.getClass().getName()); 354 if (cacheKeyProvider != null) { 355 this.cacheKeyProvider = cacheKeyProvider; 356 } 357 358 ICacheGroupsProvider cacheGroupsProvider = (ICacheGroupsProvider)instantiateFromInitParam("ICacheGroupsProvider", ICacheGroupsProvider.class, this.getClass().getName()); 360 if (cacheGroupsProvider != null) { 361 this.cacheGroupsProvider = cacheGroupsProvider; 362 } 363 364 EntryRefreshPolicy expiresRefreshPolicy = (EntryRefreshPolicy)instantiateFromInitParam("EntryRefreshPolicy", EntryRefreshPolicy.class, this.expiresRefreshPolicy.getClass().getName()); 366 if (expiresRefreshPolicy != null) { 367 this.expiresRefreshPolicy = expiresRefreshPolicy; 368 } 369 } 370 371 private Object instantiateFromInitParam(String classInitParam, Class interfaceClass, String defaultClassName) { 372 String className = config.getInitParameter(classInitParam); 373 if (className == null) { 374 log.info("Could not get init parameter '" + classInitParam + "', defaulting to " + defaultClassName + "."); 375 return null; 376 } else { 377 try { 378 Class clazz = Class.forName(className); 379 if (!interfaceClass.isAssignableFrom(clazz)) { 380 log.error("Specified class '" + className + "' does not implement" + interfaceClass.getName() + ". Using default " + defaultClassName + "."); 381 return null; 382 } else { 383 return clazz.newInstance(); 384 } 385 } catch (ClassNotFoundException e) { 386 log.error("Class '" + className + "' not found. Defaulting to " + defaultClassName + ".", e); 387 } catch (InstantiationException e) { 388 log.error("Class '" + className + "' could not be instantiated because it is not a concrete class. Using default class " + defaultClassName + ".", e); 389 } catch (IllegalAccessException e) { 390 log.error("Class '"+ className+ "' could not be instantiated because it is not public. Using default class " + defaultClassName + ".", e); 391 } 392 return null; 393 } 394 } 395 396 399 public String createCacheKey(HttpServletRequest httpRequest, ServletCacheAdministrator scAdmin, Cache cache) { 400 return scAdmin.generateEntryKey(null, httpRequest, cacheScope); 401 } 402 403 406 public String [] createCacheGroups(HttpServletRequest httpRequest, ServletCacheAdministrator scAdmin, Cache cache) { 407 return null; 408 } 409 410 422 protected boolean isFragment(HttpServletRequest request) { 423 if (fragment == FRAGMENT_AUTODETECT) { 424 return request.getAttribute("javax.servlet.include.request_uri") != null; 425 } else { 426 return (fragment == FRAGMENT_NO) ? false : true; 427 } 428 } 429 430 439 protected boolean isFilteredBefore(ServletRequest request) { 440 return request.getAttribute(REQUEST_FILTERED) != null; 441 } 442 443 450 protected boolean isCacheable(ServletRequest request) { 451 boolean cachable = request instanceof HttpServletRequest ; 453 454 if (cachable) { 455 HttpServletRequest requestHttp = (HttpServletRequest ) request; 456 if (nocache == NOCACHE_SESSION_ID_IN_URL) { cachable = !requestHttp.isRequestedSessionIdFromURL(); 458 } 459 } 460 461 if (log.isDebugEnabled()) { 462 log.debug("<cache>: the request " + ((cachable) ? "is" : "is not") + " cachable."); 463 } 464 465 return cachable; 466 } 467 468 475 protected boolean isCacheable(CacheHttpServletResponseWrapper cacheResponse) { 476 boolean cachable = cacheResponse.getStatus() == HttpServletResponse.SC_OK; 479 480 if (log.isDebugEnabled()) { 481 log.debug("<cache>: the response " + ((cachable) ? "is" : "is not") + " cachable."); 482 } 483 484 return cachable; 485 } 486 487 493 protected boolean acceptsGZipEncoding(HttpServletRequest request) { 494 String acceptEncoding = request.getHeader(HEADER_ACCEPT_ENCODING); 495 return (acceptEncoding != null) && (acceptEncoding.indexOf("gzip") != -1); 496 } 497 498 } 499 | Popular Tags |