Commits

Anonymous committed d5dbd10

- Nuked the WeakHashMap from AbstractCacheAdministrator. This meant getting rid of the named cache concept, and instead passing the Cache object through to LifecycleAware listeners (rather than the administrator class itself). Consequences are that the session caches can't/won't cluster - not that they did anyway, but the reason is now different!
- Also spotted and fixed a bug with the flushAll() cluster notifications - the date wasn't getting broadcast.

  • Participants
  • Parent commits 34ccf02

Comments (0)

Files changed (13)

src/core/java/com/opensymphony/oscache/base/AbstractCacheAdministrator.java

     protected int cacheCapacity = -1;
 
     /**
-     * Holds named cache objects that are managed by this class. Caches that
-     * are held in this registry are available for lookup by external code.
-     * This is useful when caches running in different JVMs or even different
-     * machines need to be coordinated. An event handler can pass through the name
-     * of the cache to the remote JVM, which can then retrieve the corresponding
-     * local cache with the same name.<p>
-     * For example the <code>BroadcastingCacheEventListener</code> uses this to
-     * flush cache entries across a cluster.
-     */
-    private Map namedCaches = new WeakHashMap();
-
-    /**
      * Whether the cache blocks waiting for content to be build, or serves stale
      * content instead. This value can be specified using the {@link #CACHE_BLOCKING_KEY}
      * configuration property.
         return unlimitedDiskCache;
     }
 
-    public Cache getNamedCache(String name) {
-        return (Cache) namedCaches.get(name);
-    }
-
-    protected void nameCache(String name, Cache cache) {
-        if (cache.getName() != null) {
-            log.error("nameCache() can only be called once.");
-            throw new UnsupportedOperationException("Cannot call nameCache() multiple times with the same cache object.");
-        }
-
-        cache.setName(name);
-
-        // Take a copy of the string since we're storing it in a WeakHashMap
-        namedCaches.put(new String(name), cache);
-    }
-
     /**
      * Retrieves an array containing instances all of the {@link CacheEventListener}
      * classes that are specified in the OSCache configuration file.
                 // Pass through the configuration to those listeners that require it
                 if (listeners[i] instanceof LifecycleAware) {
                     try {
-                        ((LifecycleAware) listeners[i]).initialize(this, config);
+                        ((LifecycleAware) listeners[i]).initialize(cache, config);
                     } catch (InitializationException e) {
                         log.error("Could not initialize listener '" + listeners[i].getClass().getName() + "'. Listener ignored.", e);
 

src/core/java/com/opensymphony/oscache/base/Cache.java

     private Map updateStates = new HashMap();
 
     /**
-     * An optional name for the cache. When a cache is named it gets remembered
-     * by the <code>AbstractCacheAdministrator</code>. It can then be looked up
-     * by listeners or other code that need to get hold of the named cache instance.
-     */
-    private String name = null;
-
-    /**
      * Indicates whether the cache blocks requests until new content has
      * been generated or just serves stale content instead.
      */
     }
 
     /**
-     * Sets the name of the cache. This is optional, however only named
-     * caches are able to receive asynchronous flush notifications.
-     *
-     * @param name The name of the cache
-     */
-    void setName(String name) {
-        this.name = name;
-    }
-
-    /**
-     * Retrieves the name of the cache. The name is optional, so this
-     * could return <code>null</code>.
-     */
-    public String getName() {
-        return name;
-    }
-
-    /**
      * Set the listener to use for data persistence. Only one
      * <code>PersistenceListener</code> can be configured per cache.
      *
      */
     public void flushAll(Date date, String origin) {
         flushDateTime = date;
-        dispatchCachewideEvent(CachewideEventType.CACHE_FLUSHED, origin);
+        dispatchCachewideEvent(CachewideEventType.CACHE_FLUSHED, date, origin);
     }
 
     /**
     }
 
     /**
-     * Dispatches a cache-wise event to all registered listeners.
+     * Dispatches a cache-wide event to all registered listeners.
      *
      * @param eventType The type of event (this is used to branch to the correct method handler)
-     * @param origin      The origin of this event (optional)
+     * @param origin The origin of this event (optional)
      */
