
|
If you were logged in you would be able to see more operations.
|
|
|
OSCache
Created: 18/Feb/05 05:14 AM
Updated: 19/Mar/07 05:57 PM
|
|
| Component/s: |
Filters
|
| Affects Version/s: |
2.1
|
| Fix Version/s: |
2.4
|
|
|
Sub-Tasks:
|
All
|
Open
|
|
| Sub-Task Progress: |
|
|
|
I think that would be nice to add some improvements to the CacheFilter class in order to make it easier to have a sub-class.
For example a "isCachable(request, response)" method could be called before to store a response into the cache, giving the possibility to implemented a custom algorithm in a sub-class.
A fine implementation of the method would be to test those "Pragme: no-cache" or "Cache-Control: private" response headers ...
Another good point would be to have a method called to decide whether a cache entry sould be used or not.
Suggestion:
/**
* isCachable is a method allowing subclass to decide if a response
* id cachable or not.
*
* @param request The HTTP servlet request
* @param response The HTTP servlet response
* @return Returns a boolean indicating if the request can be cached or not.
*/
protected boolean isCachable(HttpServletRequest request,
HttpServletResponse response) {
Boolean cachable = true ;
if (response.containsHeader("Cache-control")) {
cachable = false ;
} else {
cachable = true ;
}
if (log.isDebugEnabled()) {
log.debug("<cache>: the response "
+ ((cachable.booleanValue()) ? "is" : "is not")
+ " cachable.");
}
return cachable ;
}
/**
* doFilterPreprocess is a method allowing subclass to perform actions
* at the start of the filtering process.
*
* @param request The HTTP servlet request
* @param response The HTTP servlet response
* @param key The cache key used to access the cache
* @param cache The cache object
*/
protected void doFilterPreprocess(HttpServletRequest request,
HttpServletResponse response,
String key,
Cache cache) {
if (log.isDebugEnabled()) {
log.debug("<cache>: doFilter for key " + key + " is starting.");
}
}
/**
* doFilterPostprocess is a method allowing subclass to perform actions
* at the end of the filtering process.
*
* @param request The HTTP servlet request
* @param response The HTTP servlet response
* @param key The cache key used to access the cache
* @param cache The cache object
*/
protected void doFilterPostprocess(HttpServletRequest request,
HttpServletResponse response,
String key,
Cache cache) {
if (log.isDebugEnabled()) {
log.debug("<cache>: doFilter for key " + key + " has been done.");
}
}
/**
* useCache is a method allowing subclass to decide if the use
* of the cache is requested or not.
*
* @param request The HTTP servlet request
* @param response The HTTP servlet response
* @param key The cache key used to access the cache
* @param cache The cache object
* @return Returns a boolean indicating if the caching is requested or not.
*/
protected boolean useCache(HttpServletRequest request,
HttpServletResponse response,
String key,
Cache cache) {
boolean useIt = true ;
if (log.isDebugEnabled()) {
log.debug("<cache>: the cache " + ((useIt) ? "will" : "will not") + " be used.");
}
return useIt ;
}
/**
* The doFilter call caches the response by wrapping the <code>HttpServletResponse</code>
* object so that the output stream can be caught.
* This works by splitting off the output stream
* into two with the {@link SplitServletOutputStream} class.
* One stream gets written out to the response as normal,
* the other is fed into a byte array inside a {@link ResponseContent}
* object.
*
* @param request The servlet request
* @param response The servlet response
* @param chain The filter chain
* @throws ServletException IOException
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
if (log.isInfoEnabled()) {
log.info("<cache>: filter in scope " + cacheScope);
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String key = admin.generateEntryKey(null, httpRequest, cacheScope);
Cache cache = admin.getCache(httpRequest, cacheScope);
boolean cachedResponse ;
doFilterPreprocess(httpRequest, httpResponse, key, cache) ;
boolean useCache = useCache(httpRequest, httpResponse, key, cache) ;
if (useCache) {
try {
if (log.isInfoEnabled()) {
log.info("<cache>: Reading Caching entry " + key + " ...");
}
ResponseContent respContent = (ResponseContent) cache.getFromCache(key, time);
long clientLastModified = httpRequest.getDateHeader("If-Modified-Since"); // will return -1 if no header...
if (clientLastModified >= respContent.getLastModified()) {
httpResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
} else {
respContent.writeTo(response);
}
cachedResponse = true ;
} catch (NeedsRefreshException nre) {
cachedResponse = false ;
}
} else {
cachedResponse = false ;
}
if (! cachedResponse) {
boolean updateSucceeded = false;
try {
if (log.isInfoEnabled()) {
log.info("<cache>: New cache entry, cache stale or cache scope flushed for " + key);
}
CacheHttpServletResponseWrapper cacheResponse = new CacheHttpServletResponseWrapper(httpResponse);
chain.doFilter(request, cacheResponse);
cacheResponse.flushBuffer();
// Only cache if the response was 200
if (cacheResponse.getStatus() == HttpServletResponse.SC_OK) {
if (isCachable(httpRequest, httpResponse)) {
//Store as the cache content the result of the response
cache.putInCache(key, cacheResponse.getContent());
updateSucceeded = true;
}
}
} finally {
if (!updateSucceeded) {
cache.cancelUpdate(key);
}
}
}
doFilterPreprocess(httpRequest, httpResponse, key, cache) ;
}
Regards
Laurent
|
|
Description
|
I think that would be nice to add some improvements to the CacheFilter class in order to make it easier to have a sub-class.
For example a "isCachable(request, response)" method could be called before to store a response into the cache, giving the possibility to implemented a custom algorithm in a sub-class.
A fine implementation of the method would be to test those "Pragme: no-cache" or "Cache-Control: private" response headers ...
Another good point would be to have a method called to decide whether a cache entry sould be used or not.
Suggestion:
/**
* isCachable is a method allowing subclass to decide if a response
* id cachable or not.
*
* @param request The HTTP servlet request
* @param response The HTTP servlet response
* @return Returns a boolean indicating if the request can be cached or not.
*/
protected boolean isCachable(HttpServletRequest request,
HttpServletResponse response) {
Boolean cachable = true ;
if (response.containsHeader("Cache-control")) {
cachable = false ;
} else {
cachable = true ;
}
if (log.isDebugEnabled()) {
log.debug("<cache>: the response "
+ ((cachable.booleanValue()) ? "is" : "is not")
+ " cachable.");
}
return cachable ;
}
/**
* doFilterPreprocess is a method allowing subclass to perform actions
* at the start of the filtering process.
*
* @param request The HTTP servlet request
* @param response The HTTP servlet response
* @param key The cache key used to access the cache
* @param cache The cache object
*/
protected void doFilterPreprocess(HttpServletRequest request,
HttpServletResponse response,
String key,
Cache cache) {
if (log.isDebugEnabled()) {
log.debug("<cache>: doFilter for key " + key + " is starting.");
}
}
/**
* doFilterPostprocess is a method allowing subclass to perform actions
* at the end of the filtering process.
*
* @param request The HTTP servlet request
* @param response The HTTP servlet response
* @param key The cache key used to access the cache
* @param cache The cache object
*/
protected void doFilterPostprocess(HttpServletRequest request,
HttpServletResponse response,
String key,
Cache cache) {
if (log.isDebugEnabled()) {
log.debug("<cache>: doFilter for key " + key + " has been done.");
}
}
/**
* useCache is a method allowing subclass to decide if the use
* of the cache is requested or not.
*
* @param request The HTTP servlet request
* @param response The HTTP servlet response
* @param key The cache key used to access the cache
* @param cache The cache object
* @return Returns a boolean indicating if the caching is requested or not.
*/
protected boolean useCache(HttpServletRequest request,
HttpServletResponse response,
String key,
Cache cache) {
boolean useIt = true ;
if (log.isDebugEnabled()) {
log.debug("<cache>: the cache " + ((useIt) ? "will" : "will not") + " be used.");
}
return useIt ;
}
/**
* The doFilter call caches the response by wrapping the <code>HttpServletResponse</code>
* object so that the output stream can be caught.
* This works by splitting off the output stream
* into two with the {@link SplitServletOutputStream} class.
* One stream gets written out to the response as normal,
* the other is fed into a byte array inside a {@link ResponseContent}
* object.
*
* @param request The servlet request
* @param response The servlet response
* @param chain The filter chain
* @throws ServletException IOException
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
if (log.isInfoEnabled()) {
log.info("<cache>: filter in scope " + cacheScope);
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String key = admin.generateEntryKey(null, httpRequest, cacheScope);
Cache cache = admin.getCache(httpRequest, cacheScope);
boolean cachedResponse ;
doFilterPreprocess(httpRequest, httpResponse, key, cache) ;
boolean useCache = useCache(httpRequest, httpResponse, key, cache) ;
if (useCache) {
try {
if (log.isInfoEnabled()) {
log.info("<cache>: Reading Caching entry " + key + " ...");
}
ResponseContent respContent = (ResponseContent) cache.getFromCache(key, time);
long clientLastModified = httpRequest.getDateHeader("If-Modified-Since"); // will return -1 if no header...
if (clientLastModified >= respContent.getLastModified()) {
httpResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
} else {
respContent.writeTo(response);
}
cachedResponse = true ;
} catch (NeedsRefreshException nre) {
cachedResponse = false ;
}
} else {
cachedResponse = false ;
}
if (! cachedResponse) {
boolean updateSucceeded = false;
try {
if (log.isInfoEnabled()) {
log.info("<cache>: New cache entry, cache stale or cache scope flushed for " + key);
}
CacheHttpServletResponseWrapper cacheResponse = new CacheHttpServletResponseWrapper(httpResponse);
chain.doFilter(request, cacheResponse);
cacheResponse.flushBuffer();
// Only cache if the response was 200
if (cacheResponse.getStatus() == HttpServletResponse.SC_OK) {
if (isCachable(httpRequest, httpResponse)) {
//Store as the cache content the result of the response
cache.putInCache(key, cacheResponse.getContent());
updateSucceeded = true;
}
}
} finally {
if (!updateSucceeded) {
cache.cancelUpdate(key);
}
}
}
doFilterPreprocess(httpRequest, httpResponse, key, cache) ;
}
Regards
Laurent |
Show » |
|