FileDocCategorySizeDatePackage
CachingFilter.javaAPI DocGlassfish v2 API15780Fri May 04 22:35:26 BST 2007com.sun.appserv.web.cache.filter

CachingFilter

public class CachingFilter extends Object implements Filter, com.sun.appserv.web.cache.CacheManagerListener

Fields Summary
String
filterName
String
servletName
String
urlPattern
com.sun.appserv.web.cache.CacheManager
manager
com.sun.appserv.web.cache.CacheHelper
helper
com.sun.appserv.util.cache.Cache
cache
boolean
isEnabled
private static Logger
_logger
private static boolean
_isTraceEnabled
Constructors Summary
Methods Summary
public voidcacheManagerDisabled()
cache manager listener method

        if (_isTraceEnabled)
            _logger.fine("CachingFilter " + filterName + " received cacheManager disabled event.");

        this.isEnabled = false;
    
public voidcacheManagerEnabled()
cache manager listener method

        if (_isTraceEnabled)
            _logger.fine("CachingFilter " + filterName + " received cacheManager enabled event.");

        this.isEnabled = true;
    
public voiddestroy()
Called by the web container to indicate to a filter that it is being taken out of service. This method is only called once all threads within the filter's doFilter method have exited or after a timeout period has passed. After the web container calls this method, it will not call the doFilter method again on this instance of the filter.

    
public voiddoFilter(javax.servlet.ServletRequest srequest, javax.servlet.ServletResponse sresponse, javax.servlet.FilterChain chain)
The doFilter method of the Filter is called by the container each time a request/response pair is passed through the chain due to a client request for a resource at the end of the chain. The FilterChain passed in to this method allows the Filter to pass on the request and response to the next entity in the chain.

param
srequest the runtime request
param
sresponse the runtime response
param
chain the filter chain to in the request processing
throws
IOException, ServletException - First check if this HTTP method permits caching (using helper) if not, call the downstream filter and return. - Otherwise, get the key based on the request (using helper). - Check if we have a response entry in the cache already. - If there is entry and is valid, write out the response from that entry. - create a CachingResponse and CachingOutputStream wrappers and call the downstream filter

        String key;
        HttpServletRequest request = (HttpServletRequest)srequest;
        HttpServletResponse response = (HttpServletResponse)sresponse;

        request.setAttribute(DefaultCacheHelper.ATTR_CACHING_FILTER_NAME, 
                                                            filterName);
        request.setAttribute(CacheHelper.ATTR_CACHE_MAPPED_SERVLET_NAME, 
                                        servletName);
        request.setAttribute(CacheHelper.ATTR_CACHE_MAPPED_URL_PATTERN, 
                                        urlPattern);

        if (isEnabled && helper.isCacheable((HttpServletRequest)request) &&
                (key = helper.getCacheKey(request)) != null) {

            // have the key index for reuse
            int index = cache.getIndex(key);

            if (_isTraceEnabled) {
                _logger.fine("CachingFilter " + request.getServletPath() + 
                                " request is cacheable; key " + key + " index = " + index);
            }

            HttpCacheEntry entry = null;
            boolean entryReady = false, waitForRefresh = true;
            
            // if refresh is not needed then check the cache first
            if (!helper.isRefreshNeeded(request)) {
                do {
                    // lookup cache
                    entry = (HttpCacheEntry) cache.get(key);

                    if (entry != null && entry.isValid()) {
                        // see if there is cached entry and is valid
                        entryReady = true;
                        break;
                    }
                    else {
                        /** 
                         *  a cache entry needs to be generated or refreshed.
                         *  if there are more than one thread tries to fill/refresh
                         *  same cache entry, then all but the first thread will block.
                         */
                        waitForRefresh = cache.waitRefresh(index);
                    }
                } while (waitForRefresh);
            } else {
                if (_isTraceEnabled) {
                    _logger.fine("CachingFilter " + request.getServletPath() + 
                                " request needs a refresh; key " + key);
                }
            }

            // do we have a valid response?
            if (entryReady) {
                if (_isTraceEnabled) {
                    _logger.fine("CachingFilter " + request.getServletPath() + 
                                " serving response from the cache " + key);
                }
                sendCachedResponse(entry, response);
            } else {
                // call the target servlet

                HttpCacheEntry oldEntry;

                // setup the response wrapper (and the output stream)
                CachingResponseWrapper wrapper =
                                new CachingResponseWrapper(response);

                // call the target resource
                try {
                    chain.doFilter(srequest, (ServletResponse)wrapper);
                } catch (ServletException se) {
                    wrapper.clear();
                    throw se;
                } catch (IOException ioe) {
                    wrapper.clear();
                    throw ioe;
                }

                // see if the there weren't any errors
                if (!wrapper.isError()) {
                    // create/refresh the cached response entry

                    // compute the timeout
                    int timeout = helper.getTimeout(request);

                    // previous entry gets replaced
                    entry = wrapper.cacheResponse();

                    if (timeout == CacheHelper.TIMEOUT_VALUE_NOT_SET) {
                        // extracts this from the Expires: date header
                        Long lval = wrapper.getExpiresDateHeader();

                        if (lval == null) {
                            timeout = manager.getDefaultTimeout();
                            entry.computeExpireTime(timeout);
                        } else {
                            long expireTime = lval.longValue();

                            // set the time this entry would expires
                            entry.setExpireTime(expireTime);
                        }
                    } else {
                        entry.computeExpireTime(timeout);
                    }

                    oldEntry = (HttpCacheEntry)cache.put(key, entry,
                                                         entry.getSize());
                    cache.notifyRefresh(index);

                    // transmit the response body content
                    writeBody(entry, response);
                } else {
                    /** either there was an error or response from this
                     *  resource is not cacheable anymore; so, remove the
                     *  old entry from the cache.
                     */
                    oldEntry = (HttpCacheEntry)cache.remove(key);
                }

                // clear the wrapper (XXX: cache these??)
                wrapper.clear();
            }

            /** clear the old entry?
             *  may lead to NPEs with one thread that just replaced an entry
             *  which might have just obtained before the new entry is put
             *  in cache. Must implement some sort of ref count or leave it
             *  to garbage collector
             *  if (oldEntry != null)
             *      oldEntry.clear();
             */
        } else {
            if (_isTraceEnabled) {
                _logger.fine("CachingFilter " + request.getServletPath() + 
                                " pass thru; isEnabled = " + isEnabled);
            }
            request.removeAttribute(DefaultCacheHelper.ATTR_CACHING_FILTER_NAME);
            request.removeAttribute(CacheHelper.ATTR_CACHE_MAPPED_SERVLET_NAME);
            request.removeAttribute(CacheHelper.ATTR_CACHE_MAPPED_URL_PATTERN);

            // pass thru
            chain.doFilter(srequest, sresponse);
        }
    
