Commits

Anonymous committed 351c185

CACHE-266 ServletCacheAdministrator no longer a "Servlet Singleton"
CACHE-277 Allow reentrance over different filter configurations

Comments (0)

Files changed (2)

src/java/com/opensymphony/oscache/web/ServletCacheAdministrator.java

     public final static String APPLICATION_SCOPE_NAME = "application";
 
     /**
-    * The key under which the CacheAdministrator will be stored in the ServletContext
+    * The suffix added to the cache key used to store a 
+    * ServletCacheAdministrator will be stored in the ServletContext
     */
-    private final static String CACHE_ADMINISTRATOR_KEY = "__oscache_admin";
+    private final static String CACHE_ADMINISTRATOR_KEY_SUFFIX = "_admin";
+
+    /**
+    * The key under which an array of all ServletCacheAdministrator objects 
+    * will be stored in the ServletContext
+    */
+    private final static String CACHE_ADMINISTRATORS_KEY = "__oscache_admins";
 
     /**
     * Key used to store the current scope in the configuration. This is a hack
     }
 
     /**
+     * Obtain an instance of the CacheAdministrator for the specified key
+     *
+     * @param context The ServletContext that this CacheAdministrator is a Singleton under
+     * @param key the cachekey or admincachekey for the CacheAdministrator wanted
+     * @return Returns the CacheAdministrator instance for this context, or null if no
+     * CacheAdministrator exists with the key supplied
+     */
+     public static ServletCacheAdministrator getInstanceFromKey(ServletContext context, String key) {
+    	 // Note we do not bother to check if the key is null because it mustn't.
+         if (!key.endsWith(CACHE_ADMINISTRATOR_KEY_SUFFIX)) {
+        	 key = key + CACHE_ADMINISTRATOR_KEY_SUFFIX;
+         }
+         return (ServletCacheAdministrator) context.getAttribute(key);
+     }
+
+    /**
     * Obtain an instance of the CacheAdministrator
     *
     * @param context The ServletContext that this CacheAdministrator is a Singleton under
     * are loaded from the oscache.properties file in the classpath.
     * @return Returns the CacheAdministrator instance for this context
     */