-    private void dispatchCachewideEvent(CachewideEventType eventType, String origin) {
-        CachewideEvent event = new CachewideEvent(this, origin);
+    private void dispatchCachewideEvent(CachewideEventType eventType, Date date, String origin) {
+        CachewideEvent event = new CachewideEvent(this, date, origin);
 
         // Guaranteed to return a non-null array
         Object[] listeners = listenerList.getListenerList();

src/core/java/com/opensymphony/oscache/base/LifecycleAware.java

  */
 public interface LifecycleAware {
     /**
-     * Called by the cache administrator class when a cache is instantiated.
-     *
-     * @param admin The administrator of the cache that this listener is attached to.
-     * @param config The cache's configuration details. This allows the event handler
-     * to initialize itself based on the cache settings, and also to receive <em>additional</em>
-     * settings that were part of the cache configuration but that the cache
-     * itself does not care about. If you are using <code>cache.properties</code>
-     * for your configuration, simply add any additional properties that your event
-     * handler requires and they will be passed through in this parameter.
-     *
-     * @throws InitializationException thrown when there was a problem initializing the
-     * listener. The cache administrator will log this error and disable the listener.
-     */
-    public void initialize(AbstractCacheAdministrator admin, Config config) throws InitializationException;
+    * Called by the cache administrator class when a cache is instantiated.
+    *
+    * @param cache the cache instance that this listener is attached to.
+    * @param config The cache's configuration details. This allows the event handler
+    * to initialize itself based on the cache settings, and also to receive <em>additional</em>
+    * settings that were part of the cache configuration but that the cache
+    * itself does not care about. If you are using <code>cache.properties</code>
+    * for your configuration, simply add any additional properties that your event
+    * handler requires and they will be passed through in this parameter.
+    *
+    * @throws InitializationException thrown when there was a problem initializing the
+    * listener. The cache administrator will log this error and disable the listener.
+    */
+    public void initialize(Cache cache, Config config) throws InitializationException;
 
     /**
-     * Called by the cache administrator class when a cache is destroyed.
-     *
-     * @throws FinalizationException thrown when there was a problem finalizing the
-     * listener. The cache administrator will catch and log this error.
-     */
+    * Called by the cache administrator class when a cache is destroyed.
+    *
+    * @throws FinalizationException thrown when there was a problem finalizing the
+    * listener. The cache administrator will catch and log this error.
+    */
     public void finialize() throws FinalizationException;
 }

src/core/java/com/opensymphony/oscache/base/events/CachewideEvent.java

 
 import com.opensymphony.oscache.base.Cache;
 
+import java.util.Date;
+
 /**
  * A <code>CachewideEvent<code> represents and event that occurs on
  * the the entire cache, eg a cache flush or clear.
     /**
      * The cache where the event occurred.
      */
-    private Cache map = null;
+    private Cache cache = null;
 
     /**
-     * Constructs a cachewide event with no origin.
-     *
-     * @param map     The cache map that the event occurred on.
+     * The date/time for when the flush is scheduled
      */
-    public CachewideEvent(Cache map) {
-        this(map, null);
-    }
+    private Date date = null;
 
     /**
      * Constructs a cachewide event with the specified origin.
      *
-     * @param map     The cache map that the event occurred on.
+     * @param cache   The cache map that the event occurred on.
+     * @param date    The date/time that this cachewide event is scheduled for
+     * (eg, the date that the cache is to be flushed).
      * @param origin  An optional tag that can be attached to the event to
      * specify the event's origin. This is useful to prevent events from being
      * fired recursively in some situations, such as when an event handler
      * causes another event to be fired.
      */
-    public CachewideEvent(Cache map, String origin) {
+    public CachewideEvent(Cache cache, Date date, String origin) {
         super(origin);
-        this.map = map;
+        this.date = date;
+        this.cache = cache;
     }
 
     /**
      * Retrieve the cache map that the event occurred on.
      */
-    public Cache getMap() {
-        return map;
+    public Cache getCache() {
+        return cache;
+    }
+
+    /**
+     * Retrieve the date/time that the cache flush is scheduled for.
+     */
+    public Date getDate() {
+        return date;
     }
 }

src/core/java/com/opensymphony/oscache/general/GeneralCacheAdministrator.java

      * Allows to flush all items that have a specified pattern in the key.
      *
      * @param pattern     Pattern.
+     * @deprecated For performance and flexibility reasons it is preferable to
+     * store cache entries in groups and use the {@link #flushGroup(String)} method
+     * instead of relying on pattern flushing.
      */
     public void flushPattern(String pattern) {
         getCache().flushPattern(pattern);
 
         Cache newCache = new Cache(isMemoryCaching(), isUnlimitedDiskCache(), isBlocking(), algorithmClass, cacheCapacity);
 
-        // Give the cache a name
-        nameCache("applicationCache", newCache);
-
         configureStandardListeners(newCache);
 
         return newCache;

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

 
 import com.opensymphony.oscache.base.Cache;
 import com.opensymphony.oscache.base.CacheEntry;
-import com.opensymphony.oscache.base.NeedsRefreshException;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import javax.servlet.http.HttpSessionBindingEvent;
 import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.jsp.PageContext;
 
 /**
  * A simple extension of Cache that implements a session binding listener,
      */
     public ServletCache(ServletCacheAdministrator admin, int scope) {
         super(admin.isMemoryCaching(), admin.isUnlimitedDiskCache());
-        this.scope = scope;
+        setScope(scope);
         this.admin = admin;
     }
 
      */
     public ServletCache(ServletCacheAdministrator admin, String algorithmClass, int limit, int scope) {
         super(admin.isMemoryCaching(), admin.isUnlimitedDiskCache(), admin.isBlocking(), algorithmClass, limit);
+        setScope(scope);
         this.admin = admin;
-        this.scope = scope;
     }
 
     /**
         return scope;
     }
 
+    private void setScope(int scope) {
+        this.scope = scope;
+    }
+
     /**
      * When this Cache is bound to the session, do nothing.
      *

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

 
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
 import javax.servlet.jsp.PageContext;
 
 /**
  * This is a "servlet Singleton". This means it's not a Singleton in the traditional sense,
  * that is stored in a static instance. It's a Singleton _per web app context_.
  * <p>
- * Once created it manages the cache path on disk through the cachetags.properties
+ * Once created it manages the cache path on disk through the oscache.properties
  * file, and also keeps track of the flush times.
  *
  * @author <a href="mailto:mike@atlassian.com">Mike Cannon-Brookes</a>
     }
 
     /**
-     * Grabs a cache for the specified scope from the context
+     * Grabs the cache for the specified scope
      *
      * @param request The current request
-     * @param scope The scope of this cache
+     * @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) {
-        Cache thisCache = null;
-
-        Object o = null;
-
         if (scope == PageContext.APPLICATION_SCOPE) {
-            o = request.getSession(true).getServletContext().getAttribute(getCacheKey());
-        } else if (scope == PageContext.SESSION_SCOPE) {
-            o = request.getSession().getAttribute(getCacheKey());
-        } else {
-            throw new RuntimeException("Invalid scope");
+            return getAppScopeCache(request.getSession(true).getServletContext());
         }
 
-        // get or create the cache at cacheLocation in the appropriate scope
-        // if the cache does not exist, create it
-        if ((o == null) || !(o instanceof Cache)) {
-            if (log.isInfoEnabled()) {
-                log.info("Created new Cache at key: " + getCacheKey());
-            }
-
-            if (scope == PageContext.APPLICATION_SCOPE) {
-                thisCache = createCache(scope, null);
-                request.getSession(true).getServletContext().setAttribute(getCacheKey(), thisCache);
-            } else if (scope == PageContext.SESSION_SCOPE) {
-                thisCache = createCache(scope, request.getSession(false).getId());
-                request.getSession().setAttribute(getCacheKey(), thisCache);
-            }
-        } else {
-            thisCache = (Cache) o;
+        if (scope == PageContext.SESSION_SCOPE) {
+            return getSessionScopeCache(request.getSession(true));
         }
 
-        return thisCache;
+        throw new RuntimeException("The supplied scope value of " + scope + " is invalid. Acceptable values are PageContext.APPLICATION_SCOPE and PageContext.SESSION_SCOPE");
     }
 
     /**
-     *        Get the cache key from the properties. Set it to a default value if it
-     *  is not present in the properties
+     * 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.
+     */
+    public Cache getAppScopeCache(ServletContext context) {
+        Cache cache = null;
+        Object obj = context.getAttribute(getCacheKey());
+
+        if ((obj == null) || !(obj instanceof Cache)) {
+            if (log.isInfoEnabled()) {
+                log.info("Created new application-scoped cache at key: " + getCacheKey());
+            }
+
+            cache = createCache(PageContext.APPLICATION_SCOPE, null);
+            context.setAttribute(getCacheKey(), cache);
+        } else {
+            cache = (Cache) obj;
+        }
+
+        return cache;
+    }
+
+    /**
+     * A convenience method to retrieve the session scope cache
      *
-     *        @return The cache.key property or the DEFAULT_CACHE_KEY
+     * @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());
+
+        if ((obj == null) || !(obj instanceof Cache)) {
+            if (log.isInfoEnabled()) {
+                log.info("Created new session-scoped cache in session " + session.getId() + " at key: " + getCacheKey());
+            }
+
+            cache = createCache(PageContext.SESSION_SCOPE, session.getId());
+            session.setAttribute(getCacheKey(), cache);
+        } else {
+            cache = (Cache) obj;
+        }
+
+        return cache;
+    }
+
+    /**
+     * 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) {
     }
 
     /**
-     *        Set the flush time for a specific scope to a specific time
+     * 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
+     * @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()) {
             }
         }
 
-        // If the cache is in a long-lived scope, register it so it is able
-        // to receive broadcast events.
-        if (scope == PageContext.APPLICATION_SCOPE) {
-            nameCache("appCache", newCache);
-        } else if (scope == PageContext.SESSION_SCOPE) {
-            nameCache("sessionCache", newCache);
-        }
-
         return newCache;
     }
 

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

     // filter variables
     private FilterConfig config;
     private ServletCacheAdministrator admin = null;
-    private int cacheScope = PageContext.APPLICATION_SCOPE; // filter score - default is APPLICATION
+    private int cacheScope = PageContext.APPLICATION_SCOPE; // filter scope - default is APPLICATION
     private int time = 60 * 60; // time before cache should be refreshed - default one hour (in seconds)
 
     /**

src/core/test/com/opensymphony/oscache/base/TestConcurrency.java

         String key = "stale";
         admin.putInCache(key, VALUE);
 
-        // Sleep for a couple of seconds
         try {
-            Thread.sleep(1100);
-        } catch (InterruptedException ie) {
-        }
-
-        try {
-            // This should throw a NeedsRefreshException since the entry is
-            // more than 1 second old
-            admin.getFromCache(key, 1);
+            // This should throw a NeedsRefreshException since the refresh
+            // period is 0
+            admin.getFromCache(key, 0);
             fail("NeedsRefreshException should have been thrown");
         } catch (NeedsRefreshException nre) {
             // Fire off another thread to get the same cache entry.
             // Since blocking mode is currently disabled we should
             // immediately get back the stale entry
-            GetEntry getEntry = new GetEntry(key, VALUE, 1, false);
+            GetEntry getEntry = new GetEntry(key, VALUE, 0, false);
             Thread thread = new Thread(getEntry);
             thread.start();
 
             // Sleep for a bit to simulate the time taken to build the cache entry
             try {
-                Thread.sleep(500);
+                Thread.sleep(1000);
             } catch (InterruptedException ie) {
             }
 

src/plugins/clustersupport/java/com/opensymphony/oscache/plugins/clustersupport/BroadcastingCacheEventListener.java

      * the flush message to any listening nodes on the network.
      */
     public void cacheEntryFlushed(CacheEntryEvent event) {
-        Cache cache = event.getMap();
-
-        if ((cache.getName() != null) && !Cache.NESTED_EVENT.equals(event.getOrigin()) && !ClusterManager.CLUSTER_ORIGIN.equals(event.getOrigin())) {
+        if (!Cache.NESTED_EVENT.equals(event.getOrigin()) && !ClusterManager.CLUSTER_ORIGIN.equals(event.getOrigin())) {
             if (log.isDebugEnabled()) {
                 log.debug("cacheEntryFlushed called (" + event + ")");
             }
 
-            cm.signalEntryFlush(event.getKey(), cache.getName());
+            cm.signalEntryFlush(event.getKey());
         }
     }
 
     /**
      * Event fired when an entry is removed from the cache. This broadcasts
      * the remove method to any listening nodes on the network, as long as
-     * this event wasn't from a broadcast in the first place. The
+     * this event wasn't from a broadcast in the first place.
      */
     public void cacheGroupFlushed(CacheGroupEvent event) {
-        Cache cache = event.getMap();
-
-        if ((cache.getName() != null) && !Cache.NESTED_EVENT.equals(event.getOrigin()) && !ClusterManager.CLUSTER_ORIGIN.equals(event.getOrigin())) {
+        if (!Cache.NESTED_EVENT.equals(event.getOrigin()) && !ClusterManager.CLUSTER_ORIGIN.equals(event.getOrigin())) {
             if (log.isDebugEnabled()) {
                 log.debug("cacheGroupFushed called (" + event + ")");
             }
 
-            cm.signalGroupFlush(event.getGroup(), cache.getName());
+            cm.signalGroupFlush(event.getGroup());
         }
     }
 
     }
 
     public void cachePatternFlushed(CachePatternEvent event) {
-        Cache cache = event.getMap();
-
-        if ((cache.getName() != null) && !Cache.NESTED_EVENT.equals(event.getOrigin()) && !ClusterManager.CLUSTER_ORIGIN.equals(event.getOrigin())) {
+        if (!Cache.NESTED_EVENT.equals(event.getOrigin()) && !ClusterManager.CLUSTER_ORIGIN.equals(event.getOrigin())) {
             if (log.isDebugEnabled()) {
                 log.debug("cachePatternFushed called (" + event + ")");
             }
 
-            cm.signalPatternFlush(event.getPattern(), cache.getName());
+            cm.signalPatternFlush(event.getPattern());
         }
     }
 
     public void cacheFlushed(CachewideEvent event) {
-        Cache cache = event.getMap();
-
-        if ((cache.getName() != null) && !Cache.NESTED_EVENT.equals(event.getOrigin()) && !ClusterManager.CLUSTER_ORIGIN.equals(event.getOrigin())) {
+        if (!Cache.NESTED_EVENT.equals(event.getOrigin()) && !ClusterManager.CLUSTER_ORIGIN.equals(event.getOrigin())) {
             if (log.isDebugEnabled()) {
                 log.debug("cacheFushed called (" + event + ")");
             }
 
-            cm.signalCacheFlush(cache.getName());
+            cm.signalCacheFlush(event.getDate());
         }
     }
 
      * @param config An OSCache configuration object.
      * @throws InitializationException If this listener has already been initialized.
      */
-    public synchronized void initialize(AbstractCacheAdministrator admin, Config config) throws InitializationException {
+    public synchronized void initialize(Cache cache, Config config) throws InitializationException {
         if (referenceCount == 0) {
             cm = new ClusterManager(config);
-            cm.setAdministrator(admin);
+            cm.setCache(cache);
         }
 
         referenceCount++;

src/plugins/clustersupport/java/com/opensymphony/oscache/plugins/clustersupport/ClusterManager.java

 
 import java.io.Serializable;
 
+import java.util.Date;
+
 /**
  * Handles the sending of notification messages across the cluster. This
  * implementation is based on the JavaGroups library.
     private static final String DEFAULT_CHANNEL_PROPERTIES_POST = ";mcast_port=45566;ip_ttl=32;mcast_send_buf_size=150000;mcast_recv_buf_size=80000):PING(timeout=2000;num_initial_members=3):MERGE2(min_interval=5000;max_interval=10000):FD_SOCK:VERIFY_SUSPECT(timeout=1500):pbcast.STABLE(desired_avg_gossip=20000):pbcast.NAKACK(gc_lag=50;retransmit_timeout=300,600,1200,2400,4800):UNICAST(timeout=5000):FRAG(frag_size=8096;down_thread=false;up_thread=false):pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;shun=false;print_local_addr=true)";
     private static final String DEFAULT_MULTICAST_IP = "231.12.21.132";
     private final Log log = LogFactory.getLog(ClusterManager.class);
-    private AbstractCacheAdministrator admin;
+    private Cache cache;
     private NotificationBus bus;
     private boolean shuttingDown = false;
 
 
         ClusterNotification msg = (ClusterNotification) serializable;
 
-        if (admin == null) {
-            log.warn("Since no cache administrator has been specified for this ClusterManager, the cache named '" + msg.getCacheName() + "' cannot be retrieved. Cluster notification ignored.");
-            return;
-        }
-
-        // Retrieve the named cache that this message applies to
-        Cache cache = admin.getNamedCache(msg.getCacheName());
-
         if (cache == null) {
             log.warn("A cluster notification (" + msg + ") was received, but no matching cache is registered on this machine. Notification ignored.");
 
 
         switch (msg.getType()) {
             case ClusterNotification.FLUSH_KEY:
-                cache.flushEntry(msg.getData(), CLUSTER_ORIGIN);
+                cache.flushEntry((String) msg.getData(), CLUSTER_ORIGIN);
                 break;
             case ClusterNotification.FLUSH_GROUP:
-                cache.flushGroup(msg.getData(), CLUSTER_ORIGIN);
+                cache.flushGroup((String) msg.getData(), CLUSTER_ORIGIN);
                 break;
             case ClusterNotification.FLUSH_PATTERN:
-                cache.flushPattern(msg.getData(), CLUSTER_ORIGIN);
+                cache.flushPattern((String) msg.getData(), CLUSTER_ORIGIN);
                 break;
+            case ClusterNotification.FLUSH_CACHE:
+                cache.flushAll((Date) msg.getData(), CLUSTER_ORIGIN);
             default:
                 log.error("The cluster notification (" + msg + ") is of an unknown type. Notification ignored.");
         }
      * place.
      *
      * @param key The object key to broadcast a flush notification message for
-     * @param cacheName The name of the cache to flush
      */
-    void signalEntryFlush(String key, String cacheName) {
+    void signalEntryFlush(String key) {
         if (log.isDebugEnabled()) {
-            log.debug("flushEntry called for cache '" + cacheName + "', key '" + key + "'");
+            log.debug("flushEntry called for cache key '" + key + "'");
         }
 
         if (!shuttingDown) {
-            bus.sendNotification(new ClusterNotification(ClusterNotification.FLUSH_KEY, cacheName, key));
+            bus.sendNotification(new ClusterNotification(ClusterNotification.FLUSH_KEY, key));
         }
     }
 
      * Broadcasts a flush message for the given cache group.
      *
      * @param group The group to broadcast a flush message for
-     * @param cacheName The name of the cache to flush
      */
-    void signalGroupFlush(String group, String cacheName) {
+    void signalGroupFlush(String group) {
         if (log.isDebugEnabled()) {
-            log.debug("flushGroup called for cache '" + cacheName + "', group '" + group + "'");
+            log.debug("flushGroup called for cache group '" + group + "'");
         }
 
         if (!shuttingDown) {
-            bus.sendNotification(new ClusterNotification(ClusterNotification.FLUSH_GROUP, cacheName, group));
+            bus.sendNotification(new ClusterNotification(ClusterNotification.FLUSH_GROUP, group));
         }
     }
 
      * Broadcasts a flush message for the given cache pattern.
      *
      * @param pattern The pattern to broadcast a flush message for
-     * @param cacheName The name of the cache to flush
      */
-    void signalPatternFlush(String pattern, String cacheName) {
+    void signalPatternFlush(String pattern) {
         if (log.isDebugEnabled()) {
-            log.debug("flushPattern called for cache '" + cacheName + "', pattern '" + pattern + "'");
+            log.debug("flushPattern called for cache pattern '" + pattern + "'");
         }
 
         if (!shuttingDown) {
-            bus.sendNotification(new ClusterNotification(ClusterNotification.FLUSH_PATTERN, cacheName, pattern));
+            bus.sendNotification(new ClusterNotification(ClusterNotification.FLUSH_PATTERN, pattern));
         }
     }
 
     /**
      * Broadcasts a flush message for an entire cache
      *
-     * @param cacheName The name of the cache to flush
+     * @param date the date and time the cache was flushed
      */
-    void signalCacheFlush(String cacheName) {
+    void signalCacheFlush(Date date) {
         if (log.isDebugEnabled()) {
-            log.debug("flushCache called for cache '" + cacheName + "'");
+            log.debug("flushCache called");
         }
 
         if (!shuttingDown) {
-            bus.sendNotification(new ClusterNotification(ClusterNotification.FLUSH_CACHE, cacheName, null));
+            bus.sendNotification(new ClusterNotification(ClusterNotification.FLUSH_CACHE, date));
         }
     }
 
     /**
-     * Sets the adminstrator for this cluster manager. We need this so we can
-     * look up named caches.
+     * Hold a copy of the cache so we can apply asynchronous messages to it
+     * as they arrive.
      */
-    public void setAdministrator(AbstractCacheAdministrator admin) {
-        this.admin = admin;
+    public void setCache(Cache cache) {
+        this.cache = cache;
     }
 }

src/plugins/clustersupport/java/com/opensymphony/oscache/plugins/clustersupport/ClusterNotification.java

     static final int FLUSH_CACHE = 4;
 
     /**
-     * The name of the cache that this notification applies to
-     */
-    private String cacheName;
-
-    /**
      * Any additional data that may be required
      */
-    private String data;
+    private Serializable data;
 
     /**
      * The type of notification message.
      *
      * @param type       The type of notification message. Valid types are
      *                   {@link #FLUSH_KEY} and {@link #FLUSH_GROUP}.
-     * @param cacheName  The name of the cache that this message applies to.
-     *                   This is required so the remote listeners can locate
-     *                   the correct cache to flush.
      * @param data       Specifies the object key or group name to flush.
      */
-    public ClusterNotification(int type, String cacheName, String data) {
+    public ClusterNotification(int type, Serializable data) {
         this.type = type;
-        this.cacheName = cacheName;
         this.data = data;
     }
 
     /**
-     * The name of the cache that this message should be applied to. The cache
-     * will be looked up using the cache's administrator object.
+     * Holds any additional data that was required
      */
-    public String getCacheName() {
-        return cacheName;
-    }
-
-    /**
-     * Specifies the object key or group name to flush.
-     */
-    public String getData() {
+    public Serializable getData() {
         return data;
     }
 
 
     public String toString() {
         StringBuffer buf = new StringBuffer();
-        buf.append("type=").append(type).append(", cacheName=");
-        buf.append(cacheName).append(", data=").append(data);
+        buf.append("type=").append(type).append(", data=").append(data);
 
         return buf.toString();
     }

src/plugins/clustersupport/test/com/opensymphony/oscache/plugins/clustersupport/TestClusterManager.java

  */
 public final class TestClusterManager extends TestCase {
     /**
-     * Name of the cache to flush
-     */
-    private final String CACHE_NAME = "test";
-
-    /**
      * Cache group
      */
     private final String GROUP = "test group";
             ClusterManager cm = new ClusterManager(config);
 
             // Send some flush signals
-            cm.signalEntryFlush(KEY, CACHE_NAME);
-            cm.signalGroupFlush(GROUP, CACHE_NAME);
+            cm.signalEntryFlush(KEY);
+            cm.signalGroupFlush(GROUP);
 
             // Simulate receiving some signals
-            cm.handleNotification(new ClusterNotification(ClusterNotification.FLUSH_KEY, CACHE_NAME, GROUP));
-            cm.handleNotification(new ClusterNotification(ClusterNotification.FLUSH_GROUP, CACHE_NAME, GROUP));
+            cm.handleNotification(new ClusterNotification(ClusterNotification.FLUSH_KEY, GROUP));
+            cm.handleNotification(new ClusterNotification(ClusterNotification.FLUSH_GROUP, GROUP));
 
             // Shutdown the cache manager
             cm.shutdown();