[jboss-cvs] JBossAS SVN: r70757 - in projects/ejb3/branches/cluster-dev/ejb3-cache/src: main/java/org/jboss/ejb3/cache/impl and 8 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Wed Mar 12 00:58:30 EDT 2008


Author: bstansberry at jboss.com
Date: 2008-03-12 00:58:30 -0400 (Wed, 12 Mar 2008)
New Revision: 70757

Added:
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/GroupCreationContext.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/ItemCachePair.java
Removed:
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/GroupIncompatibilityException.java
Modified:
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/Cache.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/GroupAwareTransactionalCache.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/TransactionalCache.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/GroupAwareBackingCacheImpl.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/PassivatingBackingCacheImpl.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/SerializationGroupMemberContainer.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/SimplePassivatingIntegratedObjectStore.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/factory/NonClusteredIntegratedObjectStoreSource.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/BackingCache.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/GroupAwareBackingCache.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/PassivatingBackingCache.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/AbstractPassivatingIntegratedObjectStore.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/SerializationGroupImpl.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/SerializationGroupMember.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/distributed/GroupedPassivatingUnitTestCase.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/distributed/MockJBCIntegratedObjectStore.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/impl/backing/PassivatingBackingCacheImplUnitTestCase.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/integrated/GroupAwareTransactionalCacheUnitTestCase.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/integrated/TransactionalCacheUnitTestCase.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/mock/MockBeanContext.java
   projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/mock/MockStatefulObjectFactory.java
Log:
[EJBTHREE-1026] Cache.create returns key; tighten concurrency mgmt, minor refactors

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/Cache.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/Cache.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/Cache.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -51,7 +51,7 @@
     *                    be used.
     * @return the new <code>T</code> 
     */
-   T create(Class<?> initTypes[], Object initValues[]);
+   Object create(Class<?> initTypes[], Object initValues[]);
 
    /**
     * Get the specified object from cache. This will mark

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/GroupAwareTransactionalCache.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/GroupAwareTransactionalCache.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/GroupAwareTransactionalCache.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -32,7 +32,8 @@
 import org.jboss.ejb3.cache.SerializationGroup;
 import org.jboss.ejb3.cache.spi.BackingCacheEntry;
 import org.jboss.ejb3.cache.spi.GroupAwareBackingCache;
-import org.jboss.ejb3.cache.spi.GroupIncompatibilityException;
+import org.jboss.ejb3.cache.spi.impl.GroupCreationContext;
+import org.jboss.ejb3.cache.spi.impl.ItemCachePair;
 
 /**
  * {@link Cache#isGroupAware Group-aware} version of {@link TransactionalCache}.
@@ -42,9 +43,6 @@
 public class GroupAwareTransactionalCache<C extends CacheItem, T extends BackingCacheEntry<C>> 
    extends TransactionalCache<C, T>
 {
-   @SuppressWarnings("unchecked")
-   private static final ThreadLocal groupCreationContext = new ThreadLocal();
-   
    /** 
     * Another ref to super.delegate. Just saves having to do casts all the time. 
     */
@@ -65,68 +63,51 @@
 
    @Override
    @SuppressWarnings("unchecked")
-   public C create(Class<?>[] initTypes, Object[] initValues)
+   public Object create(Class<?>[] initTypes, Object[] initValues)
    {
       boolean outer = false;
-      List<ItemCachePair> contextPairs = (List<ItemCachePair>) groupCreationContext.get();
+      List<ItemCachePair> contextPairs = GroupCreationContext.getGroupCreationContext();
       if (contextPairs == null)
       {
          contextPairs = new ArrayList<ItemCachePair>();
-         groupCreationContext.set(contextPairs);
+         GroupCreationContext.setGroupCreationContext(contextPairs);
          outer = true;
       }
       
-      C cacheItem = super.create(initTypes, initValues);
+      C cacheItem = super.createInternal(initTypes, initValues);
       
-      contextPairs.add(new ItemCachePair(cacheItem, this));
+      contextPairs.add(new ItemCachePair(cacheItem, groupedCache));
             
       if (outer)
       {
-         groupCreationContext.set(null);
+         GroupCreationContext.setGroupCreationContext(null);
          if (contextPairs.size() > 1)
          {
             SerializationGroup<C> group = null;
             try
             {
-               boolean skipped = false;
-               boolean added = false;
                for (ItemCachePair pair : contextPairs)
                {
-                  if (pair.cache.isGroupAware())
+                  if (group == null)
                   {
-                     if (skipped)
-                        throw new GroupIncompatibilityException("Some caches in nested bean hierarchy are group-aware, some are not");
-                     
-                     if (group == null)
-                     {
-                        group = pair.cache.createGroup();
-                     }
-                     pair.cache.setGroup(pair.item, group);
-                     added = true;
+                     group = pair.getCache().createGroup();
                   }
-                  else if (added)
-                  {
-                     throw new GroupIncompatibilityException("Some caches in nested bean hierarchy are group-aware, some are not");
-                  }
-                  else 
-                  {
-                     skipped = true;
-                  }
+                  pair.getCache().setGroup(pair.getItem(), group);
                }
             }
-            catch (GroupIncompatibilityException e)
+            catch (IllegalStateException e)
             {
                // Clean up
                for (ItemCachePair pair : contextPairs)
                {
-                  pair.cache.remove(pair.item.getId());
+                  pair.getCache().remove(pair.getItem().getId());
                }
-               throw new RuntimeException("Failed to create SerializationGroup for nested bean hierarchy", e);
+               throw e;
             }
          }
       }
       
-      return cacheItem;
+      return cacheItem.getId();
    }
 
    @Override
@@ -135,32 +116,10 @@
       return true;
    }
 
-   private void setGroup(C obj, SerializationGroup<C> group) throws GroupIncompatibilityException
-   {
-      groupedCache.setGroup(obj, group);
-   }
-   
-   private SerializationGroup<C> createGroup() throws GroupIncompatibilityException
-   {
-      return groupedCache.createGroup();
-   }
-
    @Override
    public SerializationGroup<C> getGroup(C obj)
    {
       return groupedCache.getGroup(obj);
    }
-   
-   private class ItemCachePair
-   {
-      private final C item;
-      private final GroupAwareTransactionalCache<C, T> cache;
-      
-      ItemCachePair(C item, GroupAwareTransactionalCache<C, T> cache)
-      {
-         this.item = item;
-         this.cache = cache;
-      }
-   }
 
 }

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/TransactionalCache.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/TransactionalCache.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/TransactionalCache.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -21,10 +21,9 @@
  */
 package org.jboss.ejb3.cache.impl;
 
-import java.util.HashMap;
-import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.ReentrantLock;
 
 import javax.ejb.NoSuchEJBException;
 import javax.transaction.RollbackException;
@@ -64,23 +63,21 @@
    private final BackingCache<C, T> delegate;
    
    /** Cache of items that are in use by a tx or non-transactional invocation */
-   private final Map<Object, Entry> inUseCache;
+   private final ConcurrentMap<Object, Entry> inUseCache;
    /** Map of Synchronizations to release items in use by a tx*/
    private final ConcurrentMap<Object, CacheReleaseSynchronization<C, T>> synchronizations;
    /** Our transaction manager */
    private final TransactionManager tm;
    
-   private enum State { INITIALIZED, IN_USE, FINISHED };
    private class Entry
    {
-      long lastUsed;
       C obj;
-      State state;
+      ReentrantLock lock;
+      int getCount;
       
       Entry()
       {
-         this.lastUsed = System.currentTimeMillis();
-         this.state = State.INITIALIZED;
+         this.lock = new ReentrantLock();
       }
    }
    
