[jboss-cvs] JBossAS SVN: r71527 - in projects/ejb3/branches/cluster-dev/cache-jbc2/src: test/java/org/jboss/ejb3/test/cache/impl/backing/jbc2 and 1 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Mon Mar 31 17:43:07 EDT 2008


Author: bstansberry at jboss.com
Date: 2008-03-31 17:43:07 -0400 (Mon, 31 Mar 2008)
New Revision: 71527

Added:
   projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCBackingCacheEntryStore.java
   projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCBackingCacheEntryStoreSource.java
Removed:
   projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/ContextInUseException.java
   projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCIntegratedObjectStore.java
   projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCIntegratedObjectStoreSource.java
Modified:
   projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/OwnedItem.java
   projects/ejb3/branches/cluster-dev/cache-jbc2/src/test/java/org/jboss/ejb3/test/cache/impl/backing/jbc2/GroupedPassivatingUnitTestCase.java
   projects/ejb3/branches/cluster-dev/cache-jbc2/src/test/java/org/jboss/ejb3/test/cache/jbc2/mock/JBC2MockCluster.java
   projects/ejb3/branches/cluster-dev/cache-jbc2/src/test/java/org/jboss/ejb3/test/cache/jbc2/mock/JBC2MockClusterMember.java
Log:
[EJBTHREE-1026] Strip out extraneous abstractions, rename abstractions

Deleted: projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/ContextInUseException.java
===================================================================
--- projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/ContextInUseException.java	2008-03-31 21:42:45 UTC (rev 71526)
+++ projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/ContextInUseException.java	2008-03-31 21:43:07 UTC (rev 71527)
@@ -1,56 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2006, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-
-package org.jboss.ejb3.cache.impl.backing.jbc2;
-
-/**
- * Exception thrown by JBCIntegratedObjectStore if an attempt is made
- * to passivate a bean that is currently in use.
- * 
- * @author <a href="brian.stansberry at jboss.com">Brian Stansberry</a>
- * @version $Revision: 1.1 $
- */
-public class ContextInUseException extends RuntimeException
-{
-   /** The serialVersionUID */
-   private static final long serialVersionUID = 7731424431763921352L;
-
-   /**
-    * Create a new ContextInUseException.
-    * 
-    */
-   public ContextInUseException()
-   {
-      super();
-   }
-
-   /**
-    * Create a new ContextInUseException.
-    * 
-    * @param message
-    */
-   public ContextInUseException(String message)
-   {
-      super(message);
-   }
-
-}