-    public synchronized static ServletCacheAdministrator getInstance(ServletContext context, Properties p) {
+    public synchronized static ServletCacheAdministrator getInstance(ServletContext context, Properties p)
+    {
+    	String adminKey = null; 
+    	if (p!= null) {
+    		adminKey = p.getProperty(CACHE_KEY_KEY);
+    	}
+    	if (adminKey == null) {
+    		adminKey = DEFAULT_CACHE_KEY;
+    	}
+		adminKey += CACHE_ADMINISTRATOR_KEY_SUFFIX;
 
-        ServletCacheAdministrator admin = (ServletCacheAdministrator) context.getAttribute(CACHE_ADMINISTRATOR_KEY);
+        ServletCacheAdministrator admin = (ServletCacheAdministrator) context.getAttribute(adminKey);
 
         // First time we need to create the administrator and store it in the
         // servlet context
         if (admin == null) {
             admin = new ServletCacheAdministrator(context, p);
-            context.setAttribute(CACHE_ADMINISTRATOR_KEY, admin);
+            Map admins = (Map) context.getAttribute(CACHE_ADMINISTRATORS_KEY);
+            if (admins == null) {
+            	admins = new HashMap();
+            }
+            admins.put(adminKey, admin);
+            context.setAttribute(CACHE_ADMINISTRATORS_KEY, admins);
+            context.setAttribute(adminKey, admin);
 
             if (log.isInfoEnabled()) {
-                log.info("Created new instance of ServletCacheAdministrator");
+                log.info("Created new instance of ServletCacheAdministrator with key "+adminKey);
             }
 
             admin.getAppScopeCache(context);
     }
 
     /**
-    * Shuts down the cache administrator. This should usually only be called
-    * when the controlling application shuts down.
+    * Shuts down all servlet cache administrators. This should usually only 
+    * be called when the controlling application shuts down.
     */
-    public static void destroyInstance(ServletContext context) {
+    public static void destroyInstance(ServletContext context)
+    {
         ServletCacheAdministrator admin;
-        admin = (ServletCacheAdministrator) context.getAttribute(CACHE_ADMINISTRATOR_KEY);
-
-        if (admin != null) {
-            // Finalize the application scope cache
-            Cache cache = (Cache) context.getAttribute(admin.getCacheKey());
-
-            if (cache != null) {
-                admin.finalizeListeners(cache);
-                context.removeAttribute(admin.getCacheKey());
-                context.removeAttribute(CACHE_ADMINISTRATOR_KEY);
-                cache = null;
-
-                if (log.isInfoEnabled()) {
-                    log.info("Shut down the ServletCacheAdministrator");
-                }
-            }
-
-            admin = null;
+        Map admins = (Map) context.getAttribute(CACHE_ADMINISTRATORS_KEY);
+        if (admins != null)
+        {
+        	Set keys = admins.keySet();
+        	Iterator it = keys.iterator();
+        	while (it.hasNext())
+        	{
+        		String adminKey = (String) it.next();
+        		admin = (ServletCacheAdministrator) admins.get( adminKey );
+        		if (admin != null)
+        		{
+                    // Finalize the application scope cache
+                    Cache cache = (Cache) context.getAttribute(admin.getCacheKey());
+                    if (cache != null) {
+                    	admin.finalizeListeners(cache);
+                        context.removeAttribute(admin.getCacheKey());
+                        context.removeAttribute(adminKey);
+                        cache = null;
+                        if (log.isInfoEnabled()) {
+                            log.info("Shut down the ServletCacheAdministrator "+adminKey);
+                        }
+                    }
+                    admin = null;
+        		}
+        	}
+        	context.removeAttribute(CACHE_ADMINISTRATORS_KEY);
         }
     }
 
+
     /**
     * Grabs the cache for the specified scope
     *

src/java/com/opensymphony/oscache/web/filter/CacheFilter.java

 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.web.ServletCacheAdministrator;
 import org.apache.commons.logging.LogFactory;
 
 import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
 
 import javax.servlet.*;
 import javax.servlet.http.HttpServletRequest;
     public static final long MAX_AGE_TIME = Long.MAX_VALUE;
 
     // request attribute to avoid reentrance
-    private final static String REQUEST_FILTERED = "__oscache_filtered";
+    private final static String REQUEST_FILTERED = "__oscache_filtered__";
+    private String requestFiltered;
 
     // the policy for the expires header
     private EntryRefreshPolicy expiresRefreshPolicy;
             chain.doFilter(request, response);
             return;
         }
-        request.setAttribute(REQUEST_FILTERED, Boolean.TRUE);
+        request.setAttribute(requestFiltered, Boolean.TRUE);
 
         HttpServletRequest httpRequest = (HttpServletRequest) request;
 
      * The supported initialization parameters are:
      * <ul>
      * 
+     * <li><b>propertiesFile</b> - the properties file that contains the OSCache configuration
+     * options to be used by the Cache that this Filter should use.</li>
+     * 
      * <li><b>time</b> - the default time (in seconds) to cache content for. The default
      * value is 3600 seconds (one hour).</li>
      * 
      * @param filterConfig The filter configuration
      */
     public void init(FilterConfig filterConfig) {
-        //Get whatever settings we want...
+        log.info("Initializing OSCache CacheFilter.");
+
+        // Get whatever settings we want...
         config = filterConfig;
-        admin = ServletCacheAdministrator.getInstance(config.getServletContext());
+
+        // 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("propertiesfile");
+            
+            if (propertiesfile != null && propertiesfile.length() > 0)
+            {
+            	props = loadProps(propertiesfile);
+            }
+        } catch (Exception e) {
+            log.info("Could not get init parameter 'propertiesfile', using default.");
+        }
+    	if (props != null)
+    	{
+    		admin = ServletCacheAdministrator.getInstance(filterConfig.getServletContext(), props);
+    	}
+    	else
+    	{
+            admin = ServletCacheAdministrator.getInstance(config.getServletContext());
+    	}
 
         // filter parameter time
         try {
      * @return true if it is the first execution
      */
     protected boolean isFilteredBefore(ServletRequest request) {
-        return request.getAttribute(REQUEST_FILTERED) != null;
+        return request.getAttribute(requestFiltered) != null;
     }
 
     /**
         return  (acceptEncoding != null) && (acceptEncoding.indexOf("gzip") != -1);
     }
 
+    /**
+     * Load the specified properties file from the classpath. 
+     * If the file cannot be found or loaded, an error
+     * will be logged and no properties will be set.
+     */
+    private Properties loadProps(String filename) {
+        if (log.isDebugEnabled()) {
+            log.debug("Getting Properties file "+filename);
+        }
+
+        Properties properties = new Properties();
+        InputStream in = null;
+
+        try {
+            in = Config.class.getResourceAsStream(filename);
+            properties.load(in);
+            log.info("Properties " + properties);
+        } catch (Exception e) {
+            log.error("Error reading " + filename, e);
+            log.error("Ensure the " + filename + " file is readable and in your classpath.");
+        } finally {
+            try {
+                in.close();
+            } catch (Exception e) {
+                // Ignore errors that occur while closing file
+            }
+        }
+        return properties;
+    }
 }