| Method from com.opensymphony.oscache.web.filter.CacheFilter Detail: |
public boolean acceptsGZipEncoding(HttpServletRequest request) {
String acceptEncoding = request.getHeader(HEADER_ACCEPT_ENCODING);
return (acceptEncoding != null) && (acceptEncoding.indexOf("gzip") != -1);
}
Check if the client browser support gzip compression. |
public String[] createCacheGroups(HttpServletRequest httpRequest,
ServletCacheAdministrator scAdmin,
Cache cache) {
return null;
}
|
public String createCacheKey(HttpServletRequest httpRequest,
ServletCacheAdministrator scAdmin,
Cache cache) {
return scAdmin.generateEntryKey(null, httpRequest, cacheScope);
}
|
public void destroy() {
// caching can be disabled by defining the http methods - default is off
//Not much to do...
}
|
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (log.isInfoEnabled()) {
log.info("OSCache: filter in scope " + cacheScope);
}
// avoid reentrance (CACHE-128) and check if request is cacheable
if (isFilteredBefore(request) || !isCacheableInternal(request)) {
chain.doFilter(request, response);
return;
}
request.setAttribute(requestFiltered, Boolean.TRUE);
HttpServletRequest httpRequest = (HttpServletRequest) request;
// checks if the response will be a fragment of a page
boolean fragmentRequest = isFragment(httpRequest);
// avoid useless session creation for application scope pages (CACHE-129)
Cache cache;
if (cacheScope == PageContext.SESSION_SCOPE) {
cache = admin.getSessionScopeCache(httpRequest.getSession(true));
} else {
cache = admin.getAppScopeCache(config.getServletContext());
}
// generate the cache entry key
String key = cacheKeyProvider.createCacheKey(httpRequest, admin, cache);
try {
ResponseContent respContent = (ResponseContent) cache.getFromCache(key, time, cron);
if (log.isInfoEnabled()) {
log.info("OSCache: Using cached entry for " + key);
}
boolean acceptsGZip = false;
if ((!fragmentRequest) && (lastModified != LAST_MODIFIED_OFF)) {
long clientLastModified = httpRequest.getDateHeader(HEADER_IF_MODIFIED_SINCE); // will return -1 if no header...
// only reply with SC_NOT_MODIFIED
// if the client has already the newest page and the response isn't a fragment in a page
if ((clientLastModified != -1) && (clientLastModified >= respContent.getLastModified())) {
((HttpServletResponse) response).setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
acceptsGZip = respContent.isContentGZiped() && acceptsGZipEncoding(httpRequest);
}
respContent.writeTo(response, fragmentRequest, acceptsGZip);
// acceptsGZip is used for performance reasons above; use the following line for CACHE-49
// respContent.writeTo(response, fragmentRequest, acceptsGZipEncoding(httpRequest));
} catch (NeedsRefreshException nre) {
boolean updateSucceeded = false;
try {
if (log.isInfoEnabled()) {
log.info("OSCache: New cache entry, cache stale or cache scope flushed for " + key);
}
CacheHttpServletResponseWrapper cacheResponse = new CacheHttpServletResponseWrapper((HttpServletResponse) response, fragmentRequest, time * 1000L, lastModified, expires, cacheControlMaxAge);
chain.doFilter(request, cacheResponse);
cacheResponse.flushBuffer();
// Only cache if the response is cacheable
if (isCacheableInternal(cacheResponse)) {
// get the cache groups of the content
String[] groups = cacheGroupsProvider.createCacheGroups(httpRequest, admin, cache);
// Store as the cache content the result of the response
cache.putInCache(key, cacheResponse.getContent(), groups, expiresRefreshPolicy, null);
updateSucceeded = true;
if (log.isInfoEnabled()) {
log.info("OSCache: New entry added to the cache with key " + key);
}
}
} finally {
if (!updateSucceeded) {
cache.cancelUpdate(key);
}
}
}
}
The doFilter call caches the response by wrapping the HttpServletResponse
object so that the output stream can be caught. This works by splitting off the output
stream into two with the SplitServletOutputStream class. One stream gets written
out to the response as normal, the other is fed into a byte array inside a ResponseContent
object. |
public long getCacheControlMaxAge() {
if ((cacheControlMaxAge == MAX_AGE_NO_INIT) || (cacheControlMaxAge == MAX_AGE_TIME)) {
return cacheControlMaxAge;
}
return - cacheControlMaxAge;
}
|
public ICacheGroupsProvider getCacheGroupsProvider() {
return cacheGroupsProvider;
}
|
public ICacheKeyProvider getCacheKeyProvider() {
return cacheKeyProvider;
}
|
public int getCacheScope() {
return cacheScope;
}
Returns PageContext.APPLICATION_SCOPE or PageContext.SESSION_SCOPE. |
public String getCron() {
return cron;
}
|
public List getDisableCacheOnMethods() {
return disableCacheOnMethods;
}
|
public long getExpires() {
return expires;
}
|
public EntryRefreshPolicy getExpiresRefreshPolicy() {
return expiresRefreshPolicy;
}
|
public int getFragment() {
return fragment;
}
|
public long getLastModified() {
return lastModified;
}
|
public int getNocache() {
return nocache;
}
|
public int getTime() {
return time;
}
|
public void init(FilterConfig filterConfig) {
// Get whatever settings we want...
config = filterConfig;
log.info("OSCache: Initializing CacheFilter with filter name " + config.getFilterName());
// setting the request filter to avoid reentrance with the same filter
requestFiltered = REQUEST_FILTERED + config.getFilterName();
log.info("Request filter attribute is " + requestFiltered);
// filter Properties file
Properties props = null;
try {
String propertiesfile = config.getInitParameter("oscache-properties-file");
if (propertiesfile != null && propertiesfile.length() > 0) {
props = Config.loadProperties(propertiesfile, "CacheFilter with filter name '" + config.getFilterName()+ "'");
}
} catch (Exception e) {
log.info("OSCache: Init parameter 'oscache-properties-file' not set, using default.");
}
admin = ServletCacheAdministrator.getInstance(config.getServletContext(), props);
// filter parameter time
String timeParam = config.getInitParameter("time");
if (timeParam != null) {
try {
setTime(Integer.parseInt(timeParam));
} catch (NumberFormatException nfe) {
log.error("OSCache: Unexpected value for the init parameter 'time', defaulting to one hour. Message=" + nfe.getMessage());
}
}
// filter parameter scope
String scopeParam = config.getInitParameter("scope");
if (scopeParam != null) {
if ("session".equalsIgnoreCase(scopeParam)) {
setCacheScope(PageContext.SESSION_SCOPE);
} else if ("application".equalsIgnoreCase(scopeParam)) {
setCacheScope(PageContext.APPLICATION_SCOPE);
} else {
log.error("OSCache: Wrong value '" + scopeParam + "' for init parameter 'scope', defaulting to 'application'.");
}
}
// filter parameter cron
setCron(config.getInitParameter("cron"));
// filter parameter fragment
String fragmentParam = config.getInitParameter("fragment");
if (fragmentParam != null) {
if ("no".equalsIgnoreCase(fragmentParam)) {
setFragment(FRAGMENT_NO);
} else if ("yes".equalsIgnoreCase(fragmentParam)) {
setFragment(FRAGMENT_YES);
} else if ("auto".equalsIgnoreCase(fragmentParam)) {
setFragment(FRAGMENT_AUTODETECT);
} else {
log.error("OSCache: Wrong value '" + fragmentParam + "' for init parameter 'fragment', defaulting to 'auto detect'.");
}
}
// filter parameter nocache
String nocacheParam = config.getInitParameter("nocache");
if (nocacheParam != null) {
if ("off".equalsIgnoreCase(nocacheParam)) {
nocache = NOCACHE_OFF;
} else if ("sessionIdInURL".equalsIgnoreCase(nocacheParam)) {
nocache = NOCACHE_SESSION_ID_IN_URL;
} else {
log.error("OSCache: Wrong value '" + nocacheParam + "' for init parameter 'nocache', defaulting to 'off'.");
}
}
// filter parameter last modified
String lastModifiedParam = config.getInitParameter("lastModified");
if (lastModifiedParam != null) {
if ("off".equalsIgnoreCase(lastModifiedParam)) {
lastModified = LAST_MODIFIED_OFF;
} else if ("on".equalsIgnoreCase(lastModifiedParam)) {
lastModified = LAST_MODIFIED_ON;
} else if ("initial".equalsIgnoreCase(lastModifiedParam)) {
lastModified = LAST_MODIFIED_INITIAL;
} else {
log.error("OSCache: Wrong value '" + lastModifiedParam + "' for init parameter 'lastModified', defaulting to 'initial'.");
}
}
// filter parameter expires
String expiresParam = config.getInitParameter("expires");
if (expiresParam != null) {
if ("off".equalsIgnoreCase(expiresParam)) {
setExpires(EXPIRES_OFF);
} else if ("on".equalsIgnoreCase(expiresParam)) {
setExpires(EXPIRES_ON);
} else if ("time".equalsIgnoreCase(expiresParam)) {
setExpires(EXPIRES_TIME);
} else {
log.error("OSCache: Wrong value '" + expiresParam + "' for init parameter 'expires', defaulting to 'on'.");
}
}
// filter parameter Cache-Control
String cacheControlMaxAgeParam = config.getInitParameter("max-age");
if (cacheControlMaxAgeParam != null) {
if (cacheControlMaxAgeParam.equalsIgnoreCase("no init")) {
setCacheControlMaxAge(MAX_AGE_NO_INIT);
} else if (cacheControlMaxAgeParam.equalsIgnoreCase("time")) {
setCacheControlMaxAge(MAX_AGE_TIME);
} else {
try {
setCacheControlMaxAge(Long.parseLong(cacheControlMaxAgeParam));
} catch (NumberFormatException nfe) {
log.error("OSCache: Unexpected value for the init parameter 'max-age', defaulting to '60'. Message=" + nfe.getMessage());
}
}
}
// filter parameter ICacheKeyProvider
ICacheKeyProvider cacheKeyProviderParam = (ICacheKeyProvider)instantiateFromInitParam("ICacheKeyProvider", ICacheKeyProvider.class, this.getClass().getName());
if (cacheKeyProviderParam != null) {
setCacheKeyProvider(cacheKeyProviderParam);
}
// filter parameter ICacheGroupsProvider
ICacheGroupsProvider cacheGroupsProviderParam = (ICacheGroupsProvider)instantiateFromInitParam("ICacheGroupsProvider", ICacheGroupsProvider.class, this.getClass().getName());
if (cacheGroupsProviderParam != null) {
setCacheGroupsProvider(cacheGroupsProviderParam);
}
// filter parameter EntryRefreshPolicy
EntryRefreshPolicy expiresRefreshPolicyParam = (EntryRefreshPolicy)instantiateFromInitParam("EntryRefreshPolicy", EntryRefreshPolicy.class, ExpiresRefreshPolicy.class.getName());
if (expiresRefreshPolicyParam != null) {
setExpiresRefreshPolicy(expiresRefreshPolicyParam);
} else {
// setting the refresh period for this cache filter
setExpiresRefreshPolicy(new ExpiresRefreshPolicy(time));
}
// filter parameter scope
String disableCacheOnMethodsParam = config.getInitParameter("disableCacheOnMethods");
if (StringUtil.hasLength(disableCacheOnMethodsParam)) {
disableCacheOnMethods = StringUtil.split(disableCacheOnMethodsParam, ',");
// log.error("OSCache: Wrong value '" + disableCacheOnMethodsParam + "' for init parameter 'disableCacheOnMethods', defaulting to 'null'.");
}
}
|
public boolean isCacheable(ServletRequest request) {
boolean cacheable = request instanceof HttpServletRequest;
if (cacheable) {
HttpServletRequest requestHttp = (HttpServletRequest) request;
// CACHE-272 don't cache special http request methods
if ((disableCacheOnMethods != null) && (disableCacheOnMethods.contains(requestHttp.getMethod()))) {
return false;
}
if (nocache == NOCACHE_SESSION_ID_IN_URL) { // don't cache requests if session id is in the URL
cacheable = !requestHttp.isRequestedSessionIdFromURL();
}
}
return cacheable;
}
isCacheable is a method allowing a subclass to decide if a request is
cachable or not. |
public boolean isCacheable(CacheHttpServletResponseWrapper cacheResponse) {
// TODO implement CACHE-137 here
// Only cache if the response was 200
return cacheResponse.getStatus() == HttpServletResponse.SC_OK;
}
isCacheable is a method allowing subclass to decide if a response is
cachable or not. |
public boolean isFilteredBefore(ServletRequest request) {
return request.getAttribute(requestFiltered) != null;
}
Checks if the request was filtered before, so
guarantees to be executed once per request. You
can override this methods to define a more specific
behaviour. |
public boolean isFragment(HttpServletRequest request) {
if (fragment == FRAGMENT_AUTODETECT) {
return request.getAttribute("javax.servlet.include.request_uri") != null;
} else {
return (fragment == FRAGMENT_NO) ? false : true;
}
}
Checks if the request is a fragment in a page.
According to Java Servlet API 2.2 (8.2.1 Dispatching Requests, Included
Request Parameters), when a servlet is being used from within an include,
the attribute javax.servlet.include.request_uri is set.
According to Java Servlet API 2.3 this is excepted for servlets obtained
by using the getNamedDispatcher method. |
public void setCacheControlMaxAge(long cacheControlMaxAge) {
if ((cacheControlMaxAge == MAX_AGE_NO_INIT) || (cacheControlMaxAge == MAX_AGE_TIME)) {
this.cacheControlMaxAge = cacheControlMaxAge;
} else if (cacheControlMaxAge >= 0) {
// declare the cache control as a constant
// TODO check if this value can be stored as a positive long without changing it
this.cacheControlMaxAge = - cacheControlMaxAge;
} else {
log.warn("OSCache: 'max-age' must be at least a positive integer, defaulting to '60'. ");
this.cacheControlMaxAge = -60;
}
}
max-age - defines the cache control response header max-age. Acceptable values are
MAX_AGE_NO_INIT for don't initializing the max-age cache control,
MAX_AGE_TIME the max-age information will be based on the time parameter and creation time of the content (expiration timestamp minus current timestamp), and
[positive integer] value constant in seconds to be set in every response, the default value is 60. |
public void setCacheGroupsProvider(ICacheGroupsProvider cacheGroupsProvider) {
if (cacheGroupsProvider == null) throw new IllegalArgumentException("The ICacheGroupsProvider is null.");
this.cacheGroupsProvider = cacheGroupsProvider;
}
ICacheGroupsProvider - Class implementing the interface ICacheGroupsProvider.
A developer can implement a method which provides cache groups based on the request,
the servlect cache administrator and cache. The parameter has to be not null. |
public void setCacheKeyProvider(ICacheKeyProvider cacheKeyProvider) {
if (cacheKeyProvider == null) throw new IllegalArgumentException("The ICacheKeyProvider is null.");
this.cacheKeyProvider = cacheKeyProvider;
}
ICacheKeyProvider - Class implementing the interface ICacheKeyProvider.
A developer can implement a method which provides cache keys based on the request,
the servlect cache administrator and cache. The parameter has to be not null. |
public void setCacheScope(int cacheScope) {
if ((cacheScope != PageContext.APPLICATION_SCOPE) && (cacheScope != PageContext.SESSION_SCOPE))
throw new IllegalArgumentException("Acceptable values for cache scope are PageContext.APPLICATION_SCOPE or PageContext.SESSION_SCOPE");
this.cacheScope = cacheScope;
}
scope - the default scope to cache content. Acceptable values
are PageContext.APPLICATION_SCOPE (default) and PageContext.SESSION_SCOPE. |
public void setCron(String cron) {
this.cron = cron;
}
cron - defines an expression that determines when the page content will expire.
This allows content to be expired at particular dates and/or times, rather than once
a cache entry reaches a certain age. |
public void setDisableCacheOnMethods(List disableCacheOnMethods) {
this.disableCacheOnMethods = disableCacheOnMethods;
}
disableCacheOnMethods - Defines the http method name for which cacheing should be disabled.
The default value is null for cacheing all requests without regarding the method name. |
public void setExpires(long expires) {
if ((expires < EXPIRES_TIME) || (expires > EXPIRES_ON)) throw new IllegalArgumentException("Expires value out of range.");
this.expires = expires;
}
expires - defines if the expires header will be sent in the response. Acceptable values are
EXPIRES_OFF for don't sending the header, even it is set in the filter chain,
EXPIRES_ON (default) for sending it if it is set in the filter chain and
EXPIRES_TIME the expires information will be intialized based on the time parameter and creation time of the content. |
public void setExpiresRefreshPolicy(EntryRefreshPolicy expiresRefreshPolicy) {
if (expiresRefreshPolicy == null) throw new IllegalArgumentException("The EntryRefreshPolicy is null.");
this.expiresRefreshPolicy = expiresRefreshPolicy;
}
EntryRefreshPolicy - Class implementing the interface EntryRefreshPolicy.
A developer can implement a class which provides a different custom cache invalidation policy for a specific cache entry.
If not specified, the default policy is timed entry expiry as specified with the time parameter described above. |
public void setFragment(int fragment) {
if ((fragment < FRAGMENT_AUTODETECT) || (fragment > FRAGMENT_YES)) throw new IllegalArgumentException("Fragment value out of range.");
this.fragment = fragment;
}
fragment - defines if this filter handles fragments of a page. Acceptable values
are FRAGMENT_AUTODETECT (default) for auto detect, FRAGMENT_NO and FRAGMENT_YES. |
public void setLastModified(long lastModified) {
if ((lastModified < LAST_MODIFIED_INITIAL) || (lastModified > LAST_MODIFIED_ON)) throw new IllegalArgumentException("LastModified value out of range.");
this.lastModified = lastModified;
}
lastModified - defines if the last modified header will be sent in the response. Acceptable values are
LAST_MODIFIED_OFF for don't sending the header, even it is set in the filter chain,
LAST_MODIFIED_ON for sending it if it is set in the filter chain and
LAST_MODIFIED_INITIAL (default) the last modified information will be set based on the current time and changes are allowed. |
public void setNocache(int nocache) {
if ((nocache < NOCACHE_OFF) || (nocache > NOCACHE_SESSION_ID_IN_URL)) throw new IllegalArgumentException("Nocache value out of range.");
this.nocache = nocache;
}
nocache - defines which objects shouldn't be cached. Acceptable values
are NOCACHE_OFF (default) and NOCACHE_SESSION_ID_IN_URL if the session id is
contained in the URL. |
public void setTime(int time) {
this.time = time;
// check if ExpiresRefreshPolicy has to be reset
if (expiresRefreshPolicy instanceof ExpiresRefreshPolicy) {
((ExpiresRefreshPolicy) expiresRefreshPolicy).setRefreshPeriod(time);
}
}
time - the default time (in seconds) to cache content for. The default
value is 3600 seconds (one hour). Specifying -1 (indefinite expiry) as the cache
time will ensure a content does not become stale until it is either explicitly
flushed or the expires refresh policy causes the entry to expire. |