Copied: projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCBackingCacheEntryStore.java (from rev 71454, projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCIntegratedObjectStore.java)
===================================================================
--- projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCBackingCacheEntryStore.java	                        (rev 0)
+++ projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCBackingCacheEntryStore.java	2008-03-31 21:43:07 UTC (rev 71527)
@@ -0,0 +1,729 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.ejb3.cache.impl.backing.jbc2;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.CacheException;
+import org.jboss.cache.CacheManager;
+import org.jboss.cache.CacheStatus;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.Node;
+import org.jboss.cache.Region;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.cache.config.BuddyReplicationConfig;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.notifications.event.NodeActivatedEvent;
+import org.jboss.cache.notifications.event.NodeModifiedEvent;
+import org.jboss.cache.notifications.event.NodePassivatedEvent;
+import org.jboss.cache.notifications.event.NodeRemovedEvent;
+import org.jboss.cache.notifications.event.NodeVisitedEvent;
+import org.jboss.ejb3.annotation.CacheConfig;
+import org.jboss.ejb3.cache.api.CacheItem;
+import org.jboss.ejb3.cache.spi.BackingCacheEntry;
+import org.jboss.ejb3.cache.spi.GroupCompatibilityChecker;
+import org.jboss.ejb3.cache.spi.SerializationGroup;
+import org.jboss.ejb3.cache.spi.impl.AbstractBackingCacheEntryStore;
+import org.jboss.ejb3.cache.spi.impl.CacheableTimestamp;
+import org.jboss.logging.Logger;
+import org.jboss.util.id.GUID;
+
+/**
+ * JBoss Cache-based implementation of {@link PassivatingIntegratedObjectStore}.
+ * 
+ * @author Brian Stansberry
+ */
+public class JBCBackingCacheEntryStore<C extends CacheItem, T extends BackingCacheEntry<C>>
+   extends AbstractBackingCacheEntryStore<C, T, OwnedItem>
+{
+   /** First element in EJB3 SFSB Fqns */
+   private static final String FQN_BASE = "sfsb";
+   /** Key under which items are stored in cache nodes */
+   private static final String KEY = "item";
+   
+   /** Hack used to avoid having to make 2 calls to remove an entry */
+   @SuppressWarnings("unchecked")
+   private static final ThreadLocal removedItem = new ThreadLocal();
+   
+   /** Depth of fqn element where we store the entry. */ 
+   static final int FQN_SIZE = 4;
+   /**  
+    * Number of "buckets" under the region root -- used to increase
+    * the number of items FileCacheLoader can store w/o hitting
+    * filesystem limits.
+    */
+   private static final int DEFAULT_BUCKET_COUNT = 100;
+   /** The names of the aforementioned buckets */
+   private static final String[] DEFAULT_HASH_BUCKETS = new String[DEFAULT_BUCKET_COUNT];
+
+   static
+   {
+      for (int i = 0; i < DEFAULT_HASH_BUCKETS.length; i++)
+      {
+         DEFAULT_HASH_BUCKETS[i] = String.valueOf(i);
+      }
+   }
+   
+   /** The CacheManager from which we obtain our JBC instance */
+   private final CacheManager cacheManager;
+   /** The name of the config to request from the CacheManager */
+   private final String cacheConfigName;
+   
+   /** The underlying JBC instance */
+   private Cache<Object, T> jbc;
+
+   /** Qualifier used to scope our Fqns */
+   private final Object keyBase;
+
+   /** Fqn of region where we store items */
+   private Fqn<Object> regionRootFqn;
+   /** The region where we store items */
+   private Region region;
+   /** Listener for cache event notifications */
+   private ClusteredCacheListener listener;
+   /** Handler for listener events related to our region */
+   private RegionHandlerImpl regionHandler;
+   /** Our "buckets". See above */
+   private String[] hashBuckets = DEFAULT_HASH_BUCKETS;
+   /** Whether our cache is using buddy replication */
+   private boolean usingBuddyRepl;
+   /** Whether our RegionHandlerImpl should track visits */
+   private boolean trackVisits;
+   /** Last-use timestamps of in-memory items.  Used for passivation and expiration*/
+   private final ConcurrentMap<OwnedItem, OwnedItem> inMemoryItems;
+   /** Last-use timestamps of passivated items.  Used for expiration*/
+   private final ConcurrentMap<OwnedItem, OwnedItem> passivatedItems;
+   
+   /**
+    * Create a new JBCBackingCacheEntryStore.
+    * 
+    * @param cacheManager Source for our JBoss Cache instance
+    * @param cacheConfigName name of config to request from CacheManager
+    * @param cacheConfig configuration metadata
+    * @param name our name
+    * @param forGroups <code>true</code> if this cache is used for caching
+    *                  {@link SerializationGroup}s, <code>false</code> otherwise
+    */
+   public JBCBackingCacheEntryStore(CacheManager cacheManager,
+                                   String cacheConfigName,
+                                   CacheConfig cacheConfig,
+                                   String name,
+                                   boolean forGroups)
+   {     
+      super(cacheConfig, name, forGroups);
+      
+      assert cacheManager != null : "cacheManager is null";
+      assert cacheConfigName != null : "cacheConfigName is null";
+      
+      this.cacheManager = cacheManager;
+      this.cacheConfigName = cacheConfigName;
+      this.keyBase = name;
+
+      this.log = Logger.getLogger(getClass().getName() + "-" + name);
+      this.regionRootFqn = new Fqn<Object>(new Object[] { FQN_BASE, this.keyBase });
+      
+      this.inMemoryItems = new ConcurrentHashMap<OwnedItem, OwnedItem>();
+      this.passivatedItems = new ConcurrentHashMap<OwnedItem, OwnedItem>();
+   }
+
+   public boolean isClustered()
+   {
+      if (jbc == null)
+         return true; // assume yes
+      else
+         return jbc.getConfiguration().getCacheMode() != Configuration.CacheMode.LOCAL;
+   }
+
+   public T get(Object key)
+   {
+      T entry = null;
+      Fqn<Object> id = getFqn(key, false);
+      try
+      {
+         // If need be, gravitate
+         if (usingBuddyRepl)
+         {
+            jbc.getInvocationContext().getOptionOverrides().setForceDataGravitation(true);
+         }
+         entry = (T) jbc.get(id, KEY);
+      }
+      catch (CacheException e)
+      {
+         RuntimeException re = convertToRuntimeException(e);
+         throw re;
+      }
+
+      if(log.isTraceEnabled())
+      {
+         log.trace("get: retrieved bean with cache id " +id.toString());
+      }
+
+      return entry;
+   }
+
+   public void insert(T entry)
+   {
+      try
+      {
+         if (log.isTraceEnabled())
+            log.trace("insert: " + entry.getId());
+         
+         jbc.put(getFqn(entry.getId(), false), KEY, entry);
+      }
+      catch (CacheException e)
+      {
+         RuntimeException re = convertToRuntimeException(e);
+         throw re;
+      }
+   }
+
+   public void passivate(T entry)
+   {
+      if (log.isTraceEnabled())
+         log.trace("passivate: " + entry.getId());
+      
+      Fqn<Object> id = getFqn(entry.getId(), false);
+      jbc.evict(id);
+   }
+
+   @SuppressWarnings("unchecked")
+   public T remove(Object key)
+   {
+      Fqn<Object> id = getFqn(key, false);
+      try
+      {
+         if(log.isTraceEnabled())
+         {
+            log.trace("remove: cache id " +id.toString());
+         }
+         
+         if (usingBuddyRepl)
+         {
+            jbc.getInvocationContext().getOptionOverrides().setForceDataGravitation(true);
+         }
+         jbc.removeNode(id);
+
+         // Hack! Our cache listener has access to the removed node's data map
+         // so it passes the removed item to us via a thread local.
+         // Otherwise we'd have to do a remove(id, KEY) followed by removeNode(id)
+         T removed = (T) removedItem.get();
+         removedItem.set(null);
+         
+         return removed;
+      }
+      catch (CacheException e)
+      {
+         RuntimeException re = convertToRuntimeException(e);
+         throw re;
+      }
+   }
+
+   public void update(T entry, boolean modified)
+   {
+      if (log.isTraceEnabled())
+         log.trace("update: " + entry.getId() + 
+                   (modified ? " modified" : " unmodified"));
+      
+      if (modified)
+      {
+         try
+         {
+            jbc.put(getFqn(entry.getId(), false), KEY, entry);
+         }
+         catch (CacheException e)
+         {
+            RuntimeException re = convertToRuntimeException(e);
+            throw re;
+         }
+      }
+      else
+      {
+         OwnedItem oi = new OwnedItem(entry.getId(), regionRootFqn);
+         oi.setLastUsed(entry.getLastUsed());
+         inMemoryItems.put(oi, oi);
+      }
+   }
+
+   public int getInMemoryCount()
+   {
+      return inMemoryItems.size();
+   }
+
+   public int getPassivatedCount()
+   {
+       return passivatedItems.size();
+   }
+
+   @SuppressWarnings("unchecked")
+   public boolean isCompatibleWith(GroupCompatibilityChecker other)
+   {
+      if (other instanceof JBCBackingCacheEntryStore)
+      {
+         JBCBackingCacheEntryStore jbc2 = (JBCBackingCacheEntryStore) other;
+         return this.cacheManager == jbc2.cacheManager
+                 && this.cacheConfigName.equals(jbc2.cacheConfigName);
+      }
+      return false;
+   }
+
+   protected void internalStart()
+   {      
+      initializeJBossCache();
+      
+      region = jbc.getRegion(regionRootFqn, true);
+      
+      // Try to create an eviction region per ejb
+      // BES 2008/03/12 No, let's handle passivation ourselves
+      // since JBC doesn't properly track the buddy-backup region
+//      EvictionPolicyConfig epc = getEvictionPolicyConfig();
+//      region.setEvictionPolicy(epc);
+
+      // JBCACHE-1136.  There's no reason to have state in an inactive region
+      cleanBeanRegion();
+
+      // Transfer over the state for the region
+      region.registerContextClassLoader(Thread.currentThread().getContextClassLoader());
+      region.activate();
+      
+      // register to listen for cache events
+
+      for (Object listener : jbc.getCacheListeners())
+      {
+         if (listener instanceof ClusteredCacheListener)
+         {
+            this.listener = (ClusteredCacheListener) listener;
+            break;
+         }
+      }
+      
+      if (listener == null)
+      {
+         listener = new ClusteredCacheListener(usingBuddyRepl);
+         jbc.addCacheListener(listener);
+      }
+      
+      regionHandler = new RegionHandlerImpl();
+      listener.addRegionHandler(regionRootFqn, regionHandler);
+      
+      initializeTrackingMaps();
+      
+      super.internalStart();
+   }
+   
+   @SuppressWarnings("unchecked")
+   private void initializeJBossCache()
+   {
+      try
+      {
+         this.jbc = (Cache<Object, T>) cacheManager.getCache(cacheConfigName, true);
+      }
+      catch (CacheException e)
+      {
+         throw convertToRuntimeException(e);
+      }
+      catch (RuntimeException e)
+      {
+         throw e;
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException("Problem getting cache from CacheManager", e);
+      }
+      
+      if (this.jbc.getCacheStatus() != CacheStatus.STARTED)
+      {
+         if (this.jbc.getCacheStatus() != CacheStatus.CREATED)
+         {
+            this.jbc.create();
+         }
+         
+         this.jbc.start();            
+      }
+      
+      BuddyReplicationConfig brc = jbc.getConfiguration().getBuddyReplicationConfig();
+      this.usingBuddyRepl = brc != null && brc.isEnabled();
+   }
+
+   private void initializeTrackingMaps()
+   {      
+      // First the main tree
+      Node<Object, T> parent = jbc.getNode(regionRootFqn);
+      analyzeRegionContent(parent);
+     
+      // Now any buddy regions
+      if (usingBuddyRepl)
+      {
+         Node<Object, T> bbRoot = jbc.getNode(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN);
+         if (bbRoot != null)
+         {
+            for (Node<Object, T> bbRegion : bbRoot.getChildren())
+            {
+               Node<Object, T> ourPart = bbRegion.getChild(regionRootFqn);
+               if (ourPart != null)
+               {
+                  analyzeRegionContent(ourPart);
+               }
+            }
+         }
+      }
+      
+      // Now, we know any node visits will be from users, so
+      // lets start monitoring them
+      trackVisits = true;
+   }
+
+   private void analyzeRegionContent(Node<Object, T> parent)
+   {
+      for (int i = 0; i < hashBuckets.length; i++)
+      {
+         Node<Object, T> bucket = parent.getChild(hashBuckets[i]);
+         if (bucket == null)
+            continue;
+         Set<Object> childrenNames = bucket.getChildrenNames();
+         for (Object name : childrenNames)
+         {
+            Node<Object, T> child = bucket.getChild(name);
+            if (child == null)
+               continue;
+            T entry = (T) child.get(KEY);
+            if (entry != null)
+            {
+               boolean localEvent = false; // we don't own any of these
+               OwnedItem oi = OwnedItem.getOwnedItem(child.getFqn(), usingBuddyRepl, localEvent);
+               if (entry.isPrePassivated())
+               {
+                  jbc.evict(child.getFqn()); // we'll get a listener event for this
+               }
+               else
+               {
+                  oi.setLastUsed(entry.getLastUsed() == 0 ? System.currentTimeMillis() : entry.getLastUsed());
+                  // Use putIfAbsent so we don't overwrite listener events
+                  inMemoryItems.putIfAbsent(oi, oi);
+               }
+            }
+         }
+      }
+   }
+   
+   protected void internalStop()
+   {
+      super.internalStop();
+      
+      if (jbc != null)
+      {
+         try
+         {
+            // Remove the listener
+            if (listener != null && regionHandler != null && listener.removeRegionHandler(regionRootFqn))
+            {
+               // No more regions registered, so remove the listener
+               jbc.removeCacheListener(listener);
+            }
+            
+            // Null the listener so we have to re-check the cache is we start() again
+            listener = null;
+   
+            // Remove locally. We do this to clean up the persistent store,
+            // which is not affected by the inactivateRegion call below.
+            cleanBeanRegion();
+   
+            if (region != null)
+            {
+               region.deactivate();
+               region.unregisterContextClassLoader();
+   
+               jbc.removeRegion(region.getFqn());
+               region = null;
+            }
+         }
+         finally
+         {
+            cacheManager.releaseCache(cacheConfigName);
+            jbc = null;
+         }         
+      }
+      
+      inMemoryItems.clear();
+      passivatedItems.clear();
+   }
+
+   @Override
+   protected void processExpiration(OwnedItem ownedItem, long lastUse)
+   {
+      if (!isRunning())
+         return;
+      
+      // We only remove items we control
+      if (OwnedItem.LOCAL_OWNER.equals(ownedItem.getOwner()))
+      {
+         getPassivatingCache().remove(ownedItem.getId());
+      }
+      
+      // TODO -- add some mechanism for *eventually* removing items we
+      // don't control, in case their owner died. Eventually meaning
+      // after a day or so.
+   }
+
+   @Override
+   protected void processPassivation(OwnedItem ownedItem, long lastUse)
+   {
+      if (!isRunning())
+         return;
+      
+      synchronized (ownedItem)
+      {
+         String owner = ownedItem.getOwner();
+         if (OwnedItem.LOCAL_OWNER.equals(owner))
+         {
+            getPassivatingCache().passivate(ownedItem.getId());
+         }
+         else
+         {
+            // No callbacks; the item is serialized and under the control of 
+            // another cache. We just want it out of our memory
+            
+            // FIXME race condition here; we might have taken ownership!
+            
+            Fqn<Object> id = null;
+            if (OwnedItem.REMOTE_OWNER.equals(owner))
+            {
+               id = getFqn(ownedItem.getId(), false);
+            }
+            else {
+               id = getBuddyFqn(ownedItem.getId(), ownedItem.getOwner());
+            }
+            
+            if (log.isTraceEnabled())
+               log.trace("evicting remotely owned item " + ownedItem.getId());
+            
+            jbc.evict(id);
+         }
+      }
+   }
+
+   @Override
+   @SuppressWarnings("unchecked")
+   protected CacheableTimestamp<OwnedItem>[] getInMemoryEntries()
+   {     
+      Set<CacheableTimestamp<OwnedItem>> set = new HashSet<CacheableTimestamp<OwnedItem>>();
+      for (OwnedItem ownedItem : inMemoryItems.values())
+      {
+         set.add(new CacheableTimestamp<OwnedItem>(ownedItem, ownedItem.getLastUsed()));
+      }
+      CacheableTimestamp<OwnedItem>[] array = new CacheableTimestamp[set.size()];
+      array = set.toArray(array);
+      Arrays.sort(array);
+      return array;
+   }
+
+   @Override
+   @SuppressWarnings("unchecked")
+   protected CacheableTimestamp<OwnedItem>[] getAllEntries()
+   {     
+      Set<CacheableTimestamp<OwnedItem>> set = new HashSet<CacheableTimestamp<OwnedItem>>();
+      for (OwnedItem ownedItem : inMemoryItems.values())
+      {
+         set.add(new CacheableTimestamp<OwnedItem>(ownedItem, ownedItem.getLastUsed()));
+      }
+      CacheableTimestamp<Object>[] inMemory = new CacheableTimestamp[set.size()];
+      inMemory = set.toArray(inMemory);   
+      
+      set = new HashSet<CacheableTimestamp<OwnedItem>>();
+      for (OwnedItem ownedItem : passivatedItems.values())
+      {
+         set.add(new CacheableTimestamp<OwnedItem>(ownedItem, ownedItem.getLastUsed()));
+      }
+      CacheableTimestamp<OwnedItem>[] passivated = new CacheableTimestamp[set.size()];
+      passivated = set.toArray(passivated);
+
+      CacheableTimestamp<OwnedItem>[] all = new CacheableTimestamp[passivated.length + inMemory.length];
+      System.arraycopy(passivated, 0, all, 0, passivated.length);
+      System.arraycopy(inMemory, 0, all, passivated.length, inMemory.length);
+      Arrays.sort(all);
+      return all;
+   }
+
+   private Fqn<Object> getFqn(Object id, boolean regionRelative)
+   {
+      String beanId = id.toString();
+      int index = getIndexForId(id, beanId);
+
+      if (regionRelative)
+         return new Fqn<Object>( new Object[] {hashBuckets[index], beanId} );
+      else
+         return new Fqn<Object>(regionRootFqn, hashBuckets[index], beanId);
+   }
+   
+   private Fqn<Object> getBuddyFqn(Object id, Object owner)
+   {
+      assert owner != null : "owner cannot be null for a buddy backup Fqn";
+      
+      return new Fqn<Object>(new Object[] { BuddyManager.BUDDY_BACKUP_SUBTREE, owner, FQN_BASE, keyBase, id});
+   }
+   
+   private int getIndexForId(Object id, String stringForm)
+   {
+      int index;
+      if (id instanceof GUID)
+      {
+         index = (id.hashCode()& 0x7FFFFFFF) % hashBuckets.length;
+      }
+      else
+      {
+         index = (stringForm.hashCode()& 0x7FFFFFFF) % hashBuckets.length;
+      }
+      
+      return index;
+   }
+
+   /**
+    * Creates a RuntimeException, but doesn't pass CacheException as the cause
+    * as it is a type that likely doesn't exist on a client.
+    * Instead creates a RuntimeException with the original exception's
+    * stack trace.
+    */
+   private RuntimeException convertToRuntimeException(CacheException e)
+   {
+      RuntimeException re = new RuntimeException(e.getClass().getName() + " " + e.getMessage());
+      re.setStackTrace(e.getStackTrace());
+      return re;
+   }
+
+   private void cleanBeanRegion()
+   {
+      try {
+         // Remove locally.
+         jbc.getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
+         jbc.removeNode(regionRootFqn);
+      }
+      catch (CacheException e)
+      {
+         log.error("Can't clean region " + regionRootFqn + " in the underlying distributed cache", e);
+      }
+   }
+
+   /**
+    * A CacheListener that allows us to get notifications of passivations and
+    * activations and thus notify the cached StatefulBeanContext.
+    */
+   public class RegionHandlerImpl implements ClusteredCacheListener.RegionHandler
+   {
+      public void nodeVisited(OwnedItem oi, NodeVisitedEvent event)
+      {
+         if (trackVisits)
+         {
+            long now = System.currentTimeMillis();
+            oi.setLastUsed(now);
+            OwnedItem existing = inMemoryItems.putIfAbsent(oi, oi);
+            if (existing != null && !existing.isPassivating())
+            {
+               existing.setLastUsed(now);
+               existing.setLocallyOwned(event.isOriginLocal());
+            }               
+         }
+      }
+      
+      public void nodeModified(OwnedItem oi, NodeModifiedEvent event)
+      {
+         @SuppressWarnings("unchecked")
+         T entry = (T) event.getData().get(KEY);
+         if (entry != null)
+         {
+            long lastUsed = entry.getLastUsed();
+            lastUsed = (lastUsed == 0 ? System.currentTimeMillis() : lastUsed);
+            oi.setLastUsed(lastUsed);
+            OwnedItem existing = inMemoryItems.putIfAbsent(oi, oi);
+            if (existing != null && !existing.isPassivating())
+            {
+               existing.setLastUsed(lastUsed);
+               existing.setLocallyOwned(event.isOriginLocal());
+            }               
+            
+            if (log.isTraceEnabled())
+               log.trace(oi + " modified " + (event.isOriginLocal() ? " locally" : "remotely") );
+         }       
+      }
+      
+      @SuppressWarnings("unchecked")
+      public void nodeRemoved(OwnedItem oi, NodeRemovedEvent event)
+      {
+         inMemoryItems.remove(oi);
+         passivatedItems.remove(oi);
+         
+         // Hack! We have access to the data map here; so pass the removed
+         // item to our remove() method via a thread local
+         removedItem.set(event.getData().get(KEY));
+         
+         if (log.isTraceEnabled())
+            log.trace(oi + " removed " + (event.isOriginLocal() ? " locally" : "remotely") );
+      }
+      
+      public void nodeActivated(OwnedItem oi, NodeActivatedEvent event)
+      {
+         @SuppressWarnings("unchecked")
+         T entry = (T) event.getData().get(KEY);
+         if (entry != null)
+         {
+            long lastUsed = entry.getLastUsed();
+            oi.setLastUsed(lastUsed == 0 ? System.currentTimeMillis() : lastUsed);
+            OwnedItem existing = inMemoryItems.putIfAbsent(oi, oi);
+            if (existing == null)
+            {
+               passivatedItems.remove(oi);
+            }
+            else if (!existing.isPassivating())
+            {
+               existing.setLocallyOwned(event.isOriginLocal());
+            }
+            
+            if (log.isTraceEnabled())
+               log.trace(oi + " activated -- " + (event.isOriginLocal() ? " local" : "remote") );
+         }        
+      }
+
+      public void nodePassivated(OwnedItem oi, NodePassivatedEvent event)
+      {
+         @SuppressWarnings("unchecked")
+         T entry = (T) event.getData().get(KEY);
+         if (entry != null)
+         {
+            long lastUsed = entry.getLastUsed();
+            oi.setLastUsed(lastUsed == 0 ? System.currentTimeMillis() : lastUsed);
+            OwnedItem existing = passivatedItems.putIfAbsent(oi, oi);
+            if (existing == null)
+            {
+               inMemoryItems.remove(oi);
+            }
+            
+            if (log.isTraceEnabled())
+               log.trace(oi + " passivated");
+         } 
+      }
+   }
+
+}

