Commits

Anonymous committed 5fedfdc

Issue number: N/A
Obtained from: Jason Parrott @ soe.sony.com
Submitted by: Andres March
Reviewed by: Not yet reviewed

Created an inhertible heirarchy for the DiskPersistenceListener plugin.
Moved most of the code to the abstract base class. Created a basic implementation and
added an implementation that hashes the key to provide better filenames.

Comments (0)

Files changed (14)

 build.properties
 dist
 *.i*
+.classpath
+.project

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

     private static final transient Log log = LogFactory.getLog(ServletCacheAdministrator.class);
 
     /**
-     * Constants for properties read/written from/to file
-     */
+ * Constants for properties read/written from/to file
+ */
     private final static String CACHE_USE_HOST_DOMAIN_KEY = "cache.use.host.domain.in.key";
     private final static String CACHE_KEY_KEY = "cache.key";
 
     /**
-     * The default cache key that is used to store the cache in context.
-     */
+ * The default cache key that is used to store the cache in context.
+ */
     private final static String DEFAULT_CACHE_KEY = "__oscache_cache";
 
     /**
-     * Constants for scope's name
-     */
+ * Constants for scope's name
+ */
     public final static String SESSION_SCOPE_NAME = "session";
     public final static String APPLICATION_SCOPE_NAME = "application";
 
     /**
-     * The key under which the CacheAdministrator will be stored in the ServletContext
-     */
+ * The key under which the CacheAdministrator will be stored in the ServletContext
+ */
     private final static String CACHE_ADMINISTRATOR_KEY = "__oscache_admin";
 
     /**
-     * Key used to store the current scope in the configuration. This is a hack
-     * to let the scope information get passed through to the DiskPersistenceListener,
-     * and will be removed in a future release.
-     */
+ * Key used to store the current scope in the configuration. This is a hack
+ * to let the scope information get passed through to the BasicDiskPersistenceListener,
+ * and will be removed in a future release.
+ */
     public final static String HASH_KEY_SCOPE = "scope";
 
     /**
-     * Key used to store the current session ID in the configuration. This is a hack
-     * to let the scope information get passed through to the DiskPersistenceListener,
-     * and will be removed in a future release.
-     */
+ * Key used to store the current session ID in the configuration. This is a hack
+ * to let the scope information get passed through to the BasicDiskPersistenceListener,
+ * and will be removed in a future release.
+ */
     public final static String HASH_KEY_SESSION_ID = "sessionId";
 
     /**
-     * Key used to store the servlet container temporary directory in the configuration.
-     * This is a hack to let the scope information get passed through to the
-     * DiskPersistenceListener, and will be removed in a future release.
-     */
+ * Key used to store the servlet container temporary directory in the configuration.
+ * This is a hack to let the scope information get passed through to the
+ * BasicDiskPersistenceListener, and will be removed in a future release.
+ */
     public final static String HASH_KEY_CONTEXT_TMPDIR = "context.tempdir";
 
     /**
-     * The string to use as a file separator.
-     */
+ * The string to use as a file separator.
+ */
     private final static String FILE_SEPARATOR = "/";
 
     /**
-     * The character to use as a file separator.
-     */
+ * The character to use as a file separator.
+ */
     private final static char FILE_SEPARATOR_CHAR = FILE_SEPARATOR.charAt(0);
 
     /**
-     * Constant for Key generation.
-     */
+ * Constant for Key generation.
+ */
     private final static short AVERAGE_KEY_LENGTH = 30;
 
     /**
-     * Usable caracters for key generation
-     */
+ * Usable caracters for key generation
+ */
     private static final String m_strBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
     /**
-     * Map containing the flush times of different scopes
-     */
+ * Map containing the flush times of different scopes
+ */
     private Map flushTimes;
 
     //private transient ServletContext context;
 
     /**
-     * Key to use for storing and retrieving Object in contexts (Servlet, session).
-     */
+ * Key to use for storing and retrieving Object in contexts (Servlet, session).
+ */
     private String cacheKey;
 
     /**
-     *  Set property cache.use.host.domain.in.key=true to add domain information to key
-     *  generation for hosting multiple sites.
-     */
+ *  Set property cache.use.host.domain.in.key=true to add domain information to key
+ *  generation for hosting multiple sites.
+ */
     private boolean useHostDomainInKey = false;
 
     /**
-     *        Create the cache administrator.
-     *
-     *        This will reset all the flush times and load the properties file.
-     */
+ *        Create the cache administrator.
+ *
+ *        This will reset all the flush times and load the properties file.
+ */
     private ServletCacheAdministrator(ServletContext context, Properties p) {
         super(p);
         config.set(HASH_KEY_CONTEXT_TMPDIR, context.getAttribute("javax.servlet.context.tempdir"));
     }
 
     /**
-     * Obtain an instance of the CacheAdministrator
-     *
-     * @param context The ServletContext that this CacheAdministrator is a Singleton under
-     * @return Returns the CacheAdministrator instance for this context
-     */
+ * Obtain an instance of the CacheAdministrator
+ *
+ * @param context The ServletContext that this CacheAdministrator is a Singleton under
+ * @return Returns the CacheAdministrator instance for this context
+ */
     public static ServletCacheAdministrator getInstance(ServletContext context) {
         return getInstance(context, null);
     }
 
     /**
-     * Obtain an instance of the CacheAdministrator
-     *
-     * @param context The ServletContext that this CacheAdministrator is a Singleton under
-     * @param p the properties to use for the cache if the cache administrator has not been
-     * created yet. Once the administrator has been created, the properties parameter is
-     * ignored for all future invocations. If a null value is passed in, then the properties
-     * are loaded from the oscache.properties file in the classpath.
-     * @return Returns the CacheAdministrator instance for this context
-     */
+ * Obtain an instance of the CacheAdministrator
+ *
+ * @param context The ServletContext that this CacheAdministrator is a Singleton under
+ * @param p the properties to use for the cache if the cache administrator has not been
+ * created yet. Once the administrator has been created, the properties parameter is
+ * ignored for all future invocations. If a null value is passed in, then the properties
+ * are loaded from the oscache.properties file in the classpath.
+ * @return Returns the CacheAdministrator instance for this context
+ */
     public static ServletCacheAdministrator getInstance(ServletContext context, Properties p) {
         ServletCacheAdministrator admin = null;
         admin = (ServletCacheAdministrator) context.getAttribute(CACHE_ADMINISTRATOR_KEY);
     }
 
     /**
-     * Shuts down the cache administrator. This should usually only be called
-     * when the controlling application shuts down.
-     */
+ * Shuts down the cache administrator. This should usually only be called
+ * when the controlling application shuts down.
+ */
     public static void destroyInstance(ServletContext context) {
         ServletCacheAdministrator admin = null;
         admin = (ServletCacheAdministrator) context.getAttribute(CACHE_ADMINISTRATOR_KEY);
     }
 
     /**
-     * Grabs the cache for the specified scope
-     *
-     * @param request The current request
-     * @param scope The scope of this cache (<code>PageContext.APPLICATION_SCOPE</code>
-     * or <code>PageContext.SESSION_SCOPE</code>)
-     * @return The cache
-     */
+ * Grabs the cache for the specified scope
+ *
+ * @param request The current request
+ * @param scope The scope of this cache (<code>PageContext.APPLICATION_SCOPE</code>
+ * or <code>PageContext.SESSION_SCOPE</code>)
+ * @return The cache
+ */
     public Cache getCache(HttpServletRequest request, int scope) {
         if (scope == PageContext.APPLICATION_SCOPE) {
             return getAppScopeCache(request.getSession(true).getServletContext());
     }
 
     /**
-     * A convenience method to retrieve the application scope cache
+ * A convenience method to retrieve the application scope cache
 
-     * @param context the current <code>ServletContext</code>
-     * @return the application scope cache. If none is present, one will
-     * be created.
-     */
+ * @param context the current <code>ServletContext</code>
+ * @return the application scope cache. If none is present, one will
+ * be created.
+ */
     public Cache getAppScopeCache(ServletContext context) {
         Cache cache = null;
         Object obj = context.getAttribute(getCacheKey());
     }
 
     /**
-     * A convenience method to retrieve the session scope cache
-     *
-     * @param session the current <code>HttpSession</code>
-     * @return the session scope cache for this session. If none is present,
-     * one will be created.
-     */
+ * A convenience method to retrieve the session scope cache
+ *
+ * @param session the current <code>HttpSession</code>
+ * @return the session scope cache for this session. If none is present,
+ * one will be created.
+ */
     public Cache getSessionScopeCache(HttpSession session) {
         Cache cache = null;
         Object obj = session.getAttribute(getCacheKey());
     }
 
     /**
-     * Get the cache key from the properties. Set it to a default value if it
-     * is not present in the properties
-     *
-     * @return The cache.key property or the DEFAULT_CACHE_KEY
-     */
+ * Get the cache key from the properties. Set it to a default value if it
+ * is not present in the properties
+ *
+ * @return The cache.key property or the DEFAULT_CACHE_KEY
+ */
     public String getCacheKey() {
         if (cacheKey == null) {
             cacheKey = getProperty(CACHE_KEY_KEY);
     }
 
     /**
-     * Set the flush time for a specific scope to a specific time
-     *
-     * @param date  The time to flush the scope
-     * @param scope The scope to be flushed
-     */
+ * Set the flush time for a specific scope to a specific time
+ *
+ * @param date  The time to flush the scope
+ * @param scope The scope to be flushed
+ */
     public void setFlushTime(Date date, int scope) {
         if (log.isInfoEnabled()) {
             log.info("Flushing scope " + scope + " at " + date);
     }
 
     /**
-     * Set the flush time for a specific scope to the current time.
-     *
-     * @param scope The scope to be flushed
-     */
+ * Set the flush time for a specific scope to the current time.
+ *
+ * @param scope The scope to be flushed
+ */
     public void setFlushTime(int scope) {
         setFlushTime(new Date(), scope);
     }
 
     /**
-     *        Get the flush time for a particular scope.
-     *
-     *        @param        scope        The scope to get the flush time for.
-     *        @return A date representing the time this scope was last flushed.
-     *        Returns null if it has never been flushed.
-     */
+ *        Get the flush time for a particular scope.
+ *
+ *        @param        scope        The scope to get the flush time for.
+ *        @return A date representing the time this scope was last flushed.
+ *        Returns null if it has never been flushed.
+ */
     public Date getFlushTime(int scope) {
         synchronized (flushTimes) {
             return (Date) flushTimes.get(new Integer(scope));
     }
 
     /**
-     * Retrieve an item from the cache
-     *
-     * @param scope The cache scope
-     * @param request The servlet request
-     * @param key The key of the object to retrieve
-     * @param refreshPeriod The time interval specifying if an entry needs refresh
-     * @return The requested object
-     * @throws NeedsRefreshException
-     */
+ * Retrieve an item from the cache
+ *
+ * @param scope The cache scope
+ * @param request The servlet request
+ * @param key The key of the object to retrieve
+ * @param refreshPeriod The time interval specifying if an entry needs refresh
+ * @return The requested object
+ * @throws NeedsRefreshException
+ */
     public Object getFromCache(int scope, HttpServletRequest request, String key, int refreshPeriod) throws NeedsRefreshException {
         Cache cache = getCache(request, scope);
         key = this.generateEntryKey(key, request, scope);
     }
 
     /**
-     * Checks if the given scope was flushed more recently than the CacheEntry provided.
-     * Used to determine whether to refresh the particular CacheEntry.
-     *
-     * @param cacheEntry The cache entry which we're seeing whether to refresh
-     * @param scope The scope we're checking
-     *
-     * @return Whether or not the scope has been flushed more recently than this cache entry was updated.
-     */
+ * Checks if the given scope was flushed more recently than the CacheEntry provided.
+ * Used to determine whether to refresh the particular CacheEntry.
+ *
+ * @param cacheEntry The cache entry which we're seeing whether to refresh
+ * @param scope The scope we're checking
+ *
+ * @return Whether or not the scope has been flushed more recently than this cache entry was updated.
+ */
     public boolean isScopeFlushed(CacheEntry cacheEntry, int scope) {
         Date flushDateTime = getFlushTime(scope);
 
     }
 
     /**
-     * Register a listener for Cache Map events.
-     *
-     * @param listener  The object that listens to events.
-     */
+ * Register a listener for Cache Map events.
+ *
+ * @param listener  The object that listens to events.
+ */
     public void addScopeEventListener(ScopeEventListener listener) {
         listenerList.add(ScopeEventListener.class, listener);
     }
 
     /**
-     * Cancels a pending cache update. This should only be called by a thread
-     * that received a {@link NeedsRefreshException} and was unable to generate
-     * some new cache content.
-     *
-     * @param scope The cache scope
-     * @param request The servlet request
-     * @param key The cache entry key to cancel the update of.
-     */
+ * Cancels a pending cache update. This should only be called by a thread
+ * that received a {@link NeedsRefreshException} and was unable to generate
+ * some new cache content.
+ *
+ * @param scope The cache scope
+ * @param request The servlet request
+ * @param key The cache entry key to cancel the update of.
+ */
     public void cancelUpdate(int scope, HttpServletRequest request, String key) {
         Cache cache = getCache(request, scope);
         key = this.generateEntryKey(key, request, scope);
     }
 
     /**
-     * Flush all scopes at a particular time
-     *
-     * @param date The time to flush the scope
-     */
+ * Flush all scopes at a particular time
+ *
+ * @param date The time to flush the scope
+ */
     public void flushAll(Date date) {
         synchronized (flushTimes) {
             setFlushTime(date, PageContext.APPLICATION_SCOPE);
     }
 
     /**
-     * Flush all scopes instantly.
-     */
+ * Flush all scopes instantly.
+ */
     public void flushAll() {
         flushAll(new Date());
     }
 
     /**
-     * Generates a cache entry key.
-     *
-     * If the string key is not specified, the HTTP request URI and QueryString is used.
-     * Operating systems that have a filename limitation less than 255 or have
-     * filenames that are case insensitive may have issues with key generation where
-     * two distinct pages map to the same key.
-     * <p>
-     * POST Requests (which have no distinguishing
-     * query string) may also generate identical keys for what is actually different pages.
-     * In these cases, specify an explicit key attribute for the CacheTag.
-     *
-     * @param key The key entered by the user
-     * @param request The current request
-     * @param scope The scope this cache entry is under
-     * @return The generated cache key
-     */
+ * Generates a cache entry key.
+ *
+ * If the string key is not specified, the HTTP request URI and QueryString is used.
+ * Operating systems that have a filename limitation less than 255 or have
+ * filenames that are case insensitive may have issues with key generation where
+ * two distinct pages map to the same key.
+ * <p>
+ * POST Requests (which have no distinguishing
+ * query string) may also generate identical keys for what is actually different pages.
+ * In these cases, specify an explicit key attribute for the CacheTag.
+ *
+ * @param key The key entered by the user
+ * @param request The current request
+ * @param scope The scope this cache entry is under
+ * @return The generated cache key
+ */
     public String generateEntryKey(String key, HttpServletRequest request, int scope) {
         return generateEntryKey(key, request, scope, null, null);
     }
 
     /**
-     * Generates a cache entry key.
-     *
-     * If the string key is not specified, the HTTP request URI and QueryString is used.
-     * Operating systems that have a filename limitation less than 255 or have
-     * filenames that are case insensitive may have issues with key generation where
-     * two distinct pages map to the same key.
-     * <p>
-     * POST Requests (which have no distinguishing
-     * query string) may also generate identical keys for what is actually different pages.
-     * In these cases, specify an explicit key attribute for the CacheTag.
-     *
-     * @param key The key entered by the user
-     * @param request The current request
-     * @param scope The scope this cache entry is under
-     * @param language The ISO-639 language code to distinguish different pages in application scope
-     * @return The generated cache key
-     */
+ * Generates a cache entry key.
+ *
+ * If the string key is not specified, the HTTP request URI and QueryString is used.
+ * Operating systems that have a filename limitation less than 255 or have
+ * filenames that are case insensitive may have issues with key generation where
+ * two distinct pages map to the same key.
+ * <p>
+ * POST Requests (which have no distinguishing
+ * query string) may also generate identical keys for what is actually different pages.
+ * In these cases, specify an explicit key attribute for the CacheTag.
+ *
+ * @param key The key entered by the user
+ * @param request The current request
+ * @param scope The scope this cache entry is under
+ * @param language The ISO-639 language code to distinguish different pages in application scope
+ * @return The generated cache key
+ */
     public String generateEntryKey(String key, HttpServletRequest request, int scope, String language) {
         return generateEntryKey(key, request, scope, language, null);
     }
 
     /**
-     * Generates a cache entry key.
-     * <p>
-     * If the string key is not specified, the HTTP request URI and QueryString is used.
-     * Operating systems that have a filename limitation less than 255 or have
-     * filenames that are case insensitive may have issues with key generation where
-     * two distinct pages map to the same key.
-     * <p>
-     * POST Requests (which have no distinguishing
-     * query string) may also generate identical keys for what is actually different pages.
-     * In these cases, specify an explicit key attribute for the CacheTag.
-     *
-     * @param key The key entered by the user
-     * @param request The current request
-     * @param scope The scope this cache entry is under
-     * @param language The ISO-639 language code to distinguish different pages in application scope
-     * @param suffix The ability to put a suffix at the end of the key
-     * @return The generated cache key
-     */
+ * Generates a cache entry key.
+ * <p>
+ * If the string key is not specified, the HTTP request URI and QueryString is used.
+ * Operating systems that have a filename limitation less than 255 or have
+ * filenames that are case insensitive may have issues with key generation where
+ * two distinct pages map to the same key.
+ * <p>
+ * POST Requests (which have no distinguishing
+ * query string) may also generate identical keys for what is actually different pages.
+ * In these cases, specify an explicit key attribute for the CacheTag.
+ *
+ * @param key The key entered by the user
+ * @param request The current request
+ * @param scope The scope this cache entry is under
+ * @param language The ISO-639 language code to distinguish different pages in application scope
+ * @param suffix The ability to put a suffix at the end of the key
+ * @return The generated cache key
+ */
     public String generateEntryKey(String key, HttpServletRequest request, int scope, String language, String suffix) {
         /**
-         * Used for generating cache entry keys.
-         */
+ * Used for generating cache entry keys.
+ */
         StringBuffer cBuffer = new StringBuffer(AVERAGE_KEY_LENGTH);
 
         // Append the language if available
     }
 
     /**
-     * Creates a string that contains all of the request parameters and their
-     * values in a single string. This is very similar to
-     * <code>HttpServletRequest.getQueryString()</code> except the parameters are
-     * sorted by name, and if there is a <code>jsessionid</code> parameter it is
-     * filtered out.<p>
-     * If the request has no parameters, this method returns <code>null</code>.
-     */
+ * Creates a string that contains all of the request parameters and their
+ * values in a single string. This is very similar to
+ * <code>HttpServletRequest.getQueryString()</code> except the parameters are
+ * sorted by name, and if there is a <code>jsessionid</code> parameter it is
+ * filtered out.<p>
+ * If the request has no parameters, this method returns <code>null</code>.
+ */
     protected String getSortedQueryString(HttpServletRequest request) {
         Map paramMap = request.getParameterMap();
 
     }
 
     /**
-     * Log error messages to commons logging.
-     *
-     * @param message   Message to log.
-     */
+ * Log error messages to commons logging.
+ *
+ * @param message   Message to log.
+ */
     public void logError(String message) {
         log.error("[oscache]: " + message);
     }
 
     /**
-     * Put an object in the cache
-     *
-     * @param scope The cache scope
-     * @param request The servlet request
-     * @param key The object key
-     * @param content The object to add
-     */
+ * Put an object in the cache
+ *
+ * @param scope The cache scope
+ * @param request The servlet request
+ * @param key The object key
+ * @param content The object to add
+ */
     public void putInCache(int scope, HttpServletRequest request, String key, Object content) {
         putInCache(scope, request, key, content, null);
     }
 
     /**
-     * Put an object in the cache
-     *
-     * @param scope The cache scope
-     * @param request The servlet request
-     * @param key The object key
-     * @param content The object to add
-     * @param policy The refresh policy
-     */
+ * Put an object in the cache
+ *
+ * @param scope The cache scope
+ * @param request The servlet request
+ * @param key The object key
+ * @param content The object to add
+ * @param policy The refresh policy
+ */
     public void putInCache(int scope, HttpServletRequest request, String key, Object content, EntryRefreshPolicy policy) {
         Cache cache = getCache(request, scope);
         key = this.generateEntryKey(key, request, scope);
     }
 
     /**
-     * Sets the cache capacity (number of items). If the cache contains
-     * more than <code>capacity</code> items then items will be removed
-     * to bring the cache back down to the new size.
-     *
-     * @param scope The cache scope
-     * @param request The servlet request
-     * @param capacity The new capacity
-     */
+ * Sets the cache capacity (number of items). If the cache contains
+ * more than <code>capacity</code> items then items will be removed
+ * to bring the cache back down to the new size.
+ *
+ * @param scope The cache scope
+ * @param request The servlet request
+ * @param capacity The new capacity
+ */
     public void setCacheCapacity(int scope, HttpServletRequest request, int capacity) {
         setCacheCapacity(capacity);
         getCache(request, scope).setCapacity(capacity);
     }
 
     /**
-     * Unregister a listener for Cache Map events.
-     *
-     * @param listener  The object that currently listens to events.
-     */
+ * Unregister a listener for Cache Map events.
+ *
+ * @param listener  The object that currently listens to events.
+ */
     public void removeScopeEventListener(ScopeEventListener listener) {
         listenerList.remove(ScopeEventListener.class, listener);
     }
 
     /**
-     * Finalizes all the listeners that are associated with the given cache object
-     */
+ * Finalizes all the listeners that are associated with the given cache object
+ */
     protected void finalizeListeners(Cache cache) {
         super.finalizeListeners(cache);
     }
 
     /**
-     * Convert a byte array into a Base64 string (as used in mime formats)
-     */
+ * Convert a byte array into a Base64 string (as used in mime formats)
+ */
     private static String toBase64(byte[] aValue) {
         int byte1;
         int byte2;
     }
 
     /**
-     * Create a cache
-     *
-     * @param scope The cache scope
-     * @param sessionId The sessionId for with the cache will be created
-     * @return A new cache
-     */
+ * Create a cache
+ *
+ * @param scope The cache scope
+ * @param sessionId The sessionId for with the cache will be created
+ * @return A new cache
+ */
     private ServletCache createCache(int scope, String sessionId) {
         if (log.isInfoEnabled()) {
             log.info("Created new cache in scope " + scope);
         // TODO - Fix me please!
         // Hack! This is nasty - if two sessions are created within a short
         // space of time it is possible they will end up with duplicate
-        // session IDs being passed to the DiskPersistenceListener!...
+        // session IDs being passed to the BasicDiskPersistenceListener!...
         config.set(HASH_KEY_SCOPE, "" + scope);
         config.set(HASH_KEY_SESSION_ID, sessionId);
 
     }
 
     /**
-     * Dispatch a scope event to all registered listeners.
-     *
-     * @param eventType   The type of event
-     * @param scope       Scope that was flushed (Does not apply for FLUSH_ALL event)
-     * @param date        Date of flushing
-     * @param origin      The origin of the event
-     */
+ * Dispatch a scope event to all registered listeners.
+ *
+ * @param eventType   The type of event
+ * @param scope       Scope that was flushed (Does not apply for FLUSH_ALL event)
+ * @param date        Date of flushing
+ * @param origin      The origin of the event
+ */
     private void dispatchScopeEvent(ScopeEventType eventType, int scope, Date date, String origin) {
         // Create the event
         ScopeEvent event = new ScopeEvent(eventType, scope, date, origin);
     }
 
     /**
-     *        Set property cache.use.host.domain.in.key=true to add domain information to key
-     *  generation for hosting multiple sites
-     */
+ *        Set property cache.use.host.domain.in.key=true to add domain information to key
+ *  generation for hosting multiple sites
+ */
     private void initHostDomainInKey() {
         String propStr = getProperty(CACHE_USE_HOST_DOMAIN_KEY);
 

src/core/test/com/opensymphony/oscache/base/algorithm/TestQueueCache.java

 
 import com.opensymphony.oscache.base.Config;
 import com.opensymphony.oscache.base.persistence.PersistenceListener;
-import com.opensymphony.oscache.plugins.diskpersistence.DiskPersistenceListener;
-import com.opensymphony.oscache.plugins.diskpersistence.TestDiskPersistenceListener;
+import com.opensymphony.oscache.plugins.diskpersistence.BasicDiskPersistenceListener;
+import com.opensymphony.oscache.plugins.diskpersistence.TestBasicDiskPersistenceListener;
 
 import java.util.Iterator;
 import java.util.Properties;
      */
     public void testPutOverflow() {
         // Create a listener
-        PersistenceListener listener = new DiskPersistenceListener();
+        PersistenceListener listener = new BasicDiskPersistenceListener();
 
         Properties p = new Properties();
-        p.setProperty("cache.path", TestDiskPersistenceListener.CACHEDIR);
+        p.setProperty("cache.path", TestBasicDiskPersistenceListener.CACHEDIR);
         p.setProperty("cache.memory", "true");
         p.setProperty("cache.persistence.overflow.only", "true");
-        p.setProperty("cache.persistence.class", "com.opensymphony.oscache.plugins.diskpersistence.DiskPersistenceListener");
+        p.setProperty("cache.persistence.class", "com.opensymphony.oscache.plugins.diskpersistence.BasicDiskPersistenceListener");
         listener.configure(new Config(p));
         getCache().setPersistenceListener(listener);
         getCache().clear();

src/plugins/diskpersistence/java/com/opensymphony/oscache/plugins/diskpersistence/AbstractDiskPersistenceListener.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.oscache.plugins.diskpersistence;
+
+import com.opensymphony.oscache.base.Config;
+import com.opensymphony.oscache.base.persistence.CachePersistenceException;
+import com.opensymphony.oscache.base.persistence.PersistenceListener;
+import com.opensymphony.oscache.web.ServletCacheAdministrator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.*;
+
+import java.util.Set;
+
+import javax.servlet.jsp.PageContext;
+
+/**
+ * Persist the cache data to disk.
+ *
+ * The code in this class is totally not thread safe it is the resonsibility
+ * of the cache using this persistence listener to handle the concurrency.
+ *
+ * @author <a href="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a>
+ * @author <a href="mailto:abergevin@pyxis-tech.com">Alain Bergevin</a>
+ * @author <a href="&#109;a&#105;&#108;&#116;&#111;:chris&#64;swebtec.&#99;&#111;&#109;">Chris Miller</a>
+ * @author <a href="mailto:amarch@soe.sony.com">Andres March</a>
+ */
+public abstract class AbstractDiskPersistenceListener implements PersistenceListener, Serializable {
+    protected final static String CACHE_PATH_KEY = "cache.path";
+
+    /**
+ * File extension for disk cache file
+ */
+    protected final static String CACHE_EXTENSION = "cache";
+
+    /**
+ * The directory that cache groups are stored under
+ */
+    protected final static String GROUP_DIRECTORY = "__groups__";
+
+    /**
+ * Sub path name for application cache
+ */
+    protected final static String APPLICATION_CACHE_SUBPATH = "application";
+
+    /**
+ * Sub path name for session cache
+ */
+    protected final static String SESSION_CACHE_SUBPATH = "session";
+
+    /**
+ * Property to get the temporary working directory of the servlet container.
+ */
+    protected static final String CONTEXT_TMPDIR = "javax.servlet.context.tempdir";
+    private static transient final Log log = LogFactory.getLog(AbstractDiskPersistenceListener.class);
+
+    /**
+ * Base path where the disk cache reside.
+ */
+    private File cachePath = null;
+    private File contextTmpDir;
+
+    /**
+ * Root path for disk cache
+ */
+    private String root = null;
+
+    /**
+ *        Get the physical cache path on disk.
+ *
+ *        @return        A file representing the physical cache location.
+ */
+    public File getCachePath() {
+        return cachePath;
+    }
+
+    /**
+   *        Get the root directory for persisting the cache on disk.
+   *        This path includes scope and sessionId, if any.
+   *
+   *        @return        A String representing the root directory.
+   */
+    public String getRoot() {
+        return root;
+    }
+
+    /**
+ *        Get the servlet context tmp directory.
+ *
+ *        @return        A file representing the servlet context tmp directory.
+ */
+    public File getContextTmpDir() {
+        return contextTmpDir;
+    }
+
+    /**
+ * Verify if a group exists in the cache
+ *
+ * @param group The group name to check
+ * @return True if it exists
+ * @throws CachePersistenceException
+ */
+    public boolean isGroupStored(String group) throws CachePersistenceException {
+        try {
+            File file = getCacheGroupFile(group);
+
+            return file.exists();
+        } catch (Exception e) {
+            throw new CachePersistenceException("Unable verify group '" + group + "' exists in the cache: " + e);
+        }
+    }
+
+    /**
+ * Verify if an object is currently stored in the cache
+ *
+ * @param key The object key
+ * @return True if it exists
+ * @throws CachePersistenceException
+ */
+    public boolean isStored(String key) throws CachePersistenceException {
+        try {
+            File file = getCacheFile(key);
+
+            return file.exists();
+        } catch (Exception e) {
+            throw new CachePersistenceException("Unable verify id '" + key + "' is stored in the cache: " + e);
+        }
+    }
+
+    /**
+ * Clears the whole cache directory, starting from the root
+ *
+ * @throws CachePersistenceException
+ */
+    public void clear() throws CachePersistenceException {
+        clear(root);
+    }
+
+    /**
+ * Initialises this <tt>BasicDiskPersistenceListener</tt> using the supplied
+ * configuration.
+ *
+ * @param config The OSCache configuration
+ */
+    public PersistenceListener configure(Config config) {
+        String sessionId = null;
+        int scope = 0;
+        initFileCaching(config.getProperty(CACHE_PATH_KEY));
+
+        if (config.getProperty(ServletCacheAdministrator.HASH_KEY_SESSION_ID) != null) {
+            sessionId = config.getProperty(ServletCacheAdministrator.HASH_KEY_SESSION_ID);
+        }
+
+        if (config.getProperty(ServletCacheAdministrator.HASH_KEY_SCOPE) != null) {
+            scope = Integer.parseInt(config.getProperty(ServletCacheAdministrator.HASH_KEY_SCOPE));
+        }
+
+        StringBuffer root = new StringBuffer(getCachePath().getPath());
+        root.append("/");
+        root.append(getPathPart(scope));
+
+        if ((sessionId != null) && (sessionId.length() > 0)) {
+            root.append("/");
+            root.append(sessionId);
+        }
+
+        this.root = root.toString();
+        this.contextTmpDir = (File) config.get(ServletCacheAdministrator.HASH_KEY_CONTEXT_TMPDIR);
+
+        return this;
+    }
+
+    /**
+ * Delete a single cache entry.
+ *
+ * @param key The object key to delete
+ * @throws CachePersistenceException
+ */
+    public void remove(String key) throws CachePersistenceException {
+        File file = getCacheFile(key);
+        remove(file);
+    }
+
+    /**
+ * Deletes an entire group from the cache.
+ *
+ * @param groupName The name of the group to delete
+ * @throws CachePersistenceException
+ */
+    public void removeGroup(String groupName) throws CachePersistenceException {
+        File file = getCacheGroupFile(groupName);
+        remove(file);
+    }
+
+    /**
+ * Retrieve an object from the disk
+ *
+ * @param key The object key
+ * @return The retrieved object
+ * @throws CachePersistenceException
+ */
+    public Object retrieve(String key) throws CachePersistenceException {
+        return retrieve(getCacheFile(key));
+    }
+
+    /**
+ * Retrieves a group from the cache, or <code>null</code> if the group
+ * file could not be found.
+ *
+ * @param groupName The name of the group to retrieve.
+ * @return A <code>Set</code> containing keys of all of the cache
+ * entries that belong to this group.
+ * @throws CachePersistenceException
+ */
+    public Set retrieveGroup(String groupName) throws CachePersistenceException {
+        File groupFile = getCacheGroupFile(groupName);
+
+        try {
+            return (Set) retrieve(groupFile);
+        } catch (ClassCastException e) {
+            throw new CachePersistenceException("Group file " + groupFile + " was not persisted as a Set: " + e);
+        }
+    }
+
+    /**
+ * Stores an object in cache
+ *
+ * @param key The object's key
+ * @param obj The object to store
+ * @throws CachePersistenceException
+ */
+    public void store(String key, Object obj) throws CachePersistenceException {
+        File file = getCacheFile(key);
+        store(file, obj);
+    }
+
+    /**
+ * Stores a group in the persistent cache. This will overwrite any existing
+ * group with the same name
+ */
+    public void storeGroup(String groupName, Set group) throws CachePersistenceException {
+        File groupFile = getCacheGroupFile(groupName);
+        store(groupFile, group);
+    }
+
+    /**
+ * Allows to translate to the temp dir of the servlet container if cachePathStr
+ * is javax.servlet.context.tempdir.
+ *
+ * @param cachePathStr  Cache path read from the properties file.
+ * @return Adjusted cache path
+ */
+    protected String adjustFileCachePath(String cachePathStr) {
+        if (cachePathStr.compareToIgnoreCase(CONTEXT_TMPDIR) == 0) {
+            cachePathStr = contextTmpDir.getAbsolutePath();
+        }
+
+        return cachePathStr;
+    }
+
+    /**
+ *        Set caching to file on or off.
+ *  If the <code>cache.path</code> property exists, we assume file caching is turned on.
+ *        By the same token, to turn off file caching just remove this property.
+ */
+    protected void initFileCaching(String cachePathStr) {
+        if (cachePathStr != null) {
+            cachePath = new File(cachePathStr);
+
+            try {
+                if (!cachePath.exists()) {
+                    if (log.isInfoEnabled()) {
+                        log.info("cache.path '" + cachePathStr + "' does not exist, creating");
+                    }
+
+                    cachePath.mkdirs();
+                }
+
+                if (!cachePath.isDirectory()) {
+                    log.error("cache.path '" + cachePathStr + "' is not a directory");
+                    cachePath = null;
+                } else if (!cachePath.canWrite()) {
+                    log.error("cache.path '" + cachePathStr + "' is not a writable location");
+                    cachePath = null;
+                }
+            } catch (Exception e) {
+                log.error("cache.path '" + cachePathStr + "' could not be used", e);
+                cachePath = null;
+            }
+        } else {
+            // Use default value
+        }
+    }
+
+    protected void remove(File file) throws CachePersistenceException {
+        try {
+            // Loop until we are able to delete (No current read).
+            // The cache must ensure that there are never two concurrent threads
+            // doing write (store and delete) operations on the same item.
+            // Delete only should be enough but file.exists prevents infinite loop
+            while (!file.delete() && file.exists()) {
+                ;
+            }
+        } catch (Exception e) {
+            throw new CachePersistenceException("Unable to remove '" + file + "' from the cache: " + e);
+        }
+    }
+
+    /**
+ * Stores an object using the supplied file object
+ *
+ * @param file The file to use for storing the object
+ * @param obj the object to store
+ * @throws CachePersistenceException
+ */
+    protected void store(File file, Object obj) throws CachePersistenceException {
+        // check if the directory structure required exists and create it if it doesn't
+        File filepath = new File(file.getParent());
+
+        try {
+            if (!filepath.exists()) {
+                filepath.mkdirs();
+            }
+        } catch (Exception e) {
+            throw new CachePersistenceException("Unable to create the directory " + filepath);
+        }
+
+        // Loop until we are able to delete (No current read).
+        // The cache must ensure that there are never two concurrent threads
+        // doing write (store and delete) operations on the same item.
+        // Delete only should be enough but file.exists prevents infinite loop
+        while (file.exists() && !file.delete()) {
+            ;
+        }
+
+        // Write the object to disk
+        FileOutputStream fout = null;
+        ObjectOutputStream oout = null;
+
+        try {
+            fout = new FileOutputStream(file);
+            oout = new ObjectOutputStream(fout);
+            oout.writeObject(obj);
+            oout.flush();
+        } catch (Exception e) {
+            throw new CachePersistenceException("Unable to write '" + file + "' in the cache. Exception: " + e.getClass().getName() + ", Message: " + e.getMessage());
+        } finally {
+            try {
+                fout.close();
+            } catch (Exception e) {
+            }
+
+            try {
+                oout.close();
+            } catch (Exception e) {
+            }
+        }
+    }
+
+    /**
+ * Build fully qualified cache file for the specified cache entry key.
+ *
+ * @param key   Cache Entry Key.
+ * @return File reference.
+ */
+    protected File getCacheFile(String key) {
+        char[] fileChars = getCacheFileName(key);
+
+        File file = new File(root, new String(fileChars) + "." + CACHE_EXTENSION);
+
+        return file;
+    }
+
+    /**
+ * Build cache file name for the specified cache entry key.
+ *
+ * @param key   Cache Entry Key.
+ * @return char[] file name.
+ */
+    protected abstract char[] getCacheFileName(String key);
+
+    /**
+ * Builds a fully qualified file name that specifies a cache group entry.
+ *
+ * @param group The name of the group
+ * @return A File reference
+ */
+    private File getCacheGroupFile(String group) {
+        int AVERAGE_PATH_LENGTH = 30;
+
+        if ((group == null) || (group.length() == 0)) {
+            throw new IllegalArgumentException("Invalid group '" + group + "' specified to getCacheGroupFile.");
+        }
+
+        StringBuffer path = new StringBuffer(AVERAGE_PATH_LENGTH);
+
+        // Build a fully qualified file name for this group
+        path.append(GROUP_DIRECTORY).append('/');
+        path.append(group).append('.').append(CACHE_EXTENSION);
+
+        return new File(root, path.toString());
+    }
+
+    /**
+ * This allows to persist different scopes in different path in the case of
+ * file caching.
+ *
+ * @param scope   Cache scope.
+ * @return The scope subpath
+ */
+    private String getPathPart(int scope) {
+        if (scope == PageContext.SESSION_SCOPE) {
+            return SESSION_CACHE_SUBPATH;
+        } else {
+            return APPLICATION_CACHE_SUBPATH;
+        }
+    }
+
+    /**
+ * Clears a whole directory, starting from the specified
+ * directory
+ *
+ * @param baseDirName The root directory to delete
+ * @throws CachePersistenceException
+ */
+    private void clear(String baseDirName) throws CachePersistenceException {
+        File baseDir = new File(baseDirName);
+        File[] fileList = baseDir.listFiles();
+
+        try {
+            if (fileList != null) {
+                // Loop through all the files and directory to delete them
+                for (int count = 0; count < fileList.length; count++) {
+                    if (fileList[count].isFile()) {
+                        fileList[count].delete();
+                    } else {
+                        // Make a recursive call to delete the directory
+                        clear(fileList[count].toString());
+                        fileList[count].delete();
+                    }
+                }
+            }
+
+            // Delete the root directory
+            baseDir.delete();
+        } catch (Exception e) {
+            throw new CachePersistenceException("Unable to clear the cache directory");
+        }
+    }
+
+    /**
+ * Retrives a serialized object from the supplied file, or returns
+ * <code>null</code> if the file does not exist.
+ *
+ * @param file The file to deserialize
+ * @return The deserialized object
+ * @throws CachePersistenceException
+ */
+    private Object retrieve(File file) throws CachePersistenceException {
+        Object readContent = null;
+        boolean fileExist;
+
+        try {
+            fileExist = file.exists();
+        } catch (Exception e) {
+            throw new CachePersistenceException("Unable to verify if " + file + " exists: " + e);
+        }
+
+        // Read the file if it exists
+        if (fileExist) {
+            BufferedInputStream in = null;
+            ObjectInputStream oin = null;
+
+            try {
+                in = new BufferedInputStream(new FileInputStream(file));
+                oin = new ObjectInputStream(in);
+                readContent = oin.readObject();
+            } catch (Exception e) {
+                // We expect this exception to occur.
+                // This is when the item will be invalidated (written or deleted)
+                // during read.
+                // The cache has the logic to retry reading.
+                throw new CachePersistenceException("Unable to read '" + file.getAbsolutePath() + "' from the cache: " + e);
+            } finally {
+                try {
+                    oin.close();
+                } catch (Exception ex) {
+                }
+
+                try {
+                    in.close();
+                } catch (Exception ex) {
+                }
+            }
+        }
+
+        return readContent;
+    }
+}

src/plugins/diskpersistence/java/com/opensymphony/oscache/plugins/diskpersistence/BasicDiskPersistenceListener.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.oscache.plugins.diskpersistence;
+
+import com.opensymphony.oscache.base.Config;
+import com.opensymphony.oscache.base.persistence.CachePersistenceException;
+import com.opensymphony.oscache.base.persistence.PersistenceListener;
+import com.opensymphony.oscache.web.ServletCacheAdministrator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.*;
+
+import java.util.Set;
+
+import javax.servlet.jsp.PageContext;
+
+/**
+ * Persist the cache data to disk.
+ *
+ * The code in this class is totally not thread safe it is the resonsibility
+ * of the cache using this persistence listener to handle the concurrency.
+ *
+ * @version        $Revision$
+ * @author <a href="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a>
+ * @author <a href="mailto:abergevin@pyxis-tech.com">Alain Bergevin</a>
+ * @author <a href="&#109;a&#105;&#108;&#116;&#111;:chris&#64;swebtec.&#99;&#111;&#109;">Chris Miller</a>
+ */
+public class BasicDiskPersistenceListener extends AbstractDiskPersistenceListener {
+    /**
+ * Build cache file name for the specified cache entry key.
+ *
+ * @param key   Cache Entry Key.
+ * @return char[] file name.
+ */
+    protected char[] getCacheFileName(String key) {
+        if ((key == null) || (key.length() == 0)) {
+            throw new IllegalArgumentException("Invalid key '" + key + "' specified to getCacheFile.");
+        }
+
+        char[] chars = key.toCharArray();
+        char[] fileChars = new char[chars.length];
+
+        for (int i = 0; i < chars.length; i++) {
+            char c = chars[i];
+
+            switch (c) {
+                case '.':
+                case '/':
+                case '\\':
+                case ' ':
+                case ':':
+                case ';':
+                case '"':
+                case '\'':
+                    fileChars[i] = '_';
+                    break;
+                default:
+                    fileChars[i] = c;
+            }
+        }
+
+        return fileChars;
+    }
+}

src/plugins/diskpersistence/java/com/opensymphony/oscache/plugins/diskpersistence/DiskPersistenceListener.java

-/*
- * Copyright (c) 2002-2003 by OpenSymphony
- * All rights reserved.
- */
-package com.opensymphony.oscache.plugins.diskpersistence;
-
-import com.opensymphony.oscache.base.Config;
-import com.opensymphony.oscache.base.persistence.CachePersistenceException;
-import com.opensymphony.oscache.base.persistence.PersistenceListener;
-import com.opensymphony.oscache.web.ServletCacheAdministrator;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import java.io.*;
-
-import java.util.Set;
-
-import javax.servlet.jsp.PageContext;
-
-/**
- * Persist the cache data to disk.
- *
- * The code in this class is totally not thread safe it is the resonsibility
- * of the cache using this persistence listener to handle the concurrency.
- *
- * @version        $Revision$
- * @author <a href="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a>
- * @author <a href="mailto:abergevin@pyxis-tech.com">Alain Bergevin</a>
- * @author <a href="&#109;a&#105;&#108;&#116;&#111;:chris&#64;swebtec.&#99;&#111;&#109;">Chris Miller</a>
- */
-public class DiskPersistenceListener implements PersistenceListener, Serializable {
-    protected final static String CACHE_PATH_KEY = "cache.path";
-
-    /**
-     * File extension for disk cache file
-     */
-    protected final static String CACHE_EXTENSION = "cache";
-
-    /**
-     * The directory that cache groups are stored under
-     */
-    protected final static String GROUP_DIRECTORY = "__groups__";
-
-    /**
-     * Sub path name for application cache
-     */
-    protected final static String APPLICATION_CACHE_SUBPATH = "application";
-
-    /**
-     * Sub path name for session cache
-     */
-    protected final static String SESSION_CACHE_SUBPATH = "session";
-
-    /**
-     * Property to get the temporary working directory of the servlet container.
-     */
-    protected static final String CONTEXT_TMPDIR = "javax.servlet.context.tempdir";
-    private static transient final Log log = LogFactory.getLog(DiskPersistenceListener.class);
-
-    /**
-     * Base path where the disk cache reside.
-     */
-    private File cachePath = null;
-    private File contextTmpDir;
-
-    /**
-     * Root path for disk cache
-     */
-    private String root = null;
-
-    /**
-     *        Get the physical cache path on disk.
-     *
-     *        @return        A file representing the physical cache location.
-     */
-    public File getCachePath() {
-        return cachePath;
-    }
-
-    /**
-       *        Get the root directory for persisting the cache on disk.
-       *        This path includes scope and sessionId, if any.
-       *
-       *        @return        A String representing the root directory.
-       */
-    public String getRoot() {
-        return root;
-    }
-
-    /**
-     *        Get the servlet context tmp directory.
-     *
-     *        @return        A file representing the servlet context tmp directory.
-     */
-    public File getContextTmpDir() {
-        return contextTmpDir;
-    }
-
-    /**
-     * Verify if a group exists in the cache
-     *
-     * @param group The group name to check
-     * @return True if it exists
-     * @throws CachePersistenceException
-     */
-    public boolean isGroupStored(String group) throws CachePersistenceException {
-        try {
-            File file = getCacheGroupFile(group);
-
-            return file.exists();
-        } catch (Exception e) {
-            throw new CachePersistenceException("Unable verify group '" + group + "' exists in the cache: " + e);
-        }
-    }
-
-    /**
-     * Verify if an object is currently stored in the cache
-     *
-     * @param key The object key
-     * @return True if it exists
-     * @throws CachePersistenceException
-     */
-    public boolean isStored(String key) throws CachePersistenceException {
-        try {
-            File file = getCacheFile(key);
-
-            return file.exists();
-        } catch (Exception e) {
-            throw new CachePersistenceException("Unable verify id '" + key + "' is stored in the cache: " + e);
-        }
-    }
-
-    /**
-     * Clears the whole cache directory, starting from the root
-     *
-     * @throws CachePersistenceException
-     */
-    public void clear() throws CachePersistenceException {
-        clear(root);
-    }
-
-    /**
-     * Initialises this <tt>DiskPersistenceListener</tt> using the supplied
-     * configuration.
-     *
-     * @param config The OSCache configuration
-     */
-    public PersistenceListener configure(Config config) {
-        String sessionId = null;
-        int scope = 0;
-        initFileCaching(config.getProperty(CACHE_PATH_KEY));
-
-        if (config.getProperty(ServletCacheAdministrator.HASH_KEY_SESSION_ID) != null) {
-            sessionId = config.getProperty(ServletCacheAdministrator.HASH_KEY_SESSION_ID);
-        }
-
-        if (config.getProperty(ServletCacheAdministrator.HASH_KEY_SCOPE) != null) {
-            scope = Integer.parseInt(config.getProperty(ServletCacheAdministrator.HASH_KEY_SCOPE));
-        }
-
-        StringBuffer root = new StringBuffer(getCachePath().getPath());
-        root.append("/");
-        root.append(getPathPart(scope));
-
-        if ((sessionId != null) && (sessionId.length() > 0)) {
-            root.append("/");
-            root.append(sessionId);
-        }
-
-        this.root = root.toString();
-        this.contextTmpDir = (File) config.get(ServletCacheAdministrator.HASH_KEY_CONTEXT_TMPDIR);
-
-        return this;
-    }
-
-    /**
-     * Delete a single cache entry.
-     *
-     * @param key The object key to delete
-     * @throws CachePersistenceException
-     */
-    public void remove(String key) throws CachePersistenceException {
-        File file = getCacheFile(key);
-        remove(file);
-    }
-
-    /**
-     * Deletes an entire group from the cache.
-     *
-     * @param groupName The name of the group to delete
-     * @throws CachePersistenceException
-     */
-    public void removeGroup(String groupName) throws CachePersistenceException {
-        File file = getCacheGroupFile(groupName);
-        remove(file);
-    }
-
-    /**
-     * Retrieve an object from the disk
-     *
-     * @param key The object key
-     * @return The retrieved object
-     * @throws CachePersistenceException
-     */
-    public Object retrieve(String key) throws CachePersistenceException {
-        return retrieve(getCacheFile(key));
-    }
-
-    /**
-     * Retrieves a group from the cache, or <code>null</code> if the group
-     * file could not be found.
-     *
-     * @param groupName The name of the group to retrieve.
-     * @return A <code>Set</code> containing keys of all of the cache
-     * entries that belong to this group.
-     * @throws CachePersistenceException
-     */
-    public Set retrieveGroup(String groupName) throws CachePersistenceException {
-        File groupFile = getCacheGroupFile(groupName);
-
-        try {
-            return (Set) retrieve(groupFile);
-        } catch (ClassCastException e) {
-            throw new CachePersistenceException("Group file " + groupFile + " was not persisted as a Set: " + e);
-        }
-    }
-
-    /**
-     * Stores an object in cache
-     *
-     * @param key The object's key
-     * @param obj The object to store
-     * @throws CachePersistenceException
-     */
-    public void store(String key, Object obj) throws CachePersistenceException {
-        File file = getCacheFile(key);
-        store(file, obj);
-    }
-
-    /**
-     * Stores a group in the persistent cache. This will overwrite any existing
-     * group with the same name
-     */
-    public void storeGroup(String groupName, Set group) throws CachePersistenceException {
-        File groupFile = getCacheGroupFile(groupName);
-        store(groupFile, group);
-    }
-
-    /**
-     * Allows to translate to the temp dir of the servlet container if cachePathStr
-     * is javax.servlet.context.tempdir.
-     *
-     * @param cachePathStr  Cache path read from the properties file.
-     * @return Adjusted cache path
-     */
-    protected String adjustFileCachePath(String cachePathStr) {
-        if (cachePathStr.compareToIgnoreCase(CONTEXT_TMPDIR) == 0) {
-            cachePathStr = contextTmpDir.getAbsolutePath();
-        }
-
-        return cachePathStr;
-    }
-
-    /**
-     *        Set caching to file on or off.
-     *  If the <code>cache.path</code> property exists, we assume file caching is turned on.