cachefilter.java

来自「oscache-2.4.1-full」· Java 代码 · 共 783 行 · 第 1/3 页

JAVA
783
字号
/*
 * Copyright (c) 2002-2007 by OpenSymphony
 * All rights reserved.
 */
package com.opensymphony.oscache.web.filter;

import com.opensymphony.oscache.base.Cache;
import com.opensymphony.oscache.base.Config;
import com.opensymphony.oscache.base.EntryRefreshPolicy;
import com.opensymphony.oscache.base.NeedsRefreshException;
import com.opensymphony.oscache.util.ClassLoaderUtil;
import com.opensymphony.oscache.util.StringUtil;
import com.opensymphony.oscache.web.ServletCacheAdministrator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.util.List;
import java.util.Properties;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.PageContext;

/**
 * CacheFilter is a filter that allows for server-side caching of post-processed servlet content.<p>
 *
 * It also gives great programatic control over refreshing, flushing and updating the cache.<p>
 *
 * @author <a href="mailto:sergek [ AT ] lokitech.com">Serge Knystautas</a>
 * @author <a href="mailto:mike [ AT ] atlassian.com">Mike Cannon-Brookes</a>
 * @author <a href="mailto:ltorunski [ AT ] t-online.de">Lars Torunski</a>
 * @version $Revision: 434 $
 */
public class CacheFilter implements Filter, ICacheKeyProvider, ICacheGroupsProvider {
    // Header
    public static final String HEADER_LAST_MODIFIED = "Last-Modified";
    public static final String HEADER_CONTENT_TYPE = "Content-Type";
    public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
    public static final String HEADER_EXPIRES = "Expires";
    public static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
    public static final String HEADER_CACHE_CONTROL = "Cache-Control";
    public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";

    // Fragment parameter
    public static final int FRAGMENT_AUTODETECT = -1;
    public static final int FRAGMENT_NO = 0;
    public static final int FRAGMENT_YES = 1;
    
    // No cache parameter
    public static final int NOCACHE_OFF = 0;
    public static final int NOCACHE_SESSION_ID_IN_URL = 1;
    
    // Last Modified parameter
    public static final long LAST_MODIFIED_OFF = 0;
    public static final long LAST_MODIFIED_ON = 1;
    public static final long LAST_MODIFIED_INITIAL = -1;
    
    // Expires parameter
    public static final long EXPIRES_OFF = 0;
    public static final long EXPIRES_ON = 1;
    public static final long EXPIRES_TIME = -1;
    
    // Cache Control
    public static final long MAX_AGE_NO_INIT = Long.MIN_VALUE;
    public static final long MAX_AGE_TIME = Long.MAX_VALUE;

    // request attribute to avoid reentrance
    private final static String REQUEST_FILTERED = "__oscache_filtered__";
    private String requestFiltered;

    // the policy for the expires header
    private EntryRefreshPolicy expiresRefreshPolicy;
    
    // the logger
    private final Log log = LogFactory.getLog(this.getClass());

    // filter variables
    private FilterConfig config;
    private ServletCacheAdministrator admin = null;
    private int cacheScope = PageContext.APPLICATION_SCOPE; // filter scope - default is APPLICATION
    private int fragment = FRAGMENT_AUTODETECT; // defines if this filter handles fragments of a page - default is auto detect
    private int time = 60 * 60; // time before cache should be refreshed - default one hour (in seconds)
    private String cron = null; // A cron expression that determines when this cached content will expire - default is null
    private int nocache = NOCACHE_OFF; // defines special no cache option for the requests - default is off
    private long lastModified = LAST_MODIFIED_INITIAL; // defines if the last-modified-header will be sent - default is intial setting
    private long expires = EXPIRES_ON; // defines if the expires-header will be sent - default is on
    private long cacheControlMaxAge = -60; // defines which max-age in Cache-Control to be set - default is 60 seconds for max-age
    private ICacheKeyProvider cacheKeyProvider = this; // the provider of the cache key - default is the CacheFilter itselfs
    private ICacheGroupsProvider cacheGroupsProvider = this; // the provider of the cache groups - default is the CacheFilter itselfs
    private List disableCacheOnMethods = null; // caching can be disabled by defining the http methods - default is off

    /**
     * Filter clean-up
     */
    public void destroy() {
        //Not much to do...
    }

    /**
     * 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("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);
                }
            }
        }
    }

    /**
     * Initialize the filter. This retrieves a {@link ServletCacheAdministrator}
     * instance and configures the filter based on any initialization parameters.<p>
     * The supported initialization parameters are:
     * <ul>
     * 
     * <li><b>oscache-properties-file</b> - the properties file that contains the OSCache configuration
     * options to be used by the Cache that this Filter should use.</li>
     * 
     * @param filterConfig The filter configuration
     */
    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);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?