Copied: projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCBackingCacheEntryStoreSource.java (from rev 71454, projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCIntegratedObjectStoreSource.java)
===================================================================
--- projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCBackingCacheEntryStoreSource.java	                        (rev 0)
+++ projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCBackingCacheEntryStoreSource.java	2008-03-31 21:43:07 UTC (rev 71527)
@@ -0,0 +1,71 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.ejb3.cache.impl.backing.jbc2;
+
+import javax.transaction.TransactionManager;
+
+import org.jboss.cache.CacheManager;
+import org.jboss.ejb3.annotation.CacheConfig;
+import org.jboss.ejb3.cache.api.CacheItem;
+import org.jboss.ejb3.cache.spi.BackingCacheEntryStore;
+import org.jboss.ejb3.cache.spi.BackingCacheEntryStoreSource;
+import org.jboss.ejb3.cache.spi.SerializationGroup;
+import org.jboss.ejb3.cache.spi.SerializationGroupMember;
+import org.jboss.ejb3.cache.spi.SynchronizationCoordinator;
+
+/**
+ * {@link IntegratedObjectStoreSource} that provides instances of
+ * {@link JBCBackingCacheEntryStore}.
+ * 
+ * @author Brian Stansberry
+ */
+public class JBCBackingCacheEntryStoreSource<T extends CacheItem> 
+   implements BackingCacheEntryStoreSource<T>
+{
+   private CacheManager cacheManager;
+   
+   public BackingCacheEntryStore<T, SerializationGroup<T>>  createGroupIntegratedObjectStore(String containerName,
+         String cacheConfigName, CacheConfig cacheConfig, TransactionManager transactionManager, SynchronizationCoordinator synchronizationCoordinator)
+   {
+      String nameSuffix = (containerName == null || containerName.length() == 0) ? "" : "-" + containerName;
+      String name = "GroupCache" + nameSuffix;
+      return new JBCBackingCacheEntryStore<T, SerializationGroup<T>>(cacheManager, cacheConfigName, cacheConfig, name, true);
+   }
+
+   public BackingCacheEntryStore<T, SerializationGroupMember<T>>  createIntegratedObjectStore(String containerName, String cacheConfigName,
+         CacheConfig cacheConfig, TransactionManager transactionManager, SynchronizationCoordinator synchronizationCoordinator)
+   {
+      return new JBCBackingCacheEntryStore<T, SerializationGroupMember<T>>(cacheManager, cacheConfigName, cacheConfig, containerName, false);
+   }
+
+   public CacheManager getCacheManager()
+   {
+      return cacheManager;
+   }
+
+   public void setCacheManager(CacheManager cacheManager)
+   {
+      this.cacheManager = cacheManager;
+   }
+
+}

