[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