Clover coverage report - Coverage timestamp: Sa Jul 7 2007 09:11:40 CEST FRAMES NO FRAMES |
file stats: | LOC: | 782 |  | Methods: | 37
| NCLOC: | 393 |  | Classes: | 1
Source file | Conditionals | Statements | Methods | TOTAL
CacheFilter.java | 0% | 0% | 0% | 0% |

1 |  | /*
2 |  | * Copyright (c) 2002-2007 by OpenSymphony
3 |  | * All rights reserved.
4 |  | */
5 |  | package com.opensymphony.oscache.web.filter;
6 |  |
7 |  | import com.opensymphony.oscache.base.Cache;
8 |  | import com.opensymphony.oscache.base.Config;
9 |  | import com.opensymphony.oscache.base.EntryRefreshPolicy;
10 |  | import com.opensymphony.oscache.base.NeedsRefreshException;
11 |  | import com.opensymphony.oscache.util.ClassLoaderUtil;
12 |  | import com.opensymphony.oscache.util.StringUtil;
13 |  | import com.opensymphony.oscache.web.ServletCacheAdministrator;
14 |  |
15 |  | import org.apache.commons.logging.Log;
16 |  | import org.apache.commons.logging.LogFactory;
17 |  |
18 |  | import java.io.IOException;
19 |  | import java.util.List;
20 |  | import java.util.Properties;
21 |  |
22 |  | import javax.servlet.*;
23 |  | import javax.servlet.http.HttpServletRequest;
24 |  | import javax.servlet.http.HttpServletResponse;
25 |  | import javax.servlet.jsp.PageContext;
26 |  |
27 |  | /**
28 |  | * CacheFilter is a filter that allows for server-side caching of post-processed servlet content.<p>
29 |  | *
30 |  | * It also gives great programatic control over refreshing, flushing and updating the cache.<p>
31 |  | *
32 |  | * @author <a href="mailto:sergek [ AT ] lokitech.com">Serge Knystautas</a>
33 |  | * @author <a href="mailto:mike [ AT ] atlassian.com">Mike Cannon-Brookes</a>
34 |  | * @author <a href="mailto:ltorunski [ AT ] t-online.de">Lars Torunski</a>
35 |  | * @version $Revision: 434 $
36 |  | */
37 |  | public class CacheFilter implements Filter, ICacheKeyProvider, ICacheGroupsProvider {
38 |  | // Header
39 |  | public static final String HEADER_LAST_MODIFIED = "Last-Modified";
40 |  | public static final String HEADER_CONTENT_TYPE = "Content-Type";
41 |  | public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
42 |  | public static final String HEADER_EXPIRES = "Expires";
43 |  | public static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
44 |  | public static final String HEADER_CACHE_CONTROL = "Cache-Control";
45 |  | public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
46 |  |
47 |  | // Fragment parameter
48 |  | public static final int FRAGMENT_AUTODETECT = -1;
49 |  | public static final int FRAGMENT_NO = 0;
50 |  | public static final int FRAGMENT_YES = 1;
51 |  |
52 |  | // No cache parameter
53 |  | public static final int NOCACHE_OFF = 0;
54 |  | public static final int NOCACHE_SESSION_ID_IN_URL = 1;
55 |  |
56 |  | // Last Modified parameter
57 |  | public static final long LAST_MODIFIED_OFF = 0;
58 |  | public static final long LAST_MODIFIED_ON = 1;
59 |  | public static final long LAST_MODIFIED_INITIAL = -1;
60 |  |
61 |  | // Expires parameter
62 |  | public static final long EXPIRES_OFF = 0;
63 |  | public static final long EXPIRES_ON = 1;
64 |  | public static final long EXPIRES_TIME = -1;
65 |  |
66 |  | // Cache Control
67 |  | public static final long MAX_AGE_NO_INIT = Long.MIN_VALUE;
68 |  | public static final long MAX_AGE_TIME = Long.MAX_VALUE;
69 |  |
70 |  | // request attribute to avoid reentrance
71 |  | private final static String REQUEST_FILTERED = "__oscache_filtered__";
72 |  | private String requestFiltered;
73 |  |
74 |  | // the policy for the expires header
75 |  | private EntryRefreshPolicy expiresRefreshPolicy;
76 |  |
77 |  | // the logger
78 |  | private final Log log = LogFactory.getLog(this.getClass());
79 |  |
80 |  | // filter variables
81 |  | private FilterConfig config;
82 |  | private ServletCacheAdministrator admin = null;
83 |  | private int cacheScope = PageContext.APPLICATION_SCOPE; // filter scope - default is APPLICATION
84 |  | private int fragment = FRAGMENT_AUTODETECT; // defines if this filter handles fragments of a page - default is auto detect
85 |  | private int time = 60 * 60; // time before cache should be refreshed - default one hour (in seconds)
86 |  | private String cron = null; // A cron expression that determines when this cached content will expire - default is null
87 |  | private int nocache = NOCACHE_OFF; // defines special no cache option for the requests - default is off
88 |  | private long lastModified = LAST_MODIFIED_INITIAL; // defines if the last-modified-header will be sent - default is intial setting
89 |  | private long expires = EXPIRES_ON; // defines if the expires-header will be sent - default is on
90 |  | private long cacheControlMaxAge = -60; // defines which max-age in Cache-Control to be set - default is 60 seconds for max-age
91 |  | private ICacheKeyProvider cacheKeyProvider = this; // the provider of the cache key - default is the CacheFilter itselfs
92 |  | private ICacheGroupsProvider cacheGroupsProvider = this; // the provider of the cache groups - default is the CacheFilter itselfs
93 |  | private List disableCacheOnMethods = null; // caching can be disabled by defining the http methods - default is off
94 |  |
95 |  | /**
96 |  | * Filter clean-up
97 |  | */
98 | 0 | public void destroy() {
99 |  | //Not much to do...
100 |  | }
101 |  |
102 |  | /**
103 |  | * The doFilter call caches the response by wrapping the <code>HttpServletResponse</code>
104 |  | * object so that the output stream can be caught. This works by splitting off the output
105 |  | * stream into two with the {@link SplitServletOutputStream} class. One stream gets written
106 |  | * out to the response as normal, the other is fed into a byte array inside a {@link ResponseContent}
107 |  | * object.
108 |  | *
109 |  | * @param request The servlet request
110 |  | * @param response The servlet response
111 |  | * @param chain The filter chain
112 |  | * @throws ServletException IOException
113 |  | */
114 | 0 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
115 | 0 | if (log.isInfoEnabled()) {
116 | 0 | log.info("OSCache: filter in scope " + cacheScope);
117 |  | }
118 |  |
119 |  | // avoid reentrance (CACHE-128) and check if request is cacheable
120 | 0 | if (isFilteredBefore(request) || !isCacheableInternal(request)) {
121 | 0 | chain.doFilter(request, response);
122 | 0 | return;
123 |  | }
124 | 0 | request.setAttribute(requestFiltered, Boolean.TRUE);
125 |  |
126 | 0 | HttpServletRequest httpRequest = (HttpServletRequest) request;
127 |  |
128 |  | // checks if the response will be a fragment of a page
129 | 0 | boolean fragmentRequest = isFragment(httpRequest);
130 |  |
131 |  | // avoid useless session creation for application scope pages (CACHE-129)
132 | 0 | Cache cache;
133 | 0 | if (cacheScope == PageContext.SESSION_SCOPE) {
134 | 0 | cache = admin.getSessionScopeCache(httpRequest.getSession(true));
135 |  | } else {
136 | 0 | cache = admin.getAppScopeCache(config.getServletContext());
137 |  | }
138 |  |
139 |  | // generate the cache entry key
140 | 0 | String key = cacheKeyProvider.createCacheKey(httpRequest, admin, cache);
141 |  |
142 | 0 | try {
143 | 0 | ResponseContent respContent = (ResponseContent) cache.getFromCache(key, time, cron);
144 |  |
145 | 0 | if (log.isInfoEnabled()) {
146 | 0 | log.info("OSCache: Using cached entry for " + key);
147 |  | }
148 |  |
149 | 0 | boolean acceptsGZip = false;
150 | 0 | if ((!fragmentRequest) && (lastModified != LAST_MODIFIED_OFF)) {
151 | 0 | long clientLastModified = httpRequest.getDateHeader(HEADER_IF_MODIFIED_SINCE); // will return -1 if no header...
152 |  |
153 |  | // only reply with SC_NOT_MODIFIED
154 |  | // if the client has already the newest page and the response isn't a fragment in a page
155 | 0 | if ((clientLastModified != -1) && (clientLastModified >= respContent.getLastModified())) {
156 | 0 | ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_NOT_MODIFIED);
157 | 0 | return;
158 |  | }
159 |  |
160 | 0 | acceptsGZip = respContent.isContentGZiped() && acceptsGZipEncoding(httpRequest);
161 |  | }
162 |  |
163 | 0 | respContent.writeTo(response, fragmentRequest, acceptsGZip);
164 |  | // acceptsGZip is used for performance reasons above; use the following line for CACHE-49
165 |  | // respContent.writeTo(response, fragmentRequest, acceptsGZipEncoding(httpRequest));
166 |  | } catch (NeedsRefreshException nre) {
167 | 0 | boolean updateSucceeded = false;
168 |  |
169 | 0 | try {
170 | 0 | if (log.isInfoEnabled()) {
171 | 0 | log.info("OSCache: New cache entry, cache stale or cache scope flushed for " + key);
172 |  | }
173 |  |
174 | 0 | CacheHttpServletResponseWrapper cacheResponse = new CacheHttpServletResponseWrapper((HttpServletResponse) response, fragmentRequest, time * 1000L, lastModified, expires, cacheControlMaxAge);
175 | 0 | chain.doFilter(request, cacheResponse);
176 | 0 | cacheResponse.flushBuffer();
177 |  |
178 |  | // Only cache if the response is cacheable
179 | 0 | if (isCacheableInternal(cacheResponse)) {
180 |  | // get the cache groups of the content
181 | 0 | String[] groups = cacheGroupsProvider.createCacheGroups(httpRequest, admin, cache);
182 |  | // Store as the cache content the result of the response
183 | 0 | cache.putInCache(key, cacheResponse.getContent(), groups, expiresRefreshPolicy, null);
184 | 0 | updateSucceeded = true;
185 | 0 | if (log.isInfoEnabled()) {
186 | 0 | log.info("OSCache: New entry added to the cache with key " + key);
187 |  | }
188 |  | }
189 |  | } finally {
190 | 0 | if (!updateSucceeded) {
191 | 0 | cache.cancelUpdate(key);
192 |  | }
193 |  | }
194 |  | }
195 |  | }
196 |  |
197 |  | /**
198 |  | * Initialize the filter. This retrieves a {@link ServletCacheAdministrator}
199 |  | * instance and configures the filter based on any initialization parameters.<p>
200 |  | * The supported initialization parameters are:
201 |  | * <ul>
202 |  | *
203 |  | * <li><b>oscache-properties-file</b> - the properties file that contains the OSCache configuration
204 |  | * options to be used by the Cache that this Filter should use.</li>
205 |  | *
206 |  | * @param filterConfig The filter configuration
207 |  | */
208 | 0 | public void init(FilterConfig filterConfig) {
209 |  | // Get whatever settings we want...
210 | 0 | config = filterConfig;
211 |  |
212 | 0 | log.info("OSCache: Initializing CacheFilter with filter name " + config.getFilterName());
213 |  |
214 |  | // setting the request filter to avoid reentrance with the same filter
215 | 0 | requestFiltered = REQUEST_FILTERED + config.getFilterName();
216 | 0 | log.info("Request filter attribute is " + requestFiltered);
217 |  |
218 |  | // filter Properties file
219 | 0 | Properties props = null;
220 | 0 | try {
221 | 0 | String propertiesfile = config.getInitParameter("oscache-properties-file");
222 |  |
223 | 0 | if (propertiesfile != null && propertiesfile.length() > 0) {
224 | 0 | props = Config.loadProperties(propertiesfile, "CacheFilter with filter name '" + config.getFilterName()+ "'");
225 |  | }
226 |  | } catch (Exception e) {
227 | 0 | log.info("OSCache: Init parameter 'oscache-properties-file' not set, using default.");
228 |  | }
229 | 0 | admin = ServletCacheAdministrator.getInstance(config.getServletContext(), props);
230 |  |
231 |  | // filter parameter time
232 | 0 | String timeParam = config.getInitParameter("time");
233 | 0 | if (timeParam != null) {
234 | 0 | try {
235 | 0 | setTime(Integer.parseInt(timeParam));
236 |  | } catch (NumberFormatException nfe) {
237 | 0 | log.error("OSCache: Unexpected value for the init parameter 'time', defaulting to one hour. Message=" + nfe.getMessage());
238 |  | }
239 |  | }
240 |  |
241 |  | // filter parameter scope
242 | 0 | String scopeParam = config.getInitParameter("scope");
243 | 0 | if (scopeParam != null) {
244 | 0 | if ("session".equalsIgnoreCase(scopeParam)) {
245 | 0 | setCacheScope(PageContext.SESSION_SCOPE);
246 | 0 | } else if ("application".equalsIgnoreCase(scopeParam)) {
247 | 0 | setCacheScope(PageContext.APPLICATION_SCOPE);
248 |  | } else {
249 | 0 | log.error("OSCache: Wrong value '" + scopeParam + "' for init parameter 'scope', defaulting to 'application'.");
250 |  | }
251 |  |
252 |  | }
253 |  |
254 |  | // filter parameter cron
255 | 0 | setCron(config.getInitParameter("cron"));
256 |  |
257 |  | // filter parameter fragment
258 | 0 | String fragmentParam = config.getInitParameter("fragment");
259 | 0 | if (fragmentParam != null) {
260 | 0 | if ("no".equalsIgnoreCase(fragmentParam)) {
261 | 0 | setFragment(FRAGMENT_NO);
262 | 0 | } else if ("yes".equalsIgnoreCase(fragmentParam)) {
263 | 0 | setFragment(FRAGMENT_YES);
264 | 0 | } else if ("auto".equalsIgnoreCase(fragmentParam)) {
265 | 0 | setFragment(FRAGMENT_AUTODETECT);
266 |  | } else {
267 | 0 | log.error("OSCache: Wrong value '" + fragmentParam + "' for init parameter 'fragment', defaulting to 'auto detect'.");
268 |  | }
269 |  | }
270 |  |
271 |  | // filter parameter nocache
272 | 0 | String nocacheParam = config.getInitParameter("nocache");
273 | 0 | if (nocacheParam != null) {
274 | 0 | if ("off".equalsIgnoreCase(nocacheParam)) {
275 | 0 | nocache = NOCACHE_OFF;
276 | 0 | } else if ("sessionIdInURL".equalsIgnoreCase(nocacheParam)) {
277 | 0 | nocache = NOCACHE_SESSION_ID_IN_URL;
278 |  | } else {
279 | 0 | log.error("OSCache: Wrong value '" + nocacheParam + "' for init parameter 'nocache', defaulting to 'off'.");
280 |  | }
281 |  | }
282 |  |
283 |  | // filter parameter last modified
284 | 0 | String lastModifiedParam = config.getInitParameter("lastModified");
285 | 0 | if (lastModifiedParam != null) {
286 | 0 | if ("off".equalsIgnoreCase(lastModifiedParam)) {
287 | 0 | lastModified = LAST_MODIFIED_OFF;
288 | 0 | } else if ("on".equalsIgnoreCase(lastModifiedParam)) {
289 | 0 | lastModified = LAST_MODIFIED_ON;
290 | 0 | } else if ("initial".equalsIgnoreCase(lastModifiedParam)) {
291 | 0 | lastModified = LAST_MODIFIED_INITIAL;
292 |  | } else {
293 | 0 | log.error("OSCache: Wrong value '" + lastModifiedParam + "' for init parameter 'lastModified', defaulting to 'initial'.");
294 |  | }
295 |  | }
296 |  |
297 |  | // filter parameter expires
298 | 0 | String expiresParam = config.getInitParameter("expires");
299 | 0 | if (expiresParam != null) {
300 | 0 | if ("off".equalsIgnoreCase(expiresParam)) {
301 | 0 | setExpires(EXPIRES_OFF);
302 | 0 | } else if ("on".equalsIgnoreCase(expiresParam)) {
303 | 0 | setExpires(EXPIRES_ON);
304 | 0 | } else if ("time".equalsIgnoreCase(expiresParam)) {
305 | 0 | setExpires(EXPIRES_TIME);
306 |  | } else {
307 | 0 | log.error("OSCache: Wrong value '" + expiresParam + "' for init parameter 'expires', defaulting to 'on'.");
308 |  | }
309 |  | }
310 |  |
311 |  | // filter parameter Cache-Control
312 | 0 | String cacheControlMaxAgeParam = config.getInitParameter("max-age");
313 | 0 | if (cacheControlMaxAgeParam != null) {
314 | 0 | if (cacheControlMaxAgeParam.equalsIgnoreCase("no init")) {
315 | 0 | setCacheControlMaxAge(MAX_AGE_NO_INIT);
316 | 0 | } else if (cacheControlMaxAgeParam.equalsIgnoreCase("time")) {
317 | 0 | setCacheControlMaxAge(MAX_AGE_TIME);
318 |  | } else {
319 | 0 | try {
320 | 0 | setCacheControlMaxAge(Long.parseLong(cacheControlMaxAgeParam));
321 |  | } catch (NumberFormatException nfe) {
322 | 0 | log.error("OSCache: Unexpected value for the init parameter 'max-age', defaulting to '60'. Message=" + nfe.getMessage());
323 |  | }
324 |  | }
325 |  | }
326 |  |
327 |  | // filter parameter ICacheKeyProvider
328 | 0 | ICacheKeyProvider cacheKeyProviderParam = (ICacheKeyProvider)instantiateFromInitParam("ICacheKeyProvider", ICacheKeyProvider.class, this.getClass().getName());
329 | 0 | if (cacheKeyProviderParam != null) {
330 | 0 | setCacheKeyProvider(cacheKeyProviderParam);
331 |  | }
332 |  |
333 |  | // filter parameter ICacheGroupsProvider
334 | 0 | ICacheGroupsProvider cacheGroupsProviderParam = (ICacheGroupsProvider)instantiateFromInitParam("ICacheGroupsProvider", ICacheGroupsProvider.class, this.getClass().getName());
335 | 0 | if (cacheGroupsProviderParam != null) {
336 | 0 | setCacheGroupsProvider(cacheGroupsProviderParam);
337 |  | }
338 |  |
339 |  | // filter parameter EntryRefreshPolicy
340 | 0 | EntryRefreshPolicy expiresRefreshPolicyParam = (EntryRefreshPolicy)instantiateFromInitParam("EntryRefreshPolicy", EntryRefreshPolicy.class, ExpiresRefreshPolicy.class.getName());
341 | 0 | if (expiresRefreshPolicyParam != null) {
342 | 0 | setExpiresRefreshPolicy(expiresRefreshPolicyParam);
343 |  | } else {
344 |  | // setting the refresh period for this cache filter
345 | 0 | setExpiresRefreshPolicy(new ExpiresRefreshPolicy(time));
346 |  | }
347 |  |
348 |  | // filter parameter scope
349 | 0 | String disableCacheOnMethodsParam = config.getInitParameter("disableCacheOnMethods");
350 | 0 | if (StringUtil.hasLength(disableCacheOnMethodsParam)) {
351 | 0 | disableCacheOnMethods = StringUtil.split(disableCacheOnMethodsParam, ',');
352 |  | // log.error("OSCache: Wrong value '" + disableCacheOnMethodsParam + "' for init parameter 'disableCacheOnMethods', defaulting to 'null'.");
353 |  | }
354 |  |
355 |  | }
356 |  |
357 | 0 | private Object instantiateFromInitParam(String classInitParam, Class interfaceClass, String defaultObjectName) {
358 | 0 | String className = config.getInitParameter(classInitParam);
359 | 0 | if (className != null) {
360 | 0 | try {
361 | 0 | Class clazz = ClassLoaderUtil.loadClass(className, this.getClass());
362 | 0 | if (!interfaceClass.isAssignableFrom(clazz)) {
363 | 0 | log.error("OSCache: Specified class '" + className + "' does not implement" + interfaceClass.getName() + ". Using default " + defaultObjectName + ".");
364 | 0 | return null;
365 |  | } else {
366 | 0 | return clazz.newInstance();
367 |  | }
368 |  | } catch (ClassNotFoundException e) {
369 | 0 | log.error("OSCache: Class '" + className + "' not found. Defaulting to " + defaultObjectName + ".", e);
370 |  | } catch (InstantiationException e) {
371 | 0 | log.error("OSCache: Class '" + className + "' could not be instantiated because it is not a concrete class. Using default object " + defaultObjectName + ".", e);
372 |  | } catch (IllegalAccessException e) {
373 | 0 | log.error("OSCache: Class '"+ className+ "' could not be instantiated because it is not public. Using default object " + defaultObjectName + ".", e);
374 |  | }
375 |  | }
376 | 0 | return null;
377 |  | }
378 |  |
379 |  | /**
380 |  | * {@link ICacheKeyProvider}
381 |  | * @see com.opensymphony.oscache.web.filter.ICacheKeyProvider#createCacheKey(javax.servlet.http.HttpServletRequest, ServletCacheAdministrator, Cache)
382 |  | */
383 | 0 | public String createCacheKey(HttpServletRequest httpRequest, ServletCacheAdministrator scAdmin, Cache cache) {
384 | 0 | return scAdmin.generateEntryKey(null, httpRequest, cacheScope);
385 |  | }
386 |  |
387 |  | /**
388 |  | * {@link ICacheGroupsProvider}
389 |  | * @see com.opensymphony.oscache.web.filter.ICacheGroupsProvider#createCacheGroups(javax.servlet.http.HttpServletRequest, ServletCacheAdministrator, Cache)
390 |  | */
391 | 0 | public String[] createCacheGroups(HttpServletRequest httpRequest, ServletCacheAdministrator scAdmin, Cache cache) {
392 | 0 | return null;
393 |  | }
394 |  |
395 |  | /**
396 |  | * Checks if the request is a fragment in a page.
397 |  | *
398 |  | * According to Java Servlet API 2.2 (8.2.1 Dispatching Requests, Included
399 |  | * Request Parameters), when a servlet is being used from within an include,
400 |  | * the attribute <code>javax.servlet.include.request_uri</code> is set.
401 |  | * According to Java Servlet API 2.3 this is excepted for servlets obtained
402 |  | * by using the getNamedDispatcher method.
403 |  | *
404 |  | * @param request the to be handled request
405 |  | * @return true if the request is a fragment in a page
406 |  | */
407 | 0 | public boolean isFragment(HttpServletRequest request) {
408 | 0 | if (fragment == FRAGMENT_AUTODETECT) {
409 | 0 | return request.getAttribute("javax.servlet.include.request_uri") != null;
410 |  | } else {
411 | 0 | return (fragment == FRAGMENT_NO) ? false : true;
412 |  | }
413 |  | }
414 |  |
415 |  | /**
416 |  | * Checks if the request was filtered before, so
417 |  | * guarantees to be executed once per request. You
418 |  | * can override this methods to define a more specific
419 |  | * behaviour.
420 |  | *
421 |  | * @param request checks if the request was filtered before.
422 |  | * @return true if it is the first execution
423 |  | */
424 | 0 | public boolean isFilteredBefore(ServletRequest request) {
425 | 0 | return request.getAttribute(requestFiltered) != null;
426 |  | }
427 |  |
428 |  | /*
429 |  | * isCacheableInternal gurarantees that the log information is correct.
430 |  | *
431 |  | * @param request The servlet request
432 |  | * @return Returns a boolean indicating if the request can be cached or not.
433 |  | */
434 | 0 | private final boolean isCacheableInternal(ServletRequest request) {
435 | 0 | final boolean cacheable = isCacheable(request);
436 |  |
437 | 0 | if (log.isDebugEnabled()) {
438 | 0 | log.debug("OSCache: the request " + ((cacheable) ? "is" : "is not") + " cachable.");
439 |  | }
440 |  |
441 | 0 | return cacheable;
442 |  | }
443 |  |
444 |  | /**
445 |  | * isCacheable is a method allowing a subclass to decide if a request is
446 |  | * cachable or not.
447 |  | *
448 |  | * @param request The servlet request
449 |  | * @return Returns a boolean indicating if the request can be cached or not.
450 |  | */
451 | 0 | public boolean isCacheable(ServletRequest request) {
452 | 0 | boolean cacheable = request instanceof HttpServletRequest;
453 |  |
454 | 0 | if (cacheable) {
455 | 0 | HttpServletRequest requestHttp = (HttpServletRequest) request;
456 |  | // CACHE-272 don't cache special http request methods
457 | 0 | if ((disableCacheOnMethods != null) && (disableCacheOnMethods.contains(requestHttp.getMethod()))) {
458 | 0 | return false;
459 |  | }
460 | 0 | if (nocache == NOCACHE_SESSION_ID_IN_URL) { // don't cache requests if session id is in the URL
461 | 0 | cacheable = !requestHttp.isRequestedSessionIdFromURL();
462 |  | }
463 |  | }
464 |  |
465 | 0 | return cacheable;
466 |  | }
467 |  |
468 |  | /*
469 |  | * isCacheableInternal gurarantees that the log information is correct.
470 |  | *
471 |  | * @param cacheResponse the HTTP servlet response
472 |  | * @return Returns a boolean indicating if the response can be cached or not.
473 |  | */
474 | 0 | private final boolean isCacheableInternal(CacheHttpServletResponseWrapper cacheResponse) {
475 | 0 | final boolean cacheable = isCacheable(cacheResponse);
476 |  |
477 | 0 | if (log.isDebugEnabled()) {
478 | 0 | log.debug("OSCache: the response " + ((cacheable) ? "is" : "is not") + " cachable.");
479 |  | }
480 |  |
481 | 0 | return cacheable;
482 |  | }
483 |  |
484 |  | /**
485 |  | * isCacheable is a method allowing subclass to decide if a response is
486 |  | * cachable or not.
487 |  | *
488 |  | * @param cacheResponse the HTTP servlet response
489 |  | * @return Returns a boolean indicating if the response can be cached or not.
490 |  | */
491 | 0 | public boolean isCacheable(CacheHttpServletResponseWrapper cacheResponse) {
492 |  | // TODO implement CACHE-137 here
493 |  | // Only cache if the response was 200
494 | 0 | return cacheResponse.getStatus() == HttpServletResponse.SC_OK;
495 |  | }
496 |  |
497 |  | /**
498 |  | * Check if the client browser support gzip compression.
499 |  | *
500 |  | * @param request the http request
501 |  | * @return true if client browser supports GZIP
502 |  | */
503 | 0 | public boolean acceptsGZipEncoding(HttpServletRequest request) {
504 | 0 | String acceptEncoding = request.getHeader(HEADER_ACCEPT_ENCODING);
505 | 0 | return (acceptEncoding != null) && (acceptEncoding.indexOf("gzip") != -1);
506 |  | }
507 |  |
508 |  | // ---------------------------------
509 |  | // --- getter and setter methods ---
510 |  | // ---------------------------------
511 |  |
512 |  | /**
513 |  | * @return the max-age of the cache control
514 |  | * @since 2.4
515 |  | */
516 | 0 | public long getCacheControlMaxAge() {
517 | 0 | if ((cacheControlMaxAge == MAX_AGE_NO_INIT) || (cacheControlMaxAge == MAX_AGE_TIME)) {
518 | 0 | return cacheControlMaxAge;
519 |  | }
520 | 0 | return - cacheControlMaxAge;
521 |  | }
522 |  |
523 |  | /**
524 |  | * <b>max-age</b> - defines the cache control response header max-age. Acceptable values are
525 |  | * <code>MAX_AGE_NO_INIT</code> for don't initializing the max-age cache control,
526 |  | * <code>MAX_AGE_TIME</code> the max-age information will be based on the time parameter and creation time of the content (expiration timestamp minus current timestamp), and
527 |  | * <code>[positive integer]</code> value constant in seconds to be set in every response, the default value is 60.
528 |  | *
529 |  | * @param cacheControlMaxAge the cacheControlMaxAge to set
530 |  | * @since 2.4
531 |  | */
532 | 0 | public void setCacheControlMaxAge(long cacheControlMaxAge) {
533 | 0 | if ((cacheControlMaxAge == MAX_AGE_NO_INIT) || (cacheControlMaxAge == MAX_AGE_TIME)) {
534 | 0 | this.cacheControlMaxAge = cacheControlMaxAge;
535 | 0 | } else if (cacheControlMaxAge >= 0) {
536 |  | // declare the cache control as a constant
537 |  | // TODO check if this value can be stored as a positive long without changing it
538 | 0 | this.cacheControlMaxAge = - cacheControlMaxAge;
539 |  | } else {
540 | 0 | log.warn("OSCache: 'max-age' must be at least a positive integer, defaulting to '60'. ");
541 | 0 | this.cacheControlMaxAge = -60;
542 |  | }
543 |  | }
544 |  |
545 |  | /**
546 |  | * @return the cacheGroupsProvider
547 |  | * @since 2.4
548 |  | */
549 | 0 | public ICacheGroupsProvider getCacheGroupsProvider() {
550 | 0 | return cacheGroupsProvider;
551 |  | }
552 |  |
553 |  | /**
554 |  | * <b>ICacheGroupsProvider</b> - Class implementing the interface <code>ICacheGroupsProvider</code>.
555 |  | * A developer can implement a method which provides cache groups based on the request,
556 |  | * the servlect cache administrator and cache. The parameter has to be not <code>null</code>.
557 |  | *
558 |  | * @param cacheGroupsProvider the cacheGroupsProvider to set
559 |  | * @since 2.4
560 |  | */
561 | 0 | public void setCacheGroupsProvider(ICacheGroupsProvider cacheGroupsProvider) {
562 | 0 | if (cacheGroupsProvider == null) throw new IllegalArgumentException("The ICacheGroupsProvider is null.");
563 | 0 | this.cacheGroupsProvider = cacheGroupsProvider;
564 |  | }
565 |  |
566 |  | /**
567 |  | * @return the cacheKeyProvider
568 |  | * @since 2.4
569 |  | */
570 | 0 | public ICacheKeyProvider getCacheKeyProvider() {
571 | 0 | return cacheKeyProvider;
572 |  | }
573 |  |
574 |  | /**
575 |  | * <b>ICacheKeyProvider</b> - Class implementing the interface <code>ICacheKeyProvider</code>.
576 |  | * A developer can implement a method which provides cache keys based on the request,
577 |  | * the servlect cache administrator and cache. The parameter has to be not <code>null</code>.
578 |  | *
579 |  | * @param cacheKeyProvider the cacheKeyProvider to set
580 |  | * @since 2.4
581 |  | */
582 | 0 | public void setCacheKeyProvider(ICacheKeyProvider cacheKeyProvider) {
583 | 0 | if (cacheKeyProvider == null) throw new IllegalArgumentException("The ICacheKeyProvider is null.");
584 | 0 | this.cacheKeyProvider = cacheKeyProvider;
585 |  | }
586 |  |
587 |  | /**
588 |  | * Returns PageContext.APPLICATION_SCOPE or PageContext.SESSION_SCOPE.
589 |  | * @return the cache scope
590 |  | * @since 2.4
591 |  | */
592 | 0 | public int getCacheScope() {
593 | 0 | return cacheScope;
594 |  | }
595 |  |
596 |  | /**
597 |  | * <b>scope</b> - the default scope to cache content. Acceptable values
598 |  | * are <code>PageContext.APPLICATION_SCOPE</code> (default) and <code>PageContext.SESSION_SCOPE</code>.
599 |  | *
600 |  | * @param cacheScope the cacheScope to set
601 |  | * @since 2.4
602 |  | */
603 | 0 | public void setCacheScope(int cacheScope) {
604 | 0 | if ((cacheScope != PageContext.APPLICATION_SCOPE) && (cacheScope != PageContext.SESSION_SCOPE))
605 | 0 | throw new IllegalArgumentException("Acceptable values for cache scope are PageContext.APPLICATION_SCOPE or PageContext.SESSION_SCOPE");
606 | 0 | this.cacheScope = cacheScope;
607 |  | }
608 |  |
609 |  | /**
610 |  | * @return the cron
611 |  | * @since 2.4
612 |  | */
613 | 0 | public String getCron() {
614 | 0 | return cron;
615 |  | }
616 |  |
617 |  | /**
618 |  | * <b>cron</b> - defines an expression that determines when the page content will expire.
619 |  | * This allows content to be expired at particular dates and/or times, rather than once
620 |  | * a cache entry reaches a certain age.
621 |  | *
622 |  | * @param cron the cron to set
623 |  | * @since 2.4
624 |  | */
625 | 0 | public void setCron(String cron) {
626 | 0 | this.cron = cron;
627 |  | }
628 |  |
629 |  | /**
630 |  | * @return the expires header
631 |  | * @since 2.4
632 |  | */
633 | 0 | public long getExpires() {
634 | 0 | return expires;
635 |  | }
636 |  |
637 |  | /**
638 |  | * <b>expires</b> - defines if the expires header will be sent in the response. Acceptable values are
639 |  | * <code>EXPIRES_OFF</code> for don't sending the header, even it is set in the filter chain,
640 |  | * <code>EXPIRES_ON</code> (default) for sending it if it is set in the filter chain and
641 |  | * <code>EXPIRES_TIME</code> the expires information will be intialized based on the time parameter and creation time of the content.
642 |  | *
643 |  | * @param expires the expires to set
644 |  | * @since 2.4
645 |  | */
646 | 0 | public void setExpires(long expires) {
647 | 0 | if ((expires < EXPIRES_TIME) || (expires > EXPIRES_ON)) throw new IllegalArgumentException("Expires value out of range.");
648 | 0 | this.expires = expires;
649 |  | }
650 |  |
651 |  | /**
652 |  | * @return the expiresRefreshPolicy
653 |  | * @since 2.4
654 |  | */
655 | 0 | public EntryRefreshPolicy getExpiresRefreshPolicy() {
656 | 0 | return expiresRefreshPolicy;
657 |  | }
658 |  |
659 |  | /**
660 |  | * <b>EntryRefreshPolicy</b> - Class implementing the interface <code>EntryRefreshPolicy</code>.
661 |  | * A developer can implement a class which provides a different custom cache invalidation policy for a specific cache entry.
662 |  | * If not specified, the default policy is timed entry expiry as specified with the <b>time</b> parameter described above.
663 |  | *
664 |  | * @param expiresRefreshPolicy the expiresRefreshPolicy to set
665 |  | * @since 2.4
666 |  | */
667 | 0 | public void setExpiresRefreshPolicy(EntryRefreshPolicy expiresRefreshPolicy) {
668 | 0 | if (expiresRefreshPolicy == null) throw new IllegalArgumentException("The EntryRefreshPolicy is null.");
669 | 0 | this.expiresRefreshPolicy = expiresRefreshPolicy;
670 |  | }
671 |  |
672 |  | /**
673 |  | * @return the fragment
674 |  | * @since 2.4
675 |  | */
676 | 0 | public int getFragment() {
677 | 0 | return fragment;
678 |  | }
679 |  |
680 |  | /**
681 |  | * <b>fragment</b> - defines if this filter handles fragments of a page. Acceptable values
682 |  | * are <code>FRAGMENT_AUTODETECT</code> (default) for auto detect, <code>FRAGMENT_NO</code> and <code>FRAGMENT_YES</code>.
683 |  | *
684 |  | * @param fragment the fragment to set
685 |  | * @since 2.4
686 |  | */
687 | 0 | public void setFragment(int fragment) {
688 | 0 | if ((fragment < FRAGMENT_AUTODETECT) || (fragment > FRAGMENT_YES)) throw new IllegalArgumentException("Fragment value out of range.");
689 | 0 | this.fragment = fragment;
690 |  | }
691 |  |
692 |  | /**
693 |  | * @return the lastModified
694 |  | * @since 2.4
695 |  | */
696 | 0 | public long getLastModified() {
697 | 0 | return lastModified;
698 |  | }
699 |  |
700 |  | /**
701 |  | * <b>lastModified</b> - defines if the last modified header will be sent in the response. Acceptable values are
702 |  | * <code>LAST_MODIFIED_OFF</code> for don't sending the header, even it is set in the filter chain,
703 |  | * <code>LAST_MODIFIED_ON</code> for sending it if it is set in the filter chain and
704 |  | * <code>LAST_MODIFIED_INITIAL</code> (default) the last modified information will be set based on the current time and changes are allowed.
705 |  | *
706 |  | * @param lastModified the lastModified to set
707 |  | * @since 2.4
708 |  | */
709 | 0 | public void setLastModified(long lastModified) {
710 | 0 | if ((lastModified < LAST_MODIFIED_INITIAL) || (lastModified > LAST_MODIFIED_ON)) throw new IllegalArgumentException("LastModified value out of range.");
711 | 0 | this.lastModified = lastModified;
712 |  | }
713 |  |
714 |  | /**
715 |  | * @return the nocache
716 |  | * @since 2.4
717 |  | */
718 | 0 | public int getNocache() {
719 | 0 | return nocache;
720 |  | }
721 |  |
722 |  | /**
723 |  | * <b>nocache</b> - defines which objects shouldn't be cached. Acceptable values
724 |  | * are <code>NOCACHE_OFF</code> (default) and <code>NOCACHE_SESSION_ID_IN_URL</code> if the session id is
725 |  | * contained in the URL.
726 |  | *
727 |  | * @param nocache the nocache to set
728 |  | * @since 2.4
729 |  | */
730 | 0 | public void setNocache(int nocache) {
731 | 0 | if ((nocache < NOCACHE_OFF) || (nocache > NOCACHE_SESSION_ID_IN_URL)) throw new IllegalArgumentException("Nocache value out of range.");
732 | 0 | this.nocache = nocache;
733 |  | }
734 |  |
735 |  | /**
736 |  | * @return the time
737 |  | * @since 2.4
738 |  | */
739 | 0 | public int getTime() {
740 | 0 | return time;
741 |  | }
742 |  |
743 |  | /**
744 |  | * <b>time</b> - the default time (in seconds) to cache content for. The default
745 |  | * value is 3600 seconds (one hour). Specifying -1 (indefinite expiry) as the cache
746 |  | * time will ensure a content does not become stale until it is either explicitly
747 |  | * flushed or the expires refresh policy causes the entry to expire.
748 |  | *
749 |  | * @param time the time to set
750 |  | * @since 2.4
751 |  | */
752 | 0 | public void setTime(int time) {
753 | 0 | this.time = time;
754 |  | // check if ExpiresRefreshPolicy has to be reset
755 | 0 | if (expiresRefreshPolicy instanceof ExpiresRefreshPolicy) {
756 | 0 | ((ExpiresRefreshPolicy) expiresRefreshPolicy).setRefreshPeriod(time);
757 |  | }
758 |  | }
759 |  |
760 |  | /**
761 |  | * @link http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/http/HttpServletRequest.html#getMethod()
762 |  | * @return the list of http method names for which cacheing should be disabled
763 |  | * @since 2.4
764 |  | */
765 | 0 | public List getDisableCacheOnMethods() {
766 | 0 | return disableCacheOnMethods;
767 |  | }
768 |  |
769 |  | /**
770 |  | * <b>disableCacheOnMethods</b> - Defines the http method name for which cacheing should be disabled.
771 |  | * The default value is <code>null</code> for cacheing all requests without regarding the method name.
772 |  | * @link http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/http/HttpServletRequest.html#getMethod()
773 |  | * @param disableCacheOnMethods the list of http method names for which cacheing should be disabled
774 |  | * @since 2.4
775 |  | */
776 | 0 | public void setDisableCacheOnMethods(List disableCacheOnMethods) {
777 | 0 | this.disableCacheOnMethods = disableCacheOnMethods;
778 |  | }
779 |  |
780 |  | // TODO: check if getter/setter for oscache-properties-file is possible
781 |  |
782 |  | }