Deleted: projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCIntegratedObjectStore.java
===================================================================
--- projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCIntegratedObjectStore.java	2008-03-31 21:42:45 UTC (rev 71526)
+++ projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCIntegratedObjectStore.java	2008-03-31 21:43:07 UTC (rev 71527)
@@ -1,730 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-
-package org.jboss.ejb3.cache.impl.backing.jbc2;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import org.jboss.cache.Cache;
-import org.jboss.cache.CacheException;
-import org.jboss.cache.CacheManager;
-import org.jboss.cache.CacheStatus;
-import org.jboss.cache.Fqn;
-import org.jboss.cache.Node;
-import org.jboss.cache.Region;
-import org.jboss.cache.buddyreplication.BuddyManager;
-import org.jboss.cache.config.BuddyReplicationConfig;
-import org.jboss.cache.config.Configuration;
-import org.jboss.cache.notifications.event.NodeActivatedEvent;
-import org.jboss.cache.notifications.event.NodeModifiedEvent;
-import org.jboss.cache.notifications.event.NodePassivatedEvent;
-import org.jboss.cache.notifications.event.NodeRemovedEvent;
-import org.jboss.cache.notifications.event.NodeVisitedEvent;
-import org.jboss.ejb3.annotation.CacheConfig;
-import org.jboss.ejb3.cache.api.CacheItem;
-import org.jboss.ejb3.cache.spi.GroupCompatibilityChecker;
-import org.jboss.ejb3.cache.spi.PassivatingBackingCacheEntry;
-import org.jboss.ejb3.cache.spi.PassivatingIntegratedObjectStore;
-import org.jboss.ejb3.cache.spi.SerializationGroup;
-import org.jboss.ejb3.cache.spi.impl.AbstractPassivatingIntegratedObjectStore;
-import org.jboss.ejb3.cache.spi.impl.CacheableTimestamp;
-import org.jboss.logging.Logger;
-import org.jboss.util.id.GUID;
-
-/**
- * JBoss Cache-based implementation of {@link PassivatingIntegratedObjectStore}.
- * 
- * @author Brian Stansberry
- */
-public class JBCIntegratedObjectStore<C extends CacheItem, T extends PassivatingBackingCacheEntry<C>>
-   extends AbstractPassivatingIntegratedObjectStore<C, T, OwnedItem>
-{
-   /** First element in EJB3 SFSB Fqns */
-   private static final String FQN_BASE = "sfsb";
-   /** Key under which items are stored in cache nodes */
-   private static final String KEY = "item";
-   
-   /** Hack used to avoid having to make 2 calls to remove an entry */
-   @SuppressWarnings("unchecked")
-   private static final ThreadLocal removedItem = new ThreadLocal();
-   
-   /** Depth of fqn element where we store the entry. */ 
-   static final int FQN_SIZE = 4;
-   /**  
-    * Number of "buckets" under the region root -- used to increase
-    * the number of items FileCacheLoader can store w/o hitting
-    * filesystem limits.
-    */
-   private static final int DEFAULT_BUCKET_COUNT = 100;
-   /** The names of the aforementioned buckets */
-   private static final String[] DEFAULT_HASH_BUCKETS = new String[DEFAULT_BUCKET_COUNT];
-
-   static
-   {
-      for (int i = 0; i < DEFAULT_HASH_BUCKETS.length; i++)
-      {
-         DEFAULT_HASH_BUCKETS[i] = String.valueOf(i);
-      }
-   }
-   
-   /** The CacheManager from which we obtain our JBC instance */
-   private final CacheManager cacheManager;
-   /** The name of the config to request from the CacheManager */
-   private final String cacheConfigName;
-   
-   /** The underlying JBC instance */
-   private Cache<Object, T> jbc;
-
-   /** Qualifier used to scope our Fqns */
-   private final Object keyBase;
-
-   /** Fqn of region where we store items */
-   private Fqn<Object> regionRootFqn;
-   /** The region where we store items */
-   private Region region;
-   /** Listener for cache event notifications */
-   private ClusteredCacheListener listener;
-   /** Handler for listener events related to our region */
-   private RegionHandlerImpl regionHandler;
-   /** Our "buckets". See above */
-   private String[] hashBuckets = DEFAULT_HASH_BUCKETS;
-   /** Whether our cache is using buddy replication */
-   private boolean usingBuddyRepl;
-   /** Whether our RegionHandlerImpl should track visits */
-   private boolean trackVisits;
-   /** Last-use timestamps of in-memory items.  Used for passivation and expiration*/
-   private final ConcurrentMap<OwnedItem, OwnedItem> inMemoryItems;
-   /** Last-use timestamps of passivated items.  Used for expiration*/
-   private final ConcurrentMap<OwnedItem, OwnedItem> passivatedItems;
-   
-   /**
-    * Create a new JBCIntegratedObjectStore.
-    * 
-    * @param cacheManager Source for our JBoss Cache instance
-    * @param cacheConfigName name of config to request from CacheManager
-    * @param cacheConfig configuration metadata
-    * @param name our name
-    * @param forGroups <code>true</code> if this cache is used for caching
-    *                  {@link SerializationGroup}s, <code>false</code> otherwise
-    */
-   public JBCIntegratedObjectStore(CacheManager cacheManager,
-                                   String cacheConfigName,
-                                   CacheConfig cacheConfig,
-                                   String name,
-                                   boolean forGroups)
-   {     
-      super(cacheConfig, name, forGroups);
-      
-      assert cacheManager != null : "cacheManager is null";
-      assert cacheConfigName != null : "cacheConfigName is null";
-      
-      this.cacheManager = cacheManager;
-      this.cacheConfigName = cacheConfigName;
-      this.keyBase = name;
-
-      this.log = Logger.getLogger(getClass().getName() + "-" + name);
-      this.regionRootFqn = new Fqn<Object>(new Object[] { FQN_BASE, this.keyBase });
-      
-      this.inMemoryItems = new ConcurrentHashMap<OwnedItem, OwnedItem>();
-      this.passivatedItems = new ConcurrentHashMap<OwnedItem, OwnedItem>();
-   }
-
-   public boolean isClustered()
-   {
-      if (jbc == null)
-         return true; // assume yes
-      else
-         return jbc.getConfiguration().getCacheMode() != Configuration.CacheMode.LOCAL;
-   }
-
-   public T get(Object key)
-   {
-      T entry = null;
-      Fqn<Object> id = getFqn(key, false);
-      try
-      {
-         // If need be, gravitate
-         if (usingBuddyRepl)
-         {
-            jbc.getInvocationContext().getOptionOverrides().setForceDataGravitation(true);
-         }
-         entry = (T) jbc.get(id, KEY);
-      }
-      catch (CacheException e)
-      {
-         RuntimeException re = convertToRuntimeException(e);
-         throw re;
-      }
-
-      if(log.isTraceEnabled())
-      {
-         log.trace("get: retrieved bean with cache id " +id.toString());
-      }
-
-      return entry;
-   }
-
-   public void insert(T entry)
-   {
-      try
-      {
-         if (log.isTraceEnabled())
-            log.trace("insert: " + entry.getId());
-         
-         jbc.put(getFqn(entry.getId(), false), KEY, entry);
-      }
-      catch (CacheException e)
-      {
-         RuntimeException re = convertToRuntimeException(e);
-         throw re;
-      }
-   }
-
-   public void passivate(T entry)
-   {
-      if (log.isTraceEnabled())
-         log.trace("passivate: " + entry.getId());
-      
-      Fqn<Object> id = getFqn(entry.getId(), false);
-      jbc.evict(id);
-   }
-
-   @SuppressWarnings("unchecked")
-   public T remove(Object key)
-   {
-      Fqn<Object> id = getFqn(key, false);
-      try
-      {
-         if(log.isTraceEnabled())
-         {
-            log.trace("remove: cache id " +id.toString());
-         }
-         
-         if (usingBuddyRepl)
-         {
-            jbc.getInvocationContext().getOptionOverrides().setForceDataGravitation(true);
-         }
-         jbc.removeNode(id);
-
-         // Hack! Our cache listener has access to the removed node's data map
-         // so it passes the removed item to us via a thread local.
-         // Otherwise we'd have to do a remove(id, KEY) followed by removeNode(id)
-         T removed = (T) removedItem.get();
-         removedItem.set(null);
-         
-         return removed;
-      }
-      catch (CacheException e)
-      {
-         RuntimeException re = convertToRuntimeException(e);
-         throw re;
-      }
-   }
-
-   public void update(T entry, boolean modified)
-   {
-      if (log.isTraceEnabled())
-         log.trace("update: " + entry.getId() + 
-                   (modified ? " modified" : " unmodified"));
-      
-      if (modified)
-      {
-         try
-         {
-            jbc.put(getFqn(entry.getId(), false), KEY, entry);
-         }
-         catch (CacheException e)
-         {
-            RuntimeException re = convertToRuntimeException(e);
-            throw re;
-         }
-      }
-      else
-      {
-         OwnedItem oi = new OwnedItem(entry.getId(), regionRootFqn);
-         oi.setLastUsed(entry.getLastUsed());
-         inMemoryItems.put(oi, oi);
-      }
-   }
-
-   public int getInMemoryCount()
-   {
-      return inMemoryItems.size();
-   }
-
-   public int getPassivatedCount()
-   {
-       return passivatedItems.size();
-   }
-
-   @SuppressWarnings("unchecked")
-   public boolean isCompatibleWith(GroupCompatibilityChecker other)
-   {
-      if (other instanceof JBCIntegratedObjectStore)
-      {
-         JBCIntegratedObjectStore jbc2 = (JBCIntegratedObjectStore) other;
-         return this.cacheManager == jbc2.cacheManager
-                 && this.cacheConfigName.equals(jbc2.cacheConfigName);
-      }
-      return false;
-   }
-
-   protected void internalStart()
-   {      
-      initializeJBossCache();
-      
-      region = jbc.getRegion(regionRootFqn, true);
-      
-      // Try to create an eviction region per ejb
-      // BES 2008/03/12 No, let's handle passivation ourselves
-      // since JBC doesn't properly track the buddy-backup region
-//      EvictionPolicyConfig epc = getEvictionPolicyConfig();
-//      region.setEvictionPolicy(epc);
-
-      // JBCACHE-1136.  There's no reason to have state in an inactive region
-      cleanBeanRegion();
-
-      // Transfer over the state for the region
-      region.registerContextClassLoader(Thread.currentThread().getContextClassLoader());
-      region.activate();
-      
-      // register to listen for cache events
-
-      for (Object listener : jbc.getCacheListeners())
-      {
-         if (listener instanceof ClusteredCacheListener)
-         {
-            this.listener = (ClusteredCacheListener) listener;
-            break;
-         }
-      }
-      
-      if (listener == null)
-      {
-         listener = new ClusteredCacheListener(usingBuddyRepl);
-         jbc.addCacheListener(listener);
-      }
-      
-      regionHandler = new RegionHandlerImpl();
-      listener.addRegionHandler(regionRootFqn, regionHandler);
-      
-      initializeTrackingMaps();
-      
-      super.internalStart();
-   }
-   
-   @SuppressWarnings("unchecked")
-   private void initializeJBossCache()
-   {
-      try
-      {
-         this.jbc = (Cache<Object, T>) cacheManager.getCache(cacheConfigName, true);
-      }
-      catch (CacheException e)
-      {
-         throw convertToRuntimeException(e);
-      }
-      catch (RuntimeException e)
-      {
-         throw e;
-      }
-      catch (Exception e)
-      {
-         throw new RuntimeException("Problem getting cache from CacheManager", e);
-      }
-      
-      if (this.jbc.getCacheStatus() != CacheStatus.STARTED)
-      {
-         if (this.jbc.getCacheStatus() != CacheStatus.CREATED)
-         {
-            this.jbc.create();
-         }
-         
-         this.jbc.start();            
-      }
-      
-      BuddyReplicationConfig brc = jbc.getConfiguration().getBuddyReplicationConfig();
-      this.usingBuddyRepl = brc != null && brc.isEnabled();
-   }
-
-   private void initializeTrackingMaps()
-   {      
-      // First the main tree
-      Node<Object, T> parent = jbc.getNode(regionRootFqn);
-      analyzeRegionContent(parent);
-     
-      // Now any buddy regions
-      if (usingBuddyRepl)
-      {
-         Node<Object, T> bbRoot = jbc.getNode(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN);
-         if (bbRoot != null)
-         {
-            for (Node<Object, T> bbRegion : bbRoot.getChildren())
-            {
-               Node<Object, T> ourPart = bbRegion.getChild(regionRootFqn);
-               if (ourPart != null)
-               {
-                  analyzeRegionContent(ourPart);
-               }
-            }
-         }
-      }
-      
-      // Now, we know any node visits will be from users, so
-      // lets start monitoring them
-      trackVisits = true;
-   }
-
-   private void analyzeRegionContent(Node<Object, T> parent)
-   {
-      for (int i = 0; i < hashBuckets.length; i++)
-      {
-         Node<Object, T> bucket = parent.getChild(hashBuckets[i]);
-         if (bucket == null)
-            continue;
-         Set<Object> childrenNames = bucket.getChildrenNames();
-         for (Object name : childrenNames)
-         {
-            Node<Object, T> child = bucket.getChild(name);
-            if (child == null)
-               continue;
-            T entry = (T) child.get(KEY);
-            if (entry != null)
-            {
-               boolean localEvent = false; // we don't own any of these
-               OwnedItem oi = OwnedItem.getOwnedItem(child.getFqn(), usingBuddyRepl, localEvent);
-               if (entry.isPrePassivated())
-               {
-                  jbc.evict(child.getFqn()); // we'll get a listener event for this
-               }
-               else
-               {
-                  oi.setLastUsed(entry.getLastUsed() == 0 ? System.currentTimeMillis() : entry.getLastUsed());
-                  // Use putIfAbsent so we don't overwrite listener events
-                  inMemoryItems.putIfAbsent(oi, oi);
-               }
-            }
-         }
-      }
-   }
-   
-   protected void internalStop()
-   {
-      super.internalStop();
-      
-      if (jbc != null)
-      {
-         try
-         {
-            // Remove the listener
-            if (listener != null && regionHandler != null && listener.removeRegionHandler(regionRootFqn))
-            {
-               // No more regions registered, so remove the listener
-               jbc.removeCacheListener(listener);
-            }
-            
-            // Null the listener so we have to re-check the cache is we start() again
-            listener = null;
-   
-            // Remove locally. We do this to clean up the persistent store,
-            // which is not affected by the inactivateRegion call below.
-            cleanBeanRegion();
-   
-            if (region != null)
-            {
-               region.deactivate();
-               region.unregisterContextClassLoader();
-   
-               jbc.removeRegion(region.getFqn());
-               region = null;
-            }
-         }
-         finally
-         {
-            cacheManager.releaseCache(cacheConfigName);
-            jbc = null;
-         }         
-      }
-      
-      inMemoryItems.clear();
-      passivatedItems.clear();
-   }
-
-   @Override
-   protected void processExpiration(OwnedItem ownedItem, long lastUse)
-   {
-      if (!isRunning())
-         return;
-      
-      // We only remove items we control
-      if (OwnedItem.LOCAL_OWNER.equals(ownedItem.getOwner()))
-      {
-         getPassivatingCache().remove(ownedItem.getId());
-      }
-      
-      // TODO -- add some mechanism for *eventually* removing items we
-      // don't control, in case their owner died. Eventually meaning
-      // after a day or so.
-   }
-
-   @Override
-   protected void processPassivation(OwnedItem ownedItem, long lastUse)
-   {
-      if (!isRunning())
-         return;
-      
-      synchronized (ownedItem)
-      {
-         String owner = ownedItem.getOwner();
-         if (OwnedItem.LOCAL_OWNER.equals(owner))
-         {
-            getPassivatingCache().passivate(ownedItem.getId());
-         }
-         else
-         {
-            // No callbacks; the item is serialized and under the control of 
-            // another cache. We just want it out of our memory
-            
-            // FIXME race condition here; we might have taken ownership!
-            
-            Fqn<Object> id = null;
-            if (OwnedItem.REMOTE_OWNER.equals(owner))
-            {
-               id = getFqn(ownedItem.getId(), false);
-            }
-            else {
-               id = getBuddyFqn(ownedItem.getId(), ownedItem.getOwner());
-            }
-            
-            if (log.isTraceEnabled())
-               log.trace("evicting remotely owned item " + ownedItem.getId());
-            
-            jbc.evict(id);
-         }
-      }
-   }
-
-   @Override
-   @SuppressWarnings("unchecked")
-   protected CacheableTimestamp<OwnedItem>[] getInMemoryEntries()
-   {     
-      Set<CacheableTimestamp<OwnedItem>> set = new HashSet<CacheableTimestamp<OwnedItem>>();
-      for (OwnedItem ownedItem : inMemoryItems.values())
-      {
-         set.add(new CacheableTimestamp<OwnedItem>(ownedItem, ownedItem.getLastUsed()));
-      }
-      CacheableTimestamp<OwnedItem>[] array = new CacheableTimestamp[set.size()];
-      array = set.toArray(array);
-      Arrays.sort(array);
-      return array;
-   }
-
-   @Override
-   @SuppressWarnings("unchecked")
-   protected CacheableTimestamp<OwnedItem>[] getAllEntries()
-   {     
-      Set<CacheableTimestamp<OwnedItem>> set = new HashSet<CacheableTimestamp<OwnedItem>>();
-      for (OwnedItem ownedItem : inMemoryItems.values())
-      {
-         set.add(new CacheableTimestamp<OwnedItem>(ownedItem, ownedItem.getLastUsed()));
-      }
-      CacheableTimestamp<Object>[] inMemory = new CacheableTimestamp[set.size()];
-      inMemory = set.toArray(inMemory);   
-      
-      set = new HashSet<CacheableTimestamp<OwnedItem>>();
-      for (OwnedItem ownedItem : passivatedItems.values())
-      {
-         set.add(new CacheableTimestamp<OwnedItem>(ownedItem, ownedItem.getLastUsed()));
-      }
-      CacheableTimestamp<OwnedItem>[] passivated = new CacheableTimestamp[set.size()];
-      passivated = set.toArray(passivated);
-
-      CacheableTimestamp<OwnedItem>[] all = new CacheableTimestamp[passivated.length + inMemory.length];
-      System.arraycopy(passivated, 0, all, 0, passivated.length);
-      System.arraycopy(inMemory, 0, all, passivated.length, inMemory.length);
-      Arrays.sort(all);
-      return all;
-   }
-
-   private Fqn<Object> getFqn(Object id, boolean regionRelative)
-   {
-      String beanId = id.toString();
-      int index = getIndexForId(id, beanId);
-
-      if (regionRelative)
-         return new Fqn<Object>( new Object[] {hashBuckets[index], beanId} );
-      else
-         return new Fqn<Object>(regionRootFqn, hashBuckets[index], beanId);
-   }
-   
-   private Fqn<Object> getBuddyFqn(Object id, Object owner)
-   {
-      assert owner != null : "owner cannot be null for a buddy backup Fqn";
-      
-      return new Fqn<Object>(new Object[] { BuddyManager.BUDDY_BACKUP_SUBTREE, owner, FQN_BASE, keyBase, id});
-   }
-   
-   private int getIndexForId(Object id, String stringForm)
-   {
-      int index;
-      if (id instanceof GUID)
-      {
-         index = (id.hashCode()& 0x7FFFFFFF) % hashBuckets.length;
-      }
-      else
-      {
-         index = (stringForm.hashCode()& 0x7FFFFFFF) % hashBuckets.length;
-      }
-      
-      return index;
-   }
-
-   /**
-    * Creates a RuntimeException, but doesn't pass CacheException as the cause
-    * as it is a type that likely doesn't exist on a client.
-    * Instead creates a RuntimeException with the original exception's
-    * stack trace.
-    */
-   private RuntimeException convertToRuntimeException(CacheException e)
-   {
-      RuntimeException re = new RuntimeException(e.getClass().getName() + " " + e.getMessage());
-      re.setStackTrace(e.getStackTrace());
-      return re;
-   }
-
-   private void cleanBeanRegion()
-   {
-      try {
-         // Remove locally.
-         jbc.getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
-         jbc.removeNode(regionRootFqn);
-      }
-      catch (CacheException e)
-      {
-         log.error("Can't clean region " + regionRootFqn + " in the underlying distributed cache", e);
-      }
-   }
-
-   /**
-    * A CacheListener that allows us to get notifications of passivations and
-    * activations and thus notify the cached StatefulBeanContext.
-    */
-   public class RegionHandlerImpl implements ClusteredCacheListener.RegionHandler
-   {
-      public void nodeVisited(OwnedItem oi, NodeVisitedEvent event)
-      {
-         if (trackVisits)
-         {
-            long now = System.currentTimeMillis();
-            oi.setLastUsed(now);
-            OwnedItem existing = inMemoryItems.putIfAbsent(oi, oi);
-            if (existing != null && !existing.isPassivating())
-            {
-               existing.setLastUsed(now);
-               existing.setLocallyOwned(event.isOriginLocal());
-            }               
-         }
-      }
-      
-      public void nodeModified(OwnedItem oi, NodeModifiedEvent event)
-      {
-         @SuppressWarnings("unchecked")
-         T entry = (T) event.getData().get(KEY);
-         if (entry != null)
-         {
-            long lastUsed = entry.getLastUsed();
-            lastUsed = (lastUsed == 0 ? System.currentTimeMillis() : lastUsed);
-            oi.setLastUsed(lastUsed);
-            OwnedItem existing = inMemoryItems.putIfAbsent(oi, oi);
-            if (existing != null && !existing.isPassivating())
-            {
-               existing.setLastUsed(lastUsed);
-               existing.setLocallyOwned(event.isOriginLocal());
-            }               
-            
-            if (log.isTraceEnabled())
-               log.trace(oi + " modified " + (event.isOriginLocal() ? " locally" : "remotely") );
-         }       
-      }
-      
-      @SuppressWarnings("unchecked")
-      public void nodeRemoved(OwnedItem oi, NodeRemovedEvent event)
-      {
-         inMemoryItems.remove(oi);
-         passivatedItems.remove(oi);
-         
-         // Hack! We have access to the data map here; so pass the removed
-         // item to our remove() method via a thread local
-         removedItem.set(event.getData().get(KEY));
-         
-         if (log.isTraceEnabled())
-            log.trace(oi + " removed " + (event.isOriginLocal() ? " locally" : "remotely") );
-      }
-      
-      public void nodeActivated(OwnedItem oi, NodeActivatedEvent event)
-      {
-         @SuppressWarnings("unchecked")
-         T entry = (T) event.getData().get(KEY);
-         if (entry != null)
-         {
-            long lastUsed = entry.getLastUsed();
-            oi.setLastUsed(lastUsed == 0 ? System.currentTimeMillis() : lastUsed);
-            OwnedItem existing = inMemoryItems.putIfAbsent(oi, oi);
-            if (existing == null)
-            {
-               passivatedItems.remove(oi);
-            }
-            else if (!existing.isPassivating())
-            {
-               existing.setLocallyOwned(event.isOriginLocal());
-            }
-            
-            if (log.isTraceEnabled())
-               log.trace(oi + " activated -- " + (event.isOriginLocal() ? " local" : "remote") );
-         }        
-      }
-
-      public void nodePassivated(OwnedItem oi, NodePassivatedEvent event)
-      {
-         @SuppressWarnings("unchecked")
-         T entry = (T) event.getData().get(KEY);
-         if (entry != null)
-         {
-            long lastUsed = entry.getLastUsed();
-            oi.setLastUsed(lastUsed == 0 ? System.currentTimeMillis() : lastUsed);
-            OwnedItem existing = passivatedItems.putIfAbsent(oi, oi);
-            if (existing == null)
-            {
-               inMemoryItems.remove(oi);
-            }
-            
-            if (log.isTraceEnabled())
-               log.trace(oi + " passivated");
-         } 
-      }
-   }
-
-}