@@ -130,82 +127,110 @@
       this.delegate = delegate;
       this.tm = tm;
       
-      this.inUseCache = new HashMap<Object, Entry>();
+      this.inUseCache = new ConcurrentHashMap<Object, Entry>();
       this.synchronizations = new ConcurrentHashMap<Object, CacheReleaseSynchronization<C, T>>();
    }
 
-   public C create(Class<?>[] initTypes, Object[] initValues)
+   public Object create(Class<?>[] initTypes, Object[] initValues)
    {
-      T backingEntry = delegate.create(initTypes, initValues);
-      C obj = backingEntry.getUnderlyingItem();
-      synchronized (inUseCache)
-      {
-         // Create an entry, but do not store a ref to obj in the entry.
-         // This will ensure get() goes to the backingCache to get the
-         // missing obj, giving the backingCache an opportunity to mark
-         // the BackingCacheEntry as being in use
-         registerEntry(obj, false);
-      }
-      return obj;
+     return createInternal(initTypes, initValues).getId();
    }
-
-   private Entry registerEntry(C obj, boolean storeRef)
+   
+   protected C createInternal(Class<?>[] initTypes, Object[] initValues)
    {
       Entry entry = new Entry(); 
-      if (storeRef)
-         entry.obj = obj;
-      inUseCache.put(obj.getId(), entry);      
-      registerSynchronization(obj);
-      return entry;
+      entry.lock.lock();
+      try
+      {
+         T backingEntry = delegate.create(initTypes, initValues);
+         C obj = backingEntry.getUnderlyingItem();
+         
+         // Note we deliberately don't assign obj to entry -- we want
+         // a call to get() to get it from delegate so delegate can lock it
+         
+         Entry old = inUseCache.putIfAbsent(obj.getId(), entry);
+         if (old != null)
+            throw new IllegalStateException("entry for " + obj.getId() + " already exists");
+         registerSynchronization(obj);
+         return obj;
+      }
+      finally
+      {
+         entry.lock.unlock();
+      }
    }
    
    public C get(Object key) throws NoSuchEJBException
    {
-      Entry entry = null;
-      
-      // Yuck. This is a bottleneck
-      synchronized (inUseCache)
+      Entry entry = inUseCache.get(key);
+      if (entry == null)
       {
-         entry = inUseCache.get(key);
-         if(entry == null)
+         entry = new Entry(); 
+         entry.lock.lock();
+         Entry old = inUseCache.putIfAbsent(key, entry);
+         if (old != null)
          {
-            T backingEntry = delegate.get(key);
-            C obj = backingEntry.getUnderlyingItem();
-            entry = registerEntry(obj, true); 
+            // We've got a race for this key.  See if we won
+            if (old.lock.tryLock())
+            {
+               // We won. Just use "old"; let the one we just created get gc'd
+               entry = old;
+            }
+            else
+               throw new IllegalStateException(key + " is already in use");
          }
       }
-      
-      if (entry.obj == null)
-         entry.obj = delegate.get(key).getUnderlyingItem();
-      
-      validateTransaction(entry.obj);
-      if(entry.state == State.IN_USE)
-         throw new IllegalStateException("entry " + key + " is already in use");
-      entry.state = State.IN_USE;
-      entry.lastUsed = System.currentTimeMillis();
-      return entry.obj;
+      else
+      {
+         if (!entry.lock.tryLock())
+            throw new IllegalStateException(key + " is already in use");
+      }
+         
+      try
+      {
+         if (entry.obj == null)
+            entry.obj = delegate.get(key).getUnderlyingItem();
+         
+         validateTransaction(entry.obj);
+         entry.getCount++;
+         return entry.obj;
+      }
+      catch (RuntimeException e)
+      {
+         entry.lock.unlock();
+         throw e;
+      }
    }
    
    public void finished(C obj)
    {
-      Entry entry = null;
-      synchronized (inUseCache)
-      {
-         entry = inUseCache.get(obj.getId());
-      }
+      Entry entry = inUseCache.get(obj.getId());
       if (entry == null)
          throw new IllegalStateException("No entry for " + obj.getId());
-      if(entry.state != State.IN_USE)
-         throw new IllegalStateException("entry " + obj.getId() + " is not in operation");
-      entry.state = State.FINISHED;
-      entry.lastUsed = System.currentTimeMillis();
       
-      // If there is no tx associated with this object, we can release it
-      if (synchronizations.get(obj.getId()) == null)
+      if(!entry.lock.isHeldByCurrentThread())
       {
-         release(obj);
+         throw new IllegalStateException("entry " + obj.getId() + 
+               " lock is not held by " + Thread.currentThread().getName());
       }
       
+      try
+      {
+         if (entry.getCount < 1)
+            throw new IllegalStateException("Unmatched calls to finished");         
+         entry.getCount--;
+         
+         // If there is no tx associated with this object, we can release it
+         if (entry.getCount == 0 && synchronizations.get(obj.getId()) == null)
+         {
+            release(obj);
+         }
+      }
+      finally
+      {
+         entry.lock.unlock();
+      }
+      
    }
 
    public void remove(Object key)
@@ -248,19 +273,34 @@
    private void release(C obj)
    {
       Object key = obj.getId();
-      synchronized (inUseCache)
+      Entry entry = inUseCache.get(key);
+      if (entry == null)
       {
-         Entry entry = inUseCache.get(key);
-         if (entry == null)
-         {
-            // FIXME is this correct?
-            return;
-         }
-         if(entry.state == State.IN_USE)
-            throw new IllegalStateException("entry " + key + " is not finished");
+         // TODO is this correct?
+         return;
+      }
+      
+      // We either came here from finished(), and we thus hold the lock,
+      // or we came from our synchronization's beforeCompletion(), and
+      // validateTransaction() will promptly force any other thread
+      // that got the lock to give it up. So, we acquire the lock and are
+      // willing to wait for it
+      try
+      {
+         entry.lock.lockInterruptibly();
+         // For sure we now control this key -- tell delegate to release
+         delegate.release(obj.getId());
+         
+         // Now remove the entry
          inUseCache.remove(key);
       }
-      delegate.release(obj.getId());
+      catch (InterruptedException ie)
+      {
+         throw new RuntimeException("Interrupted waiting to lock " + key);
+      }
+
+      // Note we don't release the lock!  If anyone has a ref to entry,
+      // it's now stale and they should fail to acquire the lock
    }
    
    private void registerSynchronization(C cacheItem)
@@ -320,5 +360,9 @@
          }
                
       }
+      else
+      {
+         registerSynchronization(cacheItem);
+      }
    }   
 }

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/GroupAwareBackingCacheImpl.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/GroupAwareBackingCacheImpl.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/GroupAwareBackingCacheImpl.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -26,7 +26,6 @@
 import org.jboss.ejb3.cache.CacheItem;
 import org.jboss.ejb3.cache.SerializationGroup;
 import org.jboss.ejb3.cache.spi.GroupAwareBackingCache;
-import org.jboss.ejb3.cache.spi.GroupIncompatibilityException;
 import org.jboss.ejb3.cache.spi.PassivatingBackingCache;
 import org.jboss.ejb3.cache.spi.impl.SerializationGroupImpl;
 import org.jboss.ejb3.cache.spi.impl.SerializationGroupMember;