public voidinit(javax.servlet.FilterConfig filterConfig)
Called by the web container to indicate to a filter that it is being placed into service. The servlet container calls the init method exactly once after instantiating the filter. The init method must complete successfully before the filter is asked to do any filtering work.

param
filterConfig filter config
throws
ServletException


	                                                       	 
	      

        filterName = filterConfig.getFilterName();
        servletName = filterConfig.getInitParameter("servletName");
        urlPattern = filterConfig.getInitParameter("URLPattern");

        ServletContext context = filterConfig.getServletContext();
        manager = (CacheManager)
                  context.getAttribute(CacheManager.CACHE_MANAGER_ATTR_NAME);

        if (manager != null && manager.isEnabled()) {
            this.cache = manager.getDefaultCache();
            this.helper = manager.getCacheHelperByFilterName(filterName);

            // add filter as a listener so caching can be disabled at runtime.
            manager.addCacheManagerListener(this);
            isEnabled = true;
        }

        if (_logger == null) {
            _logger = LogDomains.getLogger(LogDomains.PWC_LOGGER);
            _isTraceEnabled = _logger.isLoggable(Level.FINE);
        }

        if (_isTraceEnabled) {
            _logger.fine("CachingFilter " + filterName + " ready; isEnabled = " + isEnabled + " manager = " + manager);
        }
    
private voidsendCachedResponse(HttpCacheEntry entry, javax.servlet.http.HttpServletResponse response)
called by doFilter to send out the cached response

param
entry cached response entry
param
response response object to write out the response
throws
IOException and ServletException.

        // status code/message
        if (entry.statusCode != HttpCacheEntry.VALUE_NOT_SET) {
            response.setStatus(entry.statusCode);
        }

        // set the outbound response headers
        for (Iterator iter = entry.responseHeaders.keySet().iterator(); 
                                                         iter.hasNext(); ) {
            String name = (String)iter.next();
            ArrayList values = (ArrayList)entry.responseHeaders.get(name);

            for (int i = 0; i < values.size(); i++) {
                response.addHeader(name, (String)values.get(i));
            }
        }

        // date headers
        for (Iterator iter = entry.dateHeaders.keySet().iterator(); 
                                                    iter.hasNext(); ) {
            String name = (String)iter.next();
            ArrayList values = (ArrayList)entry.dateHeaders.get(name);

            for (int i = 0; i < values.size(); i++) {
                response.addDateHeader(name, ((Long)values.get(i)).longValue());
            }
        }

        // cookies
        for (int i = 0; i < entry.cookies.size(); i++) {
            response.addCookie((Cookie)entry.cookies.get(i));
        }

        // content type, length and locale
        if (entry.contentLength != HttpCacheEntry.VALUE_NOT_SET) {
            response.setContentLength(entry.contentLength);
        }
        if (entry.contentType != null) {
            response.setContentType(entry.contentType);
        }
        if (entry.locale != null) {
            response.setLocale(entry.locale);
        }

        // the response body
        writeBody(entry, response);
    
private voidwriteBody(HttpCacheEntry entry, javax.servlet.http.HttpServletResponse response)
called by doFilter/sendCachedResponse to write the body content

param
entry cached response entry
param
response response object to write out the response
throws
IOException and ServletException.

        ServletOutputStream out = response.getOutputStream();
        out.write(entry.bytes);