Deleted: projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCIntegratedObjectStoreSource.java
===================================================================
--- projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCIntegratedObjectStoreSource.java	2008-03-31 21:42:45 UTC (rev 71526)
+++ projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/JBCIntegratedObjectStoreSource.java	2008-03-31 21:43:07 UTC (rev 71527)
@@ -1,71 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-
-package org.jboss.ejb3.cache.impl.backing.jbc2;
-
-import javax.transaction.TransactionManager;
-
-import org.jboss.cache.CacheManager;
-import org.jboss.ejb3.annotation.CacheConfig;
-import org.jboss.ejb3.cache.api.CacheItem;
-import org.jboss.ejb3.cache.spi.IntegratedObjectStoreSource;
-import org.jboss.ejb3.cache.spi.PassivatingIntegratedObjectStore;
-import org.jboss.ejb3.cache.spi.SerializationGroup;
-import org.jboss.ejb3.cache.spi.SerializationGroupMember;
-import org.jboss.ejb3.cache.spi.SynchronizationCoordinator;
-
-/**
- * {@link IntegratedObjectStoreSource} that provides instances of
- * {@link JBCIntegratedObjectStore}.
- * 
- * @author Brian Stansberry
- */
-public class JBCIntegratedObjectStoreSource<T extends CacheItem> 
-   implements IntegratedObjectStoreSource<T>
-{
-   private CacheManager cacheManager;
-   
-   public PassivatingIntegratedObjectStore<T, SerializationGroup<T>>  createGroupIntegratedObjectStore(String containerName,
-         String cacheConfigName, CacheConfig cacheConfig, TransactionManager transactionManager, SynchronizationCoordinator synchronizationCoordinator)
-   {
-      String nameSuffix = (containerName == null || containerName.length() == 0) ? "" : "-" + containerName;
-      String name = "GroupCache" + nameSuffix;
-      return new JBCIntegratedObjectStore<T, SerializationGroup<T>>(cacheManager, cacheConfigName, cacheConfig, name, true);
-   }
-
-   public PassivatingIntegratedObjectStore<T, SerializationGroupMember<T>>  createIntegratedObjectStore(String containerName, String cacheConfigName,
-         CacheConfig cacheConfig, TransactionManager transactionManager, SynchronizationCoordinator synchronizationCoordinator)
-   {
-      return new JBCIntegratedObjectStore<T, SerializationGroupMember<T>>(cacheManager, cacheConfigName, cacheConfig, containerName, false);
-   }
-
-   public CacheManager getCacheManager()
-   {
-      return cacheManager;
-   }
-
-   public void setCacheManager(CacheManager cacheManager)
-   {
-      this.cacheManager = cacheManager;
-   }
-
-}