@@ -67,7 +66,7 @@
       return groupCache.create(null, null);
    }
 
-   public void setGroup(C obj, SerializationGroup<C> group) throws GroupIncompatibilityException
+   public void setGroup(C obj, SerializationGroup<C> group)
    {     
       Object key = obj.getId();
       SerializationGroupMember<C> entry = peek(key);
@@ -77,7 +76,7 @@
       // Validate we share a common groupCache with the group
       SerializationGroupImpl<C> groupImpl = (SerializationGroupImpl<C>) group;
       if (groupCache != groupImpl.getGroupCache())
-         throw new GroupIncompatibilityException(obj + " and " + groupImpl + " use different group caches");
+         throw new IllegalStateException(obj + " and " + groupImpl + " use different group caches");
       
       entry.setGroup(groupImpl);
       entry.getGroup().addMember(entry);
@@ -89,7 +88,24 @@
       try
       {
          SerializationGroupMember<C> entry = peek(key);
-         return entry.getGroup();
+         synchronized (entry)
+         {
+            SerializationGroup<C> group = entry.getGroup();
+            if (group == null && entry.getGroupId() != null)
+            {
+               // Need to use get() to postActivate it
+               entry = get(key);
+               try
+               {
+                  group = entry.getGroup();
+               }
+               finally
+               {
+                  release(key);
+               }
+            }
+            return group;
+         }
       }
       catch (NoSuchEJBException nsee)
       {

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/PassivatingBackingCacheImpl.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/PassivatingBackingCacheImpl.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/PassivatingBackingCacheImpl.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -69,27 +69,19 @@
    public T create(Class<?>[] initTypes, Object[] initValues)
    {
       T obj = factory.create(initTypes, initValues);
-//      obj.setInUse(true);
-      synchronized (store)
-      {
-         store.insert(obj);
-      }
+      store.insert(obj);
       return obj;
    }
 
    public T get(Object key) throws NoSuchEJBException
    {
-      synchronized (store)
+      T entry = store.get(key);
+      
+      if(entry == null)
+         throw new NoSuchEJBException(String.valueOf(key));
+      
+      synchronized (entry)
       {
-         T entry = store.get(key);
-         if(entry == null)
-            throw new NoSuchEJBException(String.valueOf(key));
-         
-         if(entry.isInUse())
-         {
-            throw new IllegalStateException("entry " + entry + " is in use");
-         }
-         
          if (isClustered())
          {
             passivationManager.postReplicate(entry);
@@ -105,13 +97,14 @@
    public void passivate(Object key)
    {
       log.trace("passivate " + key);
-      synchronized (store)
-      {
-         T entry = store.get(key);
+      
+      T entry = store.get(key);
+      
+      if(entry == null)
+         throw new IllegalArgumentException("entry " + key + " not found in cache " + this);
          
-         if(entry == null)
-            throw new IllegalArgumentException("entry " + key + " not found in cache " + this);
-         
+      synchronized (entry)
+      {
          if(entry.isInUse())
          {
             throw new IllegalStateException("entry " + entry + " is in use");
@@ -125,24 +118,20 @@
    
    public T peek(Object key) throws NoSuchEJBException
    {
-      synchronized (store)
-      {
-         T entry = store.get(key);
-         if(entry == null)
-            throw new NoSuchEJBException(String.valueOf(key));         
-         return entry;
-      }
+      T entry = store.get(key);
+      if(entry == null)
+         throw new NoSuchEJBException(String.valueOf(key));         
+      return entry;
    }
 
    public T release(Object key)
    {      
-      synchronized (store)
+      T entry = store.get(key);
+      if(entry == null)
+         throw new IllegalStateException("object " + key + " not from this cache");
+      
+      synchronized (entry)
       {
-         T entry = store.get(key);
-         if(entry == null)
-            throw new IllegalStateException("object " + key + " not from this cache");
-         if(!entry.isInUse())
-            throw new IllegalStateException("entry " + entry + " is not in use");
          entry.setInUse(false);
          
          if (entry.isModified())
@@ -160,15 +149,17 @@
    
    public void remove(Object key)
    {
-      T entry;
-      synchronized (store)
+      T entry = store.remove(key);
+      if (entry != null)
       {
-         entry = store.remove(key);
-         if(entry != null && entry.isInUse())
-            entry.setInUse(false);
+         synchronized (entry)
+         {
+            if(entry.isInUse())
+               entry.setInUse(false);
+            factory.destroy(entry);
+         }
       }
-      if(entry != null)
-         factory.destroy(entry);
+         
    }
    
    public void start()

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/SerializationGroupMemberContainer.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/SerializationGroupMemberContainer.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/SerializationGroupMemberContainer.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -95,24 +95,40 @@
    {
       log.trace("post activate " + entry);
       
-      // Restore the entry's ref to the group and object
-      SerializationGroupImpl<C> group = entry.getGroup();
-      if(group == null && entry.getGroupId() != null)
+      boolean groupOK = false;
+      while (!groupOK)
       {
-         // TODO: peek or get?
-         // BES 2007/10/06 I think peek is better; no
-         // sense marking the group as in-use and then having
-         // to release it or something
-         group = groupCache.peek(entry.getGroupId());
-         entry.setGroup(group);
-      }
-      
-      if(group != null)
-      {
-         entry.setUnderlyingItem(group.getMemberObject(entry.getId()));
+         // Restore the entry's ref to the group and object
+         SerializationGroupImpl<C> group = entry.getGroup();
+         if(group == null && entry.getGroupId() != null)
+         {
+            // TODO: peek or get?
+            // BES 2007/10/06 I think peek is better; no
+            // sense marking the group as in-use and then having
+            // to release it or something
+//            group = groupCache.peek(entry.getGroupId());
+            group = groupCache.get(entry.getGroupId());
+         }
          
-         // Notify the group that this entry is active
-         group.addActive(entry);
+         if(group != null)
+         {
+            synchronized (group)
+            {
+               if (!group.isInvalid())
+               {
+                  entry.setGroup(group);
+                  entry.setUnderlyingItem(group.getMemberObject(entry.getId()));                  
+                  // Notify the group that this entry is active
+                  group.addActive(entry);
+                  groupOK = true;
+               }
+               // else groupOK == false and we loop again
+            }
+         }
+         else
+         {
+            groupOK = true;
+         }
       }
       
       // Invoke callbacks on the underlying object
@@ -158,6 +174,9 @@
                {
                   // Go ahead and do the real passivation.
                   groupCache.passivate(group.getId());
+                  // Mark the group as invalid so if another thread is
+                  // blocking on the above sync lock, when they enter
+                  // we realize they have a ref to an out-of-date group
                   group.setInvalid(true);
                }         
                // else {
@@ -169,7 +188,7 @@
                
                // This call didn't come through entry.prePassivate() (which nulls
                // group and obj) so we have to do it ourselves. Otherwise
-               // when this call returns, delegate will serialize the entry
+               // when this call returns, delegate may serialize the entry
                // with a ref to group and obj.            
                entry.setGroup(null);
                entry.setUnderlyingItem(null);
@@ -194,28 +213,30 @@
       SerializationGroupImpl<C> group = entry.getGroup();
       if(group != null)
       {
-         // Remove ourself from group's active list so we don't get
-         // called again via entry.prePassivate()            
-         group.removeActive(entry.getId());
-         
-         try
+         synchronized (group)
          {
-            if (group.getInUseCount() == 0)
+            // Remove ourself from group's active list so we don't get
+            // called again via entry.prePassivate()            
+            group.removeActive(entry.getId());
+            
+            try
             {
-//               group.preReplicate();
-               groupCache.release(group);
+               if (group.getInUseCount() == 0)
+               {
+                  groupCache.release(group);
+               }
             }
+            finally
+            {
+               // Here we differ from prePassivate!!
+               // Restore the entry as "active" so it can get
+               // passivation callbacks
+               group.addActive(entry);
+            }
+         
+//            entry.setGroup(null);
+//            entry.setUnderlyingItem(null);
          }
-         finally
-         {
-            // Here we differ from prePassivate!!
-            // Restore the entry as "active" so it can get
-            // passivation callbacks
-            group.addActive(entry);
-         }
-         
-//         entry.setGroup(null);
-//         entry.setUnderlyingItem(null);
       }
    }
    
@@ -223,24 +244,41 @@
    {
       log.trace("postreplicate " + entry);
       
-      // Restore the entry's ref to the group and object
-      SerializationGroupImpl<C> group = entry.getGroup();
-      if(group == null && entry.getGroupId() != null)
+      boolean groupOK = false;
+      while (!groupOK)
       {
-         // TODO: peek or get?
-         // BES 2007/10/06 peek is better; you'll get multiple calls to
-         // this as different members are accessed, and the cache
-         // will throw an ISE if we use get() since we're already in use
-         group = groupCache.peek(entry.getGroupId());
-         entry.setGroup(group);
-      }
-      
-      if(group != null)
-      {
-         entry.setUnderlyingItem(group.getMemberObject(entry.getId()));
+         // Restore the entry's ref to the group and object
+         SerializationGroupImpl<C> group = entry.getGroup();
+         if(group == null && entry.getGroupId() != null)
+         {
+            // TODO: peek or get?
+            // BES 2007/10/06 peek is better; you'll get multiple calls to
+            // this as different members are accessed, and the cache
+            // will throw an ISE if we use get() since we're already in use
+//            group = groupCache.peek(entry.getGroupId());
+            group = groupCache.get(entry.getGroupId());
+         }
          
-         // Notify the group that this entry is active
-         group.addActive(entry);
+         if(group != null)
+         {
+            synchronized (group)
+            {
+               if (!group.isInvalid())
+               {
+                  entry.setGroup(group);
+                  entry.setUnderlyingItem(group.getMemberObject(entry.getId()));
+            
+                  // Notify the group that this entry is active
+                  group.addActive(entry);
+                  groupOK = true;
+               }
+               // else groupOK == false and we loop again
+            }
+         }
+         else
+         {
+            groupOK = true;
+         }
       }
       
       // Invoke callbacks on the underlying object

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/SimplePassivatingIntegratedObjectStore.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/SimplePassivatingIntegratedObjectStore.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/backing/SimplePassivatingIntegratedObjectStore.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -58,15 +58,19 @@
     */
    public SimplePassivatingIntegratedObjectStore(ObjectStore<T> store, 
                                                  CacheConfig config,
-                                                 String name)
+                                                 String name,
+                                                 boolean forGroups)
    {
-      super(config, name);
+      super(config, name, forGroups);
       
       assert store != null : "store is null";
       
       this.store = store;
       this.cache = new HashMap<Object, T>();
-      this.passivatedEntries = new HashMap<Object, Long>();
+      if (!forGroups)
+      {
+         this.passivatedEntries = new HashMap<Object, Long>();
+      }
    }
    
    public boolean isClustered()
@@ -85,7 +89,10 @@
             if(entry != null)
             {
                cache.put(key, entry);
-               passivatedEntries.remove(key);
+               if (!isForGroups())
+               {
+                  passivatedEntries.remove(key);
+               }
             }
          }
          return entry;
@@ -97,7 +104,8 @@
       Object key = entry.getId();
       synchronized (cache)
       {
-         if (cache.containsKey(key) || passivatedEntries.containsKey(key))
+         if (cache.containsKey(key) 
+               || (!isForGroups() && passivatedEntries.containsKey(key)))
          {
             throw new IllegalStateException(key + " is already in store");
          }
@@ -110,7 +118,8 @@
       Object key = entry.getId();
       synchronized (cache)
       {
-         if (!cache.containsKey(key) && !passivatedEntries.containsKey(key))
+         if (!cache.containsKey(key) && 
+               (isForGroups() || !passivatedEntries.containsKey(key)))
          {
             throw new IllegalStateException(key + " is not managed by this store");
          }
@@ -125,7 +134,10 @@
       {
          Object key = entry.getId();
          store.store(entry);  
-         passivatedEntries.put(key, new Long(entry.getLastUsed()));
+         if (!isForGroups())
+         {
+            passivatedEntries.put(key, new Long(entry.getLastUsed()));
+         }
          cache.remove(key);
       }
    }
@@ -162,11 +174,11 @@
    @Override
    protected void runExpiration()
    {
-      if (getExpirationTimeSeconds() > 0)
+      if (!isForGroups() && getExpirationTimeSeconds() > 0)
       {
          long now = System.currentTimeMillis();
          long minRemovalUse = now - (getExpirationTimeSeconds() * 1000);                     
-         for (CacheableTimestamp ts : getPassivatedEntries())
+         for (CacheableTimestamp ts : getAllEntries())
          {
             try
             {
@@ -187,30 +199,28 @@
    @Override
    protected void runPassivation()
    {
-      if (getIdleTimeSeconds() > 0)
+      if (!isForGroups() 
+            && (getMaxSize() > 0 || getIdleTimeSeconds() > 0))
       {
          long now = System.currentTimeMillis();
-         long minPassUse = now - (getIdleTimeSeconds() * 1000);
+         long minPassUse = (getIdleTimeSeconds() > 0 ? now - (getIdleTimeSeconds() * 1000) : 0);
          
-         // Scan the in-memory entries for passivation or removal
          SortedSet<CacheableTimestamp> timestamps = getInMemoryEntries();
-         int overCount = timestamps.size() - getMaxSize();
+         int overCount = (getMaxSize() > 0 ? timestamps.size() - getMaxSize() : 0);
          for (CacheableTimestamp ts : timestamps)
          {
             try
             {
-               long lastUsed = ts.getLastUsed();
-               if (overCount > 0 || minPassUse >= lastUsed)
+               if (overCount > 0 || minPassUse >= ts.getLastUsed())
                {
-                  synchronized (cache)               
-                  {
-                     T entry = cache.get(ts.getId());
-                     if (entry == null || entry.isInUse())
-                        continue;
-                  }
+                  log.trace("attempting to passivate " + ts.getId());
                   getPassivatingCache().passivate(ts.getId());
                   overCount--;
                }
+               else
+               {
+                  break;
+               }
             }
             catch (IllegalStateException ise)
             {
@@ -218,8 +228,7 @@
                log.trace("skipping in-use entry " + ts.getId(), ise);
             }
          }
-      }
-      
+      }      
    }
 
    private SortedSet<CacheableTimestamp> getInMemoryEntries()
@@ -235,11 +244,27 @@
    private SortedSet<CacheableTimestamp> getPassivatedEntries()
    {
       SortedSet<CacheableTimestamp> set = new TreeSet<CacheableTimestamp>();
-      for (Map.Entry<Object, Long> entry : passivatedEntries.entrySet())
+      if (!isForGroups())
       {
-         set.add(new CacheableTimestamp(entry.getKey(), entry.getValue().longValue()));
+         for (Map.Entry<Object, Long> entry : passivatedEntries.entrySet())
+         {
+            set.add(new CacheableTimestamp(entry.getKey(), entry.getValue().longValue()));
+         } 
       }
       return set;
    }
+   
+   private SortedSet<CacheableTimestamp> getAllEntries()
+   {
+      SortedSet<CacheableTimestamp> set = getInMemoryEntries();
+      if (!isForGroups())
+      {
+         for (Map.Entry<Object, Long> entry : passivatedEntries.entrySet())
+         {
+            set.add(new CacheableTimestamp(entry.getKey(), entry.getValue().longValue()));
+         } 
+      }
+      return set;
+   }
 
 }

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/factory/NonClusteredIntegratedObjectStoreSource.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/factory/NonClusteredIntegratedObjectStoreSource.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/impl/factory/NonClusteredIntegratedObjectStoreSource.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -68,9 +68,10 @@
       objectStore.setStorageDirectory(getFullGroupDirectoryName(containerName));
       objectStore.setSubdirectoryCount(subdirectoryCount);
       
-      String storeName = "StdGroupStore-"+cacheConfig.name();
+      String storeNameSuffix = (cacheConfig.name().length() == 0) ? "" : "-" + cacheConfig;
+      String storeName = "StdGroupStore" + storeNameSuffix;
       SimplePassivatingIntegratedObjectStore<T, SerializationGroupImpl<T>> store = 
-         new SimplePassivatingIntegratedObjectStore<T, SerializationGroupImpl<T>>(objectStore, cacheConfig, storeName);
+         new SimplePassivatingIntegratedObjectStore<T, SerializationGroupImpl<T>>(objectStore, cacheConfig, storeName, true);
       
       return store;
    }
@@ -83,7 +84,7 @@
       objectStore.setSubdirectoryCount(subdirectoryCount);
       
       SimplePassivatingIntegratedObjectStore<T, SerializationGroupMember<T>> store = 
-         new SimplePassivatingIntegratedObjectStore<T, SerializationGroupMember<T>>(objectStore, cacheConfig, containerName);
+         new SimplePassivatingIntegratedObjectStore<T, SerializationGroupMember<T>>(objectStore, cacheConfig, containerName, false);
       
       return store;
    }

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/BackingCache.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/BackingCache.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/BackingCache.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -31,13 +31,26 @@
  * An internal cache to which an external-facing {@link Cache} delegates, either
  * directly or indirectly.
  * <p>
- * The key distinction between a BackingCache and the external-facing Cache is
- * that a Cache directly handles external classes that implement the
+ * There are two key distinctions between a BackingCache and the external-facing 
+ * Cache:
+ * <ol>
+ * <li>
+ * A Cache directly handles external classes that implement the
  * limited {@link CacheItem} interface. CacheItem is deliberately limited to
  * avoid placing a implementation burden on external classes. A BackingCache 
  * works with instances of the more expressive internal interface 
  * {@link BackingCacheEntry}, and thus can directly implement more complex 
  * functionality.
+ * </li>
+ * <li>
+ * A BackingCache does not attempt to control concurrent access to its
+ * cached {@link BackingCacheEntry} instances, beyond the simple act of
+ * {@link BackingCacheEntry#setInUse(boolean) marking the entries as being
+ * in or out of use}.  It assumes the external-facing Cache is preventing
+ * concurrent access to a given entry and is properly coordinating calls
+ * to the backing cache.
+ * </li>
+ * </ol>
  * </p>
  * 
  * @author Brian Stansberry
@@ -53,7 +66,7 @@
     * 
     * @param initTypes   the types of any <code>initValues</code>. 
     *                    May be <code>null</code>.
-    * @param initValues  any paramaters to pass to <code>T</code>'s constructor.
+    * @param initValues  any parameters to pass to <code>T</code>'s constructor.
     *                    May be null, in which case a default constructor will
     *                    be used.
     * @return the new <code>T</code> 
@@ -71,7 +84,8 @@
    T get(Object key) throws NoSuchEJBException;
    
    /**
-    * Peek at an object which might be in use.
+    * Peek at an object which might be in use. Does not change the status
+    * of the item in terms of whether it is in use.
     * 
     * @param key    the identifier of the object
     * @return       the object

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/GroupAwareBackingCache.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/GroupAwareBackingCache.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/GroupAwareBackingCache.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -51,10 +51,10 @@
     * @param obj
     * @param group
     * 
-    * @throws GroupIncompatibilityException if the group's cache is incompatible
+    * @throws IllegalStateException if the group's cache is incompatible
     *                                       with ourself.
     */
-   void setGroup(C obj, SerializationGroup<C> group) throws GroupIncompatibilityException;
+   void setGroup(C obj, SerializationGroup<C> group);
 
    /**
     * Gets the group the given object is a member of

Deleted: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/GroupIncompatibilityException.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/GroupIncompatibilityException.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/GroupIncompatibilityException.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -1,45 +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.spi;
-
-/**
- * @author Brian Stansberry
- *
- */
-public class GroupIncompatibilityException extends Exception
-{
-
-    /** The serialVersionUID */
-   private static final long serialVersionUID = 8202720497027287887L;
-
-   /**
-    * Create a new IncompatibleGroupException.
-    * 
-    * @param message
-    */
-   public GroupIncompatibilityException(String message)
-   {
-      super(message);
-   }
-
-}

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/PassivatingBackingCache.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/PassivatingBackingCache.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/PassivatingBackingCache.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -22,29 +22,33 @@
 package org.jboss.ejb3.cache.spi;
 
 import org.jboss.ejb3.cache.CacheItem;
-import org.jboss.ejb3.cache.spi.impl.SerializationGroupImpl;
+import org.jboss.ejb3.cache.SerializationGroup;
 
 
 /**
  * A {@link BackingCache} which passivates unused objects.
+ * <p>
+ * A PassivatingBackingCache is linked to an ObjectStore to store the
+ * passivated object and to a PassivationManager to manage lifecycle
+ * callbacks on the object.
+ * </p>
  * 
- * A PassivatingCache is linked to an ObjectStore to store the
- * passivated object and a PassivationManager to manage lifecycle
- * callbacks on the object.
- *
  * @author <a href="mailto:carlo.dewolf at jboss.com">Carlo de Wolf</a>
+ * @author Brian Stansberry
+ * 
  * @version $Revision: 65977 $
  */
 public interface PassivatingBackingCache<C extends CacheItem, T extends BackingCacheEntry<C>>
    extends BackingCache<C, T>
 {
    /**
-    * Force passivation of an object. The object must not be in use.
+    * Force passivation of an object. The object must not be 
+    * {@link BackingCacheEntry#isInUse() in use}.
     * 
     * @param key    the identifier of the object
     * 
     * @throws IllegalStateException if the object, or another object in the 
-    *                            same {@link SerializationGroupImpl} as the object, 
+    *                            same {@link SerializationGroup} as the object, 
     *                            is in use. 
     */
    void passivate(Object key);

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/AbstractPassivatingIntegratedObjectStore.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/AbstractPassivatingIntegratedObjectStore.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/AbstractPassivatingIntegratedObjectStore.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -46,6 +46,7 @@
     * evict an entry.
     */
    private PassivatingBackingCache<C, T> owningCache;   
+   private boolean forGroups;
    private int interval;
    private int maxSize;
    private long idleTimeSeconds;
@@ -58,7 +59,8 @@
     * Create a new AbstractPassivatingIntegratedObjectStore.
     */
    protected AbstractPassivatingIntegratedObjectStore(CacheConfig config,
-                                                   String name)
+                                                      String name,
+                                                      boolean forGroups)
    {
       assert config != null : "config is null";
       assert name != null : "name is null";
@@ -67,6 +69,7 @@
       this.expirationTimeSeconds = config.removalTimeoutSeconds();
       this.maxSize = config.maxSize();
       this.name = name;
+      this.forGroups = forGroups;
    }
 
    // ---------------------------------------------------------------- Abstract
@@ -79,7 +82,7 @@
 
    public void start()
    {
-      if (interval > 0)
+      if (!forGroups && interval > 0)
       {
          if (sessionTimeoutRunner == null)
          {
@@ -133,6 +136,10 @@
    
    public void processPassivationExpiration()
    {
+      // Group passivation/expiration is a function of its members
+      if (forGroups)
+         return;
+      
       if (!stopped)
       {
          try
@@ -188,4 +195,26 @@
       this.expirationTimeSeconds = timeout;
    }
 
+   public boolean isForGroups()
+   {
+      return forGroups;
+   }
+
+   public String getName()
+   {
+      return name;
+   }
+
+   public boolean isStopped()
+   {
+      return stopped;
+   }
+
+   public void setMaxSize(int maxSize)
+   {
+      this.maxSize = maxSize;
+   }
+   
+   
+
 }

Added: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/GroupCreationContext.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/GroupCreationContext.java	                        (rev 0)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/GroupCreationContext.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -0,0 +1,51 @@
+/*
+ * 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.spi.impl;
+
+import java.util.List;
+
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public class GroupCreationContext
+{
+   @SuppressWarnings("unchecked")
+   private static final ThreadLocal<List<ItemCachePair>> groupCreationContext = new ThreadLocal<List<ItemCachePair>>();
+   
+   /**
+    * Prevent instantiation. 
+    */
+   private GroupCreationContext() {}
+
+   public static List<ItemCachePair> getGroupCreationContext()
+   {
+      return groupCreationContext.get();
+   }
+   
+   public static void setGroupCreationContext(List<ItemCachePair> context)
+   {
+      groupCreationContext.set(context);
+   }
+}

Added: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/ItemCachePair.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/ItemCachePair.java	                        (rev 0)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/ItemCachePair.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -0,0 +1,58 @@
+/*
+ * 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.spi.impl;
+
+import org.jboss.ejb3.cache.Cache;
+import org.jboss.ejb3.cache.CacheItem;
+import org.jboss.ejb3.cache.spi.GroupAwareBackingCache;
+
+/**
+ * Simple data container for a {@link CacheItem} and the
+ * {@link Cache} that created it.
+ * 
+ * @author Brian Stansberry
+ *
+ */
+public class ItemCachePair
+{
+   private final CacheItem item;
+   private final GroupAwareBackingCache cache;
+   
+   public ItemCachePair(CacheItem item, GroupAwareBackingCache cache)
+   {
+      this.item = item;
+      this.cache = cache;
+   }
+
+   public CacheItem getItem()
+   {
+      return item;
+   }
+
+   public GroupAwareBackingCache getCache()
+   {
+      return cache;
+   }
+   
+   
+}
\ No newline at end of file

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/SerializationGroupImpl.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/SerializationGroupImpl.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/SerializationGroupImpl.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -182,7 +182,7 @@
    {
       for(SerializationGroupMember<T> member : active.values())
       {
-         member.prePassivate();
+         member.releaseReferences();
       }
       active.clear();
    }
@@ -202,7 +202,7 @@
    {
       for(SerializationGroupMember<T> member : active.values())
       {
-         member.preReplicate();
+         member.releaseReferences();
       }
       active.clear();
    }

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/SerializationGroupMember.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/SerializationGroupMember.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/main/java/org/jboss/ejb3/cache/spi/impl/SerializationGroupMember.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -74,7 +74,7 @@
    private boolean prePassivated;
    
    /** The cache that's handling us */
-   private transient PassivatingBackingCache<T, SerializationGroupMember<T>> cache;
+//   private transient PassivatingBackingCache<T, SerializationGroupMember<T>> cache;
    
    public SerializationGroupMember(T obj, PassivatingBackingCache<T, SerializationGroupMember<T>> cache)
    {
@@ -83,7 +83,7 @@
       
       this.obj = transientObj = obj;
       this.id = obj.getId();
-      this.cache = cache;
+//      this.cache = cache;
       this.clustered = cache.isClustered();
    }
    
@@ -131,7 +131,16 @@
     */
    public SerializationGroupImpl<T> getGroup()
    {
-      return (group == null || group.isInvalid()) ? null : group;
+      SerializationGroupImpl<T> result = group;
+      if (result != null)
+      {
+         synchronized (result)
+         {
+            if (result.isInvalid())
+               result = null;
+         }
+      }
+      return result;
    }
 
    /**
@@ -142,7 +151,7 @@
    public void setGroup(SerializationGroupImpl<T> group)
    {
       this.group = group;
-      if (group != null)
+      if (this.groupId == null && group != null)
          this.groupId = group.getId();
    }
 
@@ -157,13 +166,11 @@
    }
 
    /**
-    * Prepare the group member for passivation. Ensure any @PrePassivate 
-    * callback is invoked on the underlying object.  If we are a member of a 
-    * group, ensure any reference to the 
-    * {@link #getUnderlyingItem() underlying object} or to the 
+    * Prepares the group member for group passivation by ensuring any 
+    * reference to the {@link #getUnderlyingItem() underlying object} or to the 
     * {@link #getGroup()} is nulled.
     */
-   public void prePassivate()
+   public void releaseReferences()
    {
       // make sure we don't passivate the group twice
       group = null;
@@ -174,7 +181,7 @@
       // for passivation callbacks
       obj = null;
       
-      cache.passivate(this.id);
+//      cache.passivate(this.id);
    }
    
    public boolean isPrePassivated()
@@ -195,29 +202,6 @@
       // no-op
    }
    
-   /**
-    * Prepare the group member for replication. Ensure any required callback
-    * (e.g. @PreReplicaate) is invoked on the underlying object. If we are a 
-    * member of a group, ensure any reference to the 
-    * {@link #getUnderlyingItem() underlying object} or to the 
-    * {@link #getGroup()} is nulled.
-    * 
-    * @throws UnsupportedOperationException if {@link #isClustered()} returns
-    *                                       <code>false</code>
-    */
-   public void preReplicate()
-   {
-      // make sure we don't replicate the group twice
-      group = null;
-      // null out obj so when delegate serializes this entry
-      // we don't serialize it. It serializes with the PassivationGroup only
-      obj = null;
-      
-      // FIXME -- what does this do for us?
-      // Nothing -- it will fail
-//      cache.release(this);
-   }
-   
    public boolean isPreReplicated()
    {
       return preReplicated;
@@ -244,10 +228,13 @@
       // Tell our group about it
       if (group != null)
       {
-         if (inUse)
-            group.addActive(this);
-         else
-            group.removeActive(id);
+         synchronized (group)
+         {
+            if (inUse)
+               group.addActive(this);
+            else
+               group.removeActive(id);
+         }
       }         
    }
    
@@ -259,9 +246,9 @@
     */
    public void setPassivatingCache(PassivatingBackingCache<T, SerializationGroupMember<T>> delegate)
    {
-      assert delegate != null : "delegate is null";
-      
-      this.cache = delegate;
+//      assert delegate != null : "delegate is null";
+//      
+//      this.cache = delegate;
    }
 
    @Override

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/distributed/GroupedPassivatingUnitTestCase.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/distributed/GroupedPassivatingUnitTestCase.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/distributed/GroupedPassivatingUnitTestCase.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -55,6 +55,8 @@
    
    public void testSimpleGroupPassivation() throws Exception
    {      
+      log.info("testSimpleGroupPassivation()");
+      
       MockCluster cluster = new MockCluster(false);
       MockClusterMember node0 = cluster.getNode0();
       MockCacheConfig cacheConfig = new MockCacheConfig();
@@ -66,7 +68,7 @@
       cluster.getNode0().setTCCL();
       try
       {
-         Object key1 = container1.getCache().create(null, null).getId();
+         Object key1 = container1.getCache().create(null, null);
          MockBeanContext firstCtx1;
          MockBeanContext ctx1 = firstCtx1 = container1.getCache().get(key1);
          

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/distributed/MockJBCIntegratedObjectStore.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/distributed/MockJBCIntegratedObjectStore.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/distributed/MockJBCIntegratedObjectStore.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -275,7 +275,7 @@
       {
          long now = System.currentTimeMillis();
          long minRemovalUse = now - (expirationTimeSeconds * 1000);                     
-         for (CacheableTimestamp ts : getPassivatedEntries())
+         for (CacheableTimestamp ts : getAllEntries())
          {
             try
             {
@@ -295,24 +295,27 @@
 
    public void runPassivation()
    {
-      if (idleTimeSeconds > 0)
+      if (maxSize > 0 || idleTimeSeconds > 0)
       {
          long now = System.currentTimeMillis();
-         long minPassUse = now - (idleTimeSeconds * 1000);
+         long minPassUse = (idleTimeSeconds > 0 ? now - (idleTimeSeconds * 1000) : 0);
          
          SortedSet<CacheableTimestamp> timestamps = getInMemoryEntries();
-         int overCount = timestamps.size() - maxSize;
+         int overCount = (maxSize > 0 ? timestamps.size() - maxSize : 0);
          for (CacheableTimestamp ts : timestamps)
          {
             try
             {
-               long lastUsed = ts.getLastUsed();
-               if (overCount > 0 || minPassUse >= lastUsed)
+               if (overCount > 0 || minPassUse >= ts.getLastUsed())
                {
                   log.trace("attempting to passivate " + ts.getId());
                   owningCache.passivate(ts.getId());
                   overCount--;
                }
+               else
+               {
+                  break;
+               }
             }
             catch (IllegalStateException ise)
             {
@@ -390,12 +393,17 @@
       return getTimestampSet(true);
    }
    
-   private SortedSet<CacheableTimestamp> getTimestampSet(boolean passivated)
+   private SortedSet<CacheableTimestamp> getAllEntries()
+   {
+      return getTimestampSet(null);
+   }
+   
+   private SortedSet<CacheableTimestamp> getTimestampSet(Boolean passivated)
    {      
       SortedSet<CacheableTimestamp> set = new TreeSet<CacheableTimestamp>();
       for (Map.Entry<Object, Long> entry : timestamps.entrySet())
       {
-         if (passivated != inMemory.contains(entry.getKey()))
+         if (passivated == null || passivated.booleanValue() != inMemory.contains(entry.getKey()))
          {
             set.add(new CacheableTimestamp(entry.getKey(), entry.getValue().longValue()));
          }

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/impl/backing/PassivatingBackingCacheImplUnitTestCase.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/impl/backing/PassivatingBackingCacheImplUnitTestCase.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/impl/backing/PassivatingBackingCacheImplUnitTestCase.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -32,6 +32,7 @@
 import org.jboss.ejb3.test.cache.mock.MockCacheConfig;
 import org.jboss.ejb3.test.cache.mock.MockEjb3System;
 import org.jboss.ejb3.test.cache.mock.MockXPC;
+import org.jboss.logging.Logger;
 
 /**
  * Comment
@@ -40,18 +41,21 @@
  * @version $Revision: 65339 $
  */
 public class PassivatingBackingCacheImplUnitTestCase extends TestCase
-{
+{   
+   private static final Logger log = Logger.getLogger(PassivatingBackingCacheImplUnitTestCase.class);
    
    /**
-    * Peek of an active object should not change it state.
+    * Peek of an active object should not prevent its passivation.
     */
    @SuppressWarnings("unchecked")
-   public void testPeekActive() throws Exception
+   public void testPassivatePeeked() throws Exception
    {
+      log.info("testPassivatePeeked()");
+      
       MockEjb3System system = new MockEjb3System(false, CacheType.SIMPLE);
       MockXPC sharedXPC = new MockXPC();
       MockCacheConfig config = new MockCacheConfig();
-      config.setIdleTimeoutSeconds(4);
+      config.setIdleTimeoutSeconds(100);
       MockBeanContainer container = system.deployBeanContainer("MockBeanContainer1", null, CacheType.SIMPLE, config, sharedXPC);
       TransactionalCache cache = (TransactionalCache) container.getCache();
       PassivatingBackingCacheImpl<MockBeanContext, SerializationGroupMember<MockBeanContext>> backingCache = (PassivatingBackingCacheImpl<MockBeanContext, SerializationGroupMember<MockBeanContext>>) cache.getBackingCache();
@@ -62,18 +66,51 @@
       
       try
       {
-         backingCache.release(key);
-         fail("Should not be able to release entry that has not been gotten");
+         backingCache.passivate(key);
       }
-      catch (IllegalStateException good)
+      catch (IllegalStateException bad)
       {
-         backingCache.get(key);
-         backingCache.release(key);      
+         fail("Should be able to passivate entry that has been peeked");
       }
       finally
       {
          backingCache.remove(key);
+      }         
+   }  
+   
+   /**
+    * Get of an active object should prevent its passivation.
+    */
+   @SuppressWarnings("unchecked")
+   public void testPassivateActive() throws Exception
+   {
+      log.info("testPassivateActive()");
+      
+      MockEjb3System system = new MockEjb3System(false, CacheType.SIMPLE);
+      MockXPC sharedXPC = new MockXPC();
+      MockCacheConfig config = new MockCacheConfig();
+      config.setIdleTimeoutSeconds(100);
+      MockBeanContainer container = system.deployBeanContainer("MockBeanContainer1", null, CacheType.SIMPLE, config, sharedXPC);
+      TransactionalCache cache = (TransactionalCache) container.getCache();
+      PassivatingBackingCacheImpl<MockBeanContext, SerializationGroupMember<MockBeanContext>> backingCache = (PassivatingBackingCacheImpl<MockBeanContext, SerializationGroupMember<MockBeanContext>>) cache.getBackingCache();
+      SerializationGroupMember<MockBeanContext> obj = backingCache.create(null, null);
+      Object key = obj.getId();
+      
+      backingCache.get(key);
+      
+      try
+      {
+         backingCache.passivate(key);
+         fail("Should not be able to passivate entry that is active");
       }
-         
+      catch (IllegalStateException bad)
+      {
+         backingCache.release(key);
+         backingCache.passivate(key);
+      }
+      finally
+      {
+         backingCache.remove(key);
+      }         
    }
 }

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/integrated/GroupAwareTransactionalCacheUnitTestCase.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/integrated/GroupAwareTransactionalCacheUnitTestCase.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/integrated/GroupAwareTransactionalCacheUnitTestCase.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -50,7 +50,9 @@
    }
    
    public void testNonGroupedPassivation() throws Exception
-   {
+   {    
+      log.info("testNonGroupedPassivation()");
+      
       MockEjb3System system = new MockEjb3System(false, CacheType.SIMPLE);
       MockXPC sharedXPC = new MockXPC();
       MockCacheConfig config = new MockCacheConfig();
@@ -58,7 +60,7 @@
       MockBeanContainer container = system.deployBeanContainer("MockBeanContainer1", null, CacheType.SIMPLE, config, sharedXPC);
       Cache<MockBeanContext> cache = container.getCache();
       
-      Object key = cache.create(null, null).getId();
+      Object key = cache.create(null, null);
       MockBeanContext obj = cache.get(key);
       
       cache.finished(obj);
@@ -91,6 +93,8 @@
 
    public void testSimpleGroupPassivation() throws Exception
    {    
+      log.info("testSimpleGroupPassivation()");
+      
       MockEjb3System system = new MockEjb3System(false, CacheType.SIMPLE);
       MockXPC sharedXPC = new MockXPC();
       MockCacheConfig config = new MockCacheConfig();
@@ -104,7 +108,7 @@
       
       try
       {
-         Object key1 = container1.getCache().create(null, null).getId();
+         Object key1 = container1.getCache().create(null, null);
          MockBeanContext firstCtx1;
          MockBeanContext ctx1 = firstCtx1 = container1.getCache().get(key1);
          
@@ -160,8 +164,47 @@
    /**
     * Test call to bean1 that calls into bean2 that calls back into bean1
     */
-   public void testRecursiveCalls()
-   {
-      // FIXME implement testRecursiveCalls() -- which will fail :(
+   public void testRecursiveCalls() throws Exception
+   {    
+      log.info("testRecursiveCalls()");
+      
+      MockEjb3System system = new MockEjb3System(false, CacheType.SIMPLE);
+      MockXPC sharedXPC = new MockXPC();
+      MockCacheConfig config = new MockCacheConfig();
+      config.setIdleTimeoutSeconds(1);
+      MockBeanContainer container1 = system.deployBeanContainer("MockBeanContainer1", null, CacheType.SIMPLE, config, sharedXPC);
+      MockBeanContainer container2 = system.deployBeanContainer("MockBeanContainer2", "MockBeanContainer1", CacheType.SIMPLE, config, sharedXPC);
+      
+      log.info("Containers deployed");
+      
+      assertTrue(container1.hasChild(container2));
+      
+      try
+      {
+         Object key1 = container1.getCache().create(null, null);
+         MockBeanContext firstCtx1;
+         MockBeanContext ctx1 = firstCtx1 = container1.getCache().get(key1);
+         
+         assertEquals(sharedXPC, ctx1.getXPC());
+         Object key2 = ctx1.getChild(container2.getName());
+         MockBeanContext ctx2 = container2.getCache().get(key2);
+         assertNotNull(ctx2);
+         assertSame(sharedXPC, ctx2.getXPC());
+         
+         MockBeanContext secondCtx1 = container1.getCache().get(key1);
+         assertSame(firstCtx1, secondCtx1);
+         assertSame(sharedXPC, secondCtx1.getXPC());
+
+         container1.getCache().finished(secondCtx1);
+         container2.getCache().finished(ctx2);
+         container1.getCache().finished(firstCtx1);
+         
+      }
+      finally
+      {
+         container1.stop();
+         container2.stop();
+      }
+      
    }
 }

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/integrated/TransactionalCacheUnitTestCase.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/integrated/TransactionalCacheUnitTestCase.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/integrated/TransactionalCacheUnitTestCase.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -28,6 +28,7 @@
 import org.jboss.ejb3.test.cache.mock.MockBeanContainer;
 import org.jboss.ejb3.test.cache.mock.MockBeanContext;
 import org.jboss.ejb3.test.cache.mock.MockEjb3System;
+import org.jboss.logging.Logger;
 
 /**
  * Comment
@@ -38,6 +39,8 @@
  */
 public class TransactionalCacheUnitTestCase extends Ejb3CacheTestCaseBase
 {   
+   private static final Logger log = Logger.getLogger(TransactionalCacheUnitTestCase.class);
+   
    protected Cache<MockBeanContext> createCache() throws Exception
    {
       MockEjb3System system = new MockEjb3System(false, CacheType.NON_PASSIVATING);
@@ -47,6 +50,8 @@
    
    public void testNonExistingGet() throws Exception
    {      
+      log.info("testNonExistingGet()");
+      
       Cache<MockBeanContext> cache = createCache();
       
       try
@@ -62,9 +67,11 @@
    
    public void testSimpleLifeCycle() throws Exception
    {
+      log.info("testSimpleLifeCycle()");
+      
       Cache<MockBeanContext> cache = createCache();
       
-      Object key = cache.create(null, null).getId();
+      Object key = cache.create(null, null);
       MockBeanContext object = cache.get(key);
       
       assertNotNull(object);
@@ -86,30 +93,28 @@
    {
       Cache<MockBeanContext> cache = createCache();
       
-      Object key = cache.create(null, null).getId();
+      Object key = cache.create(null, null);
       MockBeanContext object = cache.get(key);
       
       assertNotNull(object);
       
-      try
-      {
-         cache.get(key);
-         fail("Two sequential get calls should throw ISE");
-      }
-      catch(IllegalStateException e)
-      {
-         // good
-      }
-      finally {
-         cache.remove(key);
-      }
+      MockBeanContext object2 = cache.get(key);
+      
+      assertSame(object, object2);
+      
+      cache.finished(object2);
+      cache.finished(object);
+      
+      cache.remove(key);
    }
    
-   public void testSequentialFinishedCalls() throws Exception
-   {
+   public void testExcessFinishedCalls() throws Exception
+   {    
+      log.info("testExcessFinishedCalls()");
+      
       Cache<MockBeanContext> cache = createCache();
       
-      Object key = cache.create(null, null).getId();
+      Object key = cache.create(null, null);
       MockBeanContext object = cache.get(key);
       
       assertNotNull(object);

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/mock/MockBeanContext.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/mock/MockBeanContext.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/mock/MockBeanContext.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -61,13 +61,13 @@
       return (MockBeanContainer) MockRegistry.get(containerName);
    }
    
-   public void addChild(MockBeanContext child)
+   public void addChild(String childContainerName, Object childId)
    {
       // Just store the id; tests get the id from getChild() and
       // then call get(id) on the child container's cache.
       // This simulates how an internal call via a nested bean's 
       // proxy would work
-      children.put(child.containerName, child.getId());
+      children.put(childContainerName, childId);
    }
    
    public Object getChild(String containerName)

Modified: projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/mock/MockStatefulObjectFactory.java
===================================================================
--- projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/mock/MockStatefulObjectFactory.java	2008-03-12 02:33:18 UTC (rev 70756)
+++ projects/ejb3/branches/cluster-dev/ejb3-cache/src/test/java/org/jboss/ejb3/test/cache/mock/MockStatefulObjectFactory.java	2008-03-12 04:58:30 UTC (rev 70757)
@@ -50,7 +50,7 @@
       // Here we mock creating nested beans
       for (MockBeanContainer childContainer : container.getChildren())
       {
-         ctx.addChild(childContainer.getCache().create(null, null));
+         ctx.addChild(childContainer.getName(), childContainer.getCache().create(null, null));
       }
       
       creationCount++;




More information about the jboss-cvs-commits mailing list