Modified: projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/OwnedItem.java
===================================================================
--- projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/OwnedItem.java	2008-03-31 21:42:45 UTC (rev 71526)
+++ projects/ejb3/branches/cluster-dev/cache-jbc2/src/main/java/org/jboss/ejb3/cache/impl/backing/jbc2/OwnedItem.java	2008-03-31 21:43:07 UTC (rev 71527)
@@ -37,7 +37,7 @@
    public static final String LOCAL_OWNER = "local_owner";
    public static final String REMOTE_OWNER = "remote_owner";
    
-   private static final int FQN_SIZE = JBCIntegratedObjectStore.FQN_SIZE;
+   private static final int FQN_SIZE = JBCBackingCacheEntryStore.FQN_SIZE;
    private static final int KEY_INDEX = FQN_SIZE - 1;
    private static final int REGION_INDEX = FQN_SIZE - 2;
    private static final int OWNER_INDEX = 1;

Modified: projects/ejb3/branches/cluster-dev/cache-jbc2/src/test/java/org/jboss/ejb3/test/cache/impl/backing/jbc2/GroupedPassivatingUnitTestCase.java
===================================================================
--- projects/ejb3/branches/cluster-dev/cache-jbc2/src/test/java/org/jboss/ejb3/test/cache/impl/backing/jbc2/GroupedPassivatingUnitTestCase.java	2008-03-31 21:42:45 UTC (rev 71526)
+++ projects/ejb3/branches/cluster-dev/cache-jbc2/src/test/java/org/jboss/ejb3/test/cache/impl/backing/jbc2/GroupedPassivatingUnitTestCase.java	2008-03-31 21:43:07 UTC (rev 71527)
@@ -290,7 +290,9 @@
       }
       finally
       {
-         cluster.getNode1().restoreTCCL();    
+         cluster.getNode1().restoreTCCL();  
+         container1A.stop();
+         container2A.stop();  
          container1B.stop();
          container2B.stop();     
       }

Modified: projects/ejb3/branches/cluster-dev/cache-jbc2/src/test/java/org/jboss/ejb3/test/cache/jbc2/mock/JBC2MockCluster.java
===================================================================
--- projects/ejb3/branches/cluster-dev/cache-jbc2/src/test/java/org/jboss/ejb3/test/cache/jbc2/mock/JBC2MockCluster.java	2008-03-31 21:42:45 UTC (rev 71526)
+++ projects/ejb3/branches/cluster-dev/cache-jbc2/src/test/java/org/jboss/ejb3/test/cache/jbc2/mock/JBC2MockCluster.java	2008-03-31 21:43:07 UTC (rev 71527)
@@ -25,7 +25,7 @@
 import javax.transaction.TransactionManager;
 
 import org.jboss.cache.CacheManagerImpl;
-import org.jboss.ejb3.cache.impl.backing.jbc2.JBCIntegratedObjectStoreSource;
+import org.jboss.ejb3.cache.impl.backing.jbc2.JBCBackingCacheEntryStoreSource;
 import org.jboss.ejb3.test.cache.mock.CacheType;
 import org.jboss.ejb3.test.cache.mock.MockBeanContainer;
 import org.jboss.ejb3.test.cache.mock.MockBeanContext;
@@ -44,8 +44,8 @@
    
    private TransactionManager tm0;
    private TransactionManager tm1;
-   private JBCIntegratedObjectStoreSource<MockBeanContext> storeSource0;
-   private JBCIntegratedObjectStoreSource<MockBeanContext> storeSource1;
+   private JBCBackingCacheEntryStoreSource<MockBeanContext> storeSource0;
+   private JBCBackingCacheEntryStoreSource<MockBeanContext> storeSource1;
    private JBC2MockClusterMember node0;
    private JBC2MockClusterMember node1;
    
@@ -70,14 +70,14 @@
       channelFactory0.setMultiplexerConfig(JGROUPS_STACKS);
       CacheManagerImpl mgr0 = new CacheManagerImpl(JBC_CONFIGS, channelFactory0);
       mgr0.start();
-      storeSource0 = new JBCIntegratedObjectStoreSource<MockBeanContext>();
+      storeSource0 = new JBCBackingCacheEntryStoreSource<MockBeanContext>();
       storeSource0.setCacheManager(mgr0);
       
       JChannelFactory channelFactory1 = new JChannelFactory();
       channelFactory1.setMultiplexerConfig(JGROUPS_STACKS);
       CacheManagerImpl mgr1 = new CacheManagerImpl(JBC_CONFIGS, channelFactory1);
       mgr1.start();
-      storeSource1 = new JBCIntegratedObjectStoreSource<MockBeanContext>();
+      storeSource1 = new JBCBackingCacheEntryStoreSource<MockBeanContext>();
       storeSource1.setCacheManager(mgr1);
       
       node0 = new JBC2MockClusterMember(tm0, useCoordinator, availableTypes, storeSource0);

Modified: projects/ejb3/branches/cluster-dev/cache-jbc2/src/test/java/org/jboss/ejb3/test/cache/jbc2/mock/JBC2MockClusterMember.java
===================================================================
--- projects/ejb3/branches/cluster-dev/cache-jbc2/src/test/java/org/jboss/ejb3/test/cache/jbc2/mock/JBC2MockClusterMember.java	2008-03-31 21:42:45 UTC (rev 71526)
+++ projects/ejb3/branches/cluster-dev/cache-jbc2/src/test/java/org/jboss/ejb3/test/cache/jbc2/mock/JBC2MockClusterMember.java	2008-03-31 21:43:07 UTC (rev 71527)
@@ -29,8 +29,8 @@
 
 import org.jboss.ejb3.annotation.CacheConfig;
 import org.jboss.ejb3.cache.api.CacheFactoryNotRegisteredException;
-import org.jboss.ejb3.cache.impl.backing.jbc2.JBCIntegratedObjectStoreSource;
-import org.jboss.ejb3.cache.spi.IntegratedObjectStoreSource;
+import org.jboss.ejb3.cache.impl.backing.jbc2.JBCBackingCacheEntryStoreSource;
+import org.jboss.ejb3.cache.spi.BackingCacheEntryStoreSource;
 import org.jboss.ejb3.cache.spi.impl.AbstractStatefulCacheFactory;
 import org.jboss.ejb3.test.cache.mock.CacheType;
 import org.jboss.ejb3.test.cache.mock.MockBeanContainer;
@@ -60,12 +60,12 @@
    }
    
    private ClassLoader localClassLoader;
-   private JBCIntegratedObjectStoreSource<MockBeanContext> storeSource;
+   private JBCBackingCacheEntryStoreSource<MockBeanContext> storeSource;
    
    public JBC2MockClusterMember(TransactionManager tm, 
                             boolean useCoordinator, 
                             CacheType cacheType,
-                            JBCIntegratedObjectStoreSource<MockBeanContext> storeSource)
+                            JBCBackingCacheEntryStoreSource<MockBeanContext> storeSource)
    {
       this(tm, useCoordinator, new CacheType[] {cacheType}, storeSource);
    }
@@ -73,7 +73,7 @@
    public JBC2MockClusterMember(TransactionManager tm, 
                             boolean useCoordinator, 
                             CacheType[] cacheTypes,
-                            JBCIntegratedObjectStoreSource<MockBeanContext> storeSource)
+                            JBCBackingCacheEntryStoreSource<MockBeanContext> storeSource)
    {
       this(tm, useCoordinator, cacheTypes, storeSource, DEFAULT_JBC_CONFIG, getAliasMap());
    }
@@ -81,7 +81,7 @@
    public JBC2MockClusterMember(TransactionManager tm, 
          boolean useCoordinator, 
          CacheType[] availableTypes,
-         JBCIntegratedObjectStoreSource<MockBeanContext> storeSource,
+         JBCBackingCacheEntryStoreSource<MockBeanContext> storeSource,
          String defaultJBCConfig,
          Map<String, String> aliases)
    {
@@ -141,7 +141,7 @@
    }
 
    @Override
-   protected IntegratedObjectStoreSource<MockBeanContext> getDistributedStoreSource()
+   protected BackingCacheEntryStoreSource<MockBeanContext> getDistributedStoreSource()
    {
       return storeSource;
    }




More information about the jboss-cvs-commits mailing list