[jbosscache-commits] JBoss Cache SVN: r7732 - in core/branches/3.0.X/src: test/java/org/jboss/cache/api and 1 other directory.

jbosscache-commits at lists.jboss.org jbosscache-commits at lists.jboss.org
Thu Feb 19 08:07:43 EST 2009


Author: manik.surtani at jboss.com
Date: 2009-02-19 08:07:43 -0500 (Thu, 19 Feb 2009)
New Revision: 7732

Added:
   core/branches/3.0.X/src/test/java/org/jboss/cache/api/ReAddDeletedNodeTest.java
Modified:
   core/branches/3.0.X/src/main/java/org/jboss/cache/mvcc/MVCCNodeHelper.java
Log:
JBCACHE-1481 -  Re-adding a node deleted via parent inside transaction breaks the parent

Modified: core/branches/3.0.X/src/main/java/org/jboss/cache/mvcc/MVCCNodeHelper.java
===================================================================
--- core/branches/3.0.X/src/main/java/org/jboss/cache/mvcc/MVCCNodeHelper.java	2009-02-19 13:03:56 UTC (rev 7731)
+++ core/branches/3.0.X/src/main/java/org/jboss/cache/mvcc/MVCCNodeHelper.java	2009-02-19 13:07:43 UTC (rev 7732)
@@ -46,15 +46,15 @@
 import java.util.Map;
 
 /**
- * Utility functions to manipulate wrapping {@link org.jboss.cache.InternalNode}s as {@link org.jboss.cache.mvcc.ReadCommittedNode}
- * or {@link org.jboss.cache.mvcc.RepeatableReadNode}s.  Would also entail locking, if necessary.
+ * Utility functions to manipulate wrapping {@link org.jboss.cache.InternalNode}s as {@link
+ * org.jboss.cache.mvcc.ReadCommittedNode} or {@link org.jboss.cache.mvcc.RepeatableReadNode}s.  Would also entail
+ * locking, if necessary.
  *
  * @author Manik Surtani (<a href="mailto:manik AT jboss DOT org">manik AT jboss DOT org</a>)
  * @since 3.0
  */
 @NonVolatile
-public class MVCCNodeHelper
-{
+public class MVCCNodeHelper {
    DataContainer dataContainer;
    NodeFactory nodeFactory;
    private static final Log log = LogFactory.getLog(MVCCNodeHelper.class);
@@ -66,8 +66,7 @@
    private boolean lockParentForChildInsertRemove;
 
    @Inject
-   public void injectDependencies(DataContainer dataContainer, NodeFactory nodeFactory, LockManager lockManager, Configuration configuration)
-   {
+   public void injectDependencies(DataContainer dataContainer, NodeFactory nodeFactory, LockManager lockManager, Configuration configuration) {
       this.nodeFactory = nodeFactory;
       this.dataContainer = dataContainer;
       this.configuration = configuration;
@@ -75,8 +74,7 @@
    }
 
    @Start
-   public void start()
-   {
+   public void start() {
       defaultLockAcquisitionTimeout = configuration.getLockAcquisitionTimeout();
       writeSkewCheck = configuration.isWriteSkewCheck();
       lockParentForChildInsertRemove = configuration.isLockParentForChildInsertRemove();
@@ -84,23 +82,23 @@
 
 
    /**
-    * Attempts to provide the context with a set of wrapped nodes based on the Collection of fqns passed in.  If the nodes
-    * already exist in the context then the node is not wrapped again.
+    * Attempts to provide the context with a set of wrapped nodes based on the Collection of fqns passed in.  If the
+    * nodes already exist in the context then the node is not wrapped again.
     * <p/>
-    * {@link InternalNode}s are wrapped using {@link org.jboss.cache.NodeFactory#createWrappedNode(org.jboss.cache.InternalNode, org.jboss.cache.InternalNode)}
-    * and as such, null internal nodes are treated according to isolation level used.  See {@link org.jboss.cache.NodeFactory#createWrappedNode(org.jboss.cache.InternalNode, org.jboss.cache.InternalNode)}
-    * for details on this behaviour.
+    * {@link InternalNode}s are wrapped using {@link org.jboss.cache.NodeFactory#createWrappedNode(org.jboss.cache.InternalNode,
+    * org.jboss.cache.InternalNode)} and as such, null internal nodes are treated according to isolation level used.
+    * See {@link org.jboss.cache.NodeFactory#createWrappedNode(org.jboss.cache.InternalNode,
+    * org.jboss.cache.InternalNode)} for details on this behaviour.
     * <p/>
-    * Note that if the context has the {@link org.jboss.cache.config.Option#isForceWriteLock()} option set, then write locks are
-    * acquired and the node is copied.
+    * Note that if the context has the {@link org.jboss.cache.config.Option#isForceWriteLock()} option set, then write
+    * locks are acquired and the node is copied.
     * <p/>
     *
     * @param ctx  current invocation context
     * @param fqns collection of Fqns.  Should not be null.
     * @throws InterruptedException if write locks are forced and the lock manager is interrupted.
     */
-   public void wrapNodesForReading(InvocationContext ctx, Collection<Fqn> fqns) throws InterruptedException
-   {
+   public void wrapNodesForReading(InvocationContext ctx, Collection<Fqn> fqns) throws InterruptedException {
       boolean forceWriteLock = ctx.getOptionOverrides().isForceWriteLock();
 
       // does the node exist in the context?
@@ -108,8 +106,8 @@
    }
 
    /**
-    * Similar to {@link #wrapNodesForReading(org.jboss.cache.InvocationContext, java.util.Collection)} except
-    * that this version takes a single Fqn parameter to wrap a single node.
+    * Similar to {@link #wrapNodesForReading(org.jboss.cache.InvocationContext, java.util.Collection)} except that this
+    * version takes a single Fqn parameter to wrap a single node.
     *
     * @param ctx          current invocation context
     * @param fqn          fqn to fetch and wrap
@@ -117,31 +115,24 @@
     * @return read committed node, or null if one is not found.
     * @throws InterruptedException if write locks are forced and the lock manager is interrupted.
     */
-   public NodeSPI wrapNodeForReading(InvocationContext ctx, Fqn fqn, boolean putInContext) throws InterruptedException
-   {
+   public NodeSPI wrapNodeForReading(InvocationContext ctx, Fqn fqn, boolean putInContext) throws InterruptedException {
       return wrapNodeForReading(ctx, fqn, ctx.getOptionOverrides().isForceWriteLock(), putInContext);
    }
 
    @SuppressWarnings("unchecked")
-   private NodeSPI wrapNodeForReading(InvocationContext ctx, Fqn f, boolean writeLockForced, boolean putInContext) throws InterruptedException
-   {
+   private NodeSPI wrapNodeForReading(InvocationContext ctx, Fqn f, boolean writeLockForced, boolean putInContext) throws InterruptedException {
       NodeSPI n;
-      if (writeLockForced)
-      {
+      if (writeLockForced) {
          if (trace) log.trace("Forcing lock on reading node " + f);
          return wrapNodeForWriting(ctx, f, true, false, false, false, false);
-      }
-      else if ((n = ctx.lookUpNode(f)) == null)
-      {
+      } else if ((n = ctx.lookUpNode(f)) == null) {
          if (trace) log.trace("Node " + f + " is not in context, fetching from container.");
          // simple implementation.  Peek the node, wrap it, put wrapped node in the context.
          InternalNode[] nodes = dataContainer.peekInternalNodeAndDirectParent(f, false);
          ReadCommittedNode wrapped = nodeFactory.createWrappedNode(nodes[0], nodes[1]);  // even though parents aren't needed for reading, we hold on to this ref in case the node is later written to.
          if (putInContext && wrapped != null) ctx.putLookedUpNode(f, wrapped);
          return wrapped;
-      }
-      else
-      {
+      } else {
          if (trace) log.trace("Node " + f + " is already in context.");
          return n;
       }
@@ -152,20 +143,18 @@
     *
     * @param ctx context
     * @param fqn Fqn to lock
-    * @return true if a lock was needed and acquired, false if it didn't need to acquire the lock (i.e., lock was already held)
+    * @return true if a lock was needed and acquired, false if it didn't need to acquire the lock (i.e., lock was
+    *         already held)
     * @throws InterruptedException if interrupted
     * @throws TimeoutException     if we are unable to acquire the lock after a specified timeout.
     */
-   private boolean acquireLock(InvocationContext ctx, Fqn fqn) throws InterruptedException, TimeoutException
-   {
+   private boolean acquireLock(InvocationContext ctx, Fqn fqn) throws InterruptedException, TimeoutException {
       // don't EVER use lockManager.isLocked() since with lock striping it may be the case that we hold the relevant
       // lock which may be shared with another Fqn that we have a lock for already.
       // nothing wrong, just means that we fail to record the lock.  And that is a problem.
       // Better to check our records and lock again if necessary.
-      if (!ctx.hasLock(fqn))
-      {
-         if (!lockManager.lockAndRecord(fqn, WRITE, ctx))
-         {
+      if (!ctx.hasLock(fqn)) {
+         if (!lockManager.lockAndRecord(fqn, WRITE, ctx)) {
             Object owner = lockManager.getWriteOwner(fqn);
             throw new TimeoutException("Unable to acquire lock on Fqn [" + fqn + "] after [" + ctx.getLockAcquisitionTimeout(defaultLockAcquisitionTimeout) + "] milliseconds for requestor [" + lockManager.getLockOwner(ctx) + "]! Lock held by [" + owner + "]");
          }
@@ -176,10 +165,10 @@
 
    /**
     * First checks in contexts for the existence of the node.  If it does exist, it will return it, acquiring a lock if
-    * necessary.  Otherwise, it will peek in the dataContainer, wrap the node, lock if necessary, and add it to the context.
-    * If it doesn't even exist in the dataContainer and createIfAbsent is true, it will create a new node and add it to the
-    * data structure.  It will lock the node, and potentially the parent as well, if necessary.  If the parent is locked,
-    * it too will be added to the context if it wasn't there already.
+    * necessary.  Otherwise, it will peek in the dataContainer, wrap the node, lock if necessary, and add it to the
+    * context. If it doesn't even exist in the dataContainer and createIfAbsent is true, it will create a new node and
+    * add it to the data structure.  It will lock the node, and potentially the parent as well, if necessary.  If the
+    * parent is locked, it too will be added to the context if it wasn't there already.
     *
     * @param context             invocation context
     * @param fqn                 to retrieve
@@ -192,54 +181,47 @@
     * @throws InterruptedException if interrupted
     */
    @SuppressWarnings("unchecked")
-   public ReadCommittedNode wrapNodeForWriting(InvocationContext context, Fqn fqn, boolean lockForWriting, boolean createIfAbsent, boolean includeInvalidNodes, boolean forRemoval, boolean force) throws InterruptedException
-   {
+   public ReadCommittedNode wrapNodeForWriting(InvocationContext context, Fqn fqn, boolean lockForWriting, boolean createIfAbsent, boolean includeInvalidNodes, boolean forRemoval, boolean force) throws InterruptedException {
       Fqn parentFqn = null;
       ReadCommittedNode n = (ReadCommittedNode) context.lookUpNode(fqn);
       if (createIfAbsent && n != null && n.isNullNode()) n = null;
       if (n != null) // exists in context!  Just acquire lock if needed, and wrap.
       {
          // acquire lock if needed
-         if (lockForWriting && acquireLock(context, fqn))
-         {
+         if (lockForWriting && acquireLock(context, fqn)) {
             // create a copy of the underlying node
             n.markForUpdate(dataContainer, writeSkewCheck);
          }
          if (trace) log.trace("Retrieving wrapped node " + fqn);
-         if (n.isDeleted() && createIfAbsent)
-         {
+         if (n.isDeleted() && createIfAbsent) {
             if (trace) log.trace("Node is deleted in current scope.  Need to un-delete.");
             n.markAsDeleted(false);
             n.setValid(true, false);
+            // has the parent been deleted too?  :-(
+            wrapNodeForWriting(context, fqn.getParent(), true, true, includeInvalidNodes, false, force);
          }
-      }
-      else
-      {
+      } else {
          // else, fetch from dataContainer.
          InternalNode[] nodes = dataContainer.peekInternalNodeAndDirectParent(fqn, includeInvalidNodes);
          InternalNode in = nodes[0];
-         if (in != null)
-         {
+         if (in != null) {
             // exists in cache!  Just acquire lock if needed, and wrap.
             // do we need a lock?
             boolean needToCopy = false;
-            if (lockForWriting && acquireLock(context, fqn))
-            {
+            if (lockForWriting && acquireLock(context, fqn)) {
                needToCopy = true;
             }
             n = nodeFactory.createWrappedNode(in, nodes[1]);
             context.putLookedUpNode(fqn, n);
             if (needToCopy) n.markForUpdate(dataContainer, writeSkewCheck);
-         }
-         else if (createIfAbsent) // else, do we need to create one?
+         } else if (createIfAbsent) // else, do we need to create one?
          {
             parentFqn = fqn.getParent();
             NodeSPI parent = wrapNodeForWriting(context, parentFqn, false, createIfAbsent, false, false, false);
             // do we need to lock the parent to create children?
             boolean parentLockNeeded = isParentLockNeeded(parent.getDelegationTarget());
             // get a lock on the parent.
-            if (parentLockNeeded && acquireLock(context, parentFqn))
-            {
+            if (parentLockNeeded && acquireLock(context, parentFqn)) {
                ReadCommittedNode parentRCN = (ReadCommittedNode) context.lookUpNode(parentFqn);
                parentRCN.markForUpdate(dataContainer, writeSkewCheck);
             }
@@ -267,8 +249,9 @@
    }
 
    /**
-    * The same as {@link #wrapNodeForWriting(org.jboss.cache.InvocationContext, org.jboss.cache.Fqn, boolean, boolean, boolean, boolean, boolean)}
-    * except that it takes in an {@link org.jboss.cache.InternalNode} instead of a {@link Fqn}.  Saves on a lookup.
+    * The same as {@link #wrapNodeForWriting(org.jboss.cache.InvocationContext, org.jboss.cache.Fqn, boolean, boolean,
+    * boolean, boolean, boolean)} except that it takes in an {@link org.jboss.cache.InternalNode} instead of a {@link
+    * Fqn}.  Saves on a lookup.
     * <p/>
     * Also assumes that the node exists, and hence will not be created.
     * <p/>
@@ -279,27 +262,22 @@
     * @throws InterruptedException if interrupted
     */
    @SuppressWarnings("unchecked")
-   public NodeSPI wrapNodeForWriting(InvocationContext context, InternalNode node, InternalNode parent) throws InterruptedException
-   {
+   public NodeSPI wrapNodeForWriting(InvocationContext context, InternalNode node, InternalNode parent) throws InterruptedException {
       Fqn fqn = node.getFqn();
       NodeSPI n = context.lookUpNode(fqn);
       if (n != null) // exists in context!  Just acquire lock if needed, and wrap.
       {
          // acquire lock if needed
-         if (acquireLock(context, fqn))
-         {
+         if (acquireLock(context, fqn)) {
             // create a copy of the underlying node
             n.markForUpdate(dataContainer, writeSkewCheck);
          }
          if (trace) log.trace("Retrieving wrapped node " + fqn);
-      }
-      else
-      {
+      } else {
          // exists in cache!  Just acquire lock if needed, and wrap.
          // do we need a lock?
          boolean needToCopy = false;
-         if (acquireLock(context, fqn))
-         {
+         if (acquireLock(context, fqn)) {
             needToCopy = true;
          }
          n = nodeFactory.createWrappedNode(node, parent);
@@ -319,8 +297,7 @@
     * @throws InterruptedException if the lock manager is interrupted.
     */
    @SuppressWarnings("unchecked")
-   public List<Fqn> wrapNodesRecursivelyForRemoval(InvocationContext ctx, Fqn fqn) throws InterruptedException
-   {
+   public List<Fqn> wrapNodesRecursivelyForRemoval(InvocationContext ctx, Fqn fqn) throws InterruptedException {
       // when removing a node we want to get a lock on the Fqn anyway and return the wrapped node.
       if (fqn.isRoot()) throw new CacheException("Attempting to remove Fqn.ROOT!");
 
@@ -329,8 +306,7 @@
       boolean needToCopyParent = false;
       boolean parentLockNeeded = isParentLockNeeded(parentFqn, ctx);
       ReadCommittedNode parent = null;
-      if (parentLockNeeded)
-      {
+      if (parentLockNeeded) {
          needToCopyParent = acquireLock(ctx, parentFqn);
          // Ensure the node is in the context.
          parent = wrapAndPutInContext(ctx, parentFqn, needToCopyParent);
@@ -341,16 +317,12 @@
       // Ensure the node is in the context.
       ReadCommittedNode node = wrapAndPutInContext(ctx, fqn, needToCopyNode);
 
-      if (node == null || node.isNullNode())
-      {
+      if (node == null || node.isNullNode()) {
          // node does not exist; return an empty list since there is nothing to remove!
          return Collections.emptyList();
-      }
-      else
-      {
+      } else {
          // update child ref on parent to point to child as this is now a copy.
-         if (parentLockNeeded && (needToCopyNode || needToCopyParent))
-         {
+         if (parentLockNeeded && (needToCopyNode || needToCopyParent)) {
             if (parent == null) throw new NodeNotExistsException("Parent node " + parentFqn + " does not exist!");
             parent.getDelegationTarget().addChild(node.getDelegationTarget());
          }
@@ -360,8 +332,7 @@
          List<Fqn> fqnsToBeRemoved = new LinkedList<Fqn>();
          fqnsToBeRemoved.add(fqn);
 
-         if (!childMap.isEmpty())
-         {
+         if (!childMap.isEmpty()) {
             for (InternalNode n : childMap.values()) lockForWritingRecursive(n.getFqn(), ctx, fqnsToBeRemoved);
          }
 
@@ -374,20 +345,18 @@
     *
     * @param fqn     Fqn to lock
     * @param ctx     invocation context to add wrapped node to
-    * @param fqnList fqnList to update - this list should not be null but should be initially empty and will be populated
-    *                with a list of all Fqns locked in this call.
+    * @param fqnList fqnList to update - this list should not be null but should be initially empty and will be
+    *                populated with a list of all Fqns locked in this call.
     * @throws InterruptedException if interrupted
     */
    @SuppressWarnings("unchecked")
-   private void lockForWritingRecursive(Fqn fqn, InvocationContext ctx, List<Fqn> fqnList) throws InterruptedException
-   {
+   private void lockForWritingRecursive(Fqn fqn, InvocationContext ctx, List<Fqn> fqnList) throws InterruptedException {
       acquireLock(ctx, fqn); // lock node
       if (fqnList != null) fqnList.add(fqn);
 
       // now wrap and add to the context
       ReadCommittedNode rcn = wrapNodeForWriting(ctx, fqn, true, false, true, false, false);
-      if (rcn != null)
-      {
+      if (rcn != null) {
          rcn.markForUpdate(dataContainer, writeSkewCheck);
          Map<Object, InternalNode<?, ?>> children = rcn.getDelegationTarget().getChildrenMap();
          for (InternalNode child : children.values())
@@ -396,8 +365,9 @@
    }
 
    /**
-    * Identical to {@link #lockForWritingRecursive(org.jboss.cache.Fqn, org.jboss.cache.InvocationContext, java.util.List)}
-    * except that it uses an {@link org.jboss.cache.InternalNode} instead of an {@link Fqn} - saves a lookup.
+    * Identical to {@link #lockForWritingRecursive(org.jboss.cache.Fqn, org.jboss.cache.InvocationContext,
+    * java.util.List)} except that it uses an {@link org.jboss.cache.InternalNode} instead of an {@link Fqn} - saves a
+    * lookup.
     *
     * @param node    node to lock recursively
     * @param ctx     invocation context
@@ -405,16 +375,14 @@
     * @throws InterruptedException if interrupted
     */
    @SuppressWarnings("unchecked")
-   private void lockForWritingRecursive(InternalNode node, InternalNode parent, InvocationContext ctx, List<Fqn> fqnList) throws InterruptedException
-   {
+   private void lockForWritingRecursive(InternalNode node, InternalNode parent, InvocationContext ctx, List<Fqn> fqnList) throws InterruptedException {
       Fqn fqn = node.getFqn();
       acquireLock(ctx, fqn); // lock node
       if (fqnList != null) fqnList.add(fqn);
 
       // now wrap and add to the context
       NodeSPI rcn = wrapNodeForWriting(ctx, node, parent);
-      if (rcn != null)
-      {
+      if (rcn != null) {
          rcn.markForUpdate(dataContainer, writeSkewCheck);
          Map<Object, InternalNode<?, ?>> children = node.getChildrenMap();
          for (InternalNode child : children.values()) lockForWritingRecursive(child, node, ctx, fqnList);
@@ -423,9 +391,9 @@
 
 
    /**
-    * Wraps a node and puts it in the context, optionally copying the node for updating if <tt>forUpdate</tt> is <tt>true</tt>.
-    * If the node is already in the context, a new wrapped node is not created, but the existing one is still checked
-    * for changes and potentially marked for update if <tt>forUpdate</tt> is <tt>true</tt>.
+    * Wraps a node and puts it in the context, optionally copying the node for updating if <tt>forUpdate</tt> is
+    * <tt>true</tt>. If the node is already in the context, a new wrapped node is not created, but the existing one is
+    * still checked for changes and potentially marked for update if <tt>forUpdate</tt> is <tt>true</tt>.
     *
     * @param ctx       invocation context to add node to
     * @param fqn       fqn of node to add
@@ -433,11 +401,9 @@
     * @return the ReadCommittedNode wrapper, or null if the node does not exist.
     */
    @SuppressWarnings("unchecked")
-   private ReadCommittedNode wrapAndPutInContext(InvocationContext ctx, Fqn fqn, boolean forUpdate)
-   {
+   private ReadCommittedNode wrapAndPutInContext(InvocationContext ctx, Fqn fqn, boolean forUpdate) {
       ReadCommittedNode node = (ReadCommittedNode) ctx.lookUpNode(fqn);
-      if (node == null)
-      {
+      if (node == null) {
          InternalNode[] nodes = dataContainer.peekInternalNodeAndDirectParent(fqn, false);
          node = nodeFactory.createWrappedNode(nodes[0], nodes[1]);
          ctx.putLookedUpNode(fqn, node);
@@ -450,14 +416,13 @@
    }
 
    /**
-    * An overloaded version of {@link #isParentLockNeeded(org.jboss.cache.Fqn, org.jboss.cache.InvocationContext)}
-    * which takes in an {@link org.jboss.cache.InternalNode} instead of a {@link Fqn}.
+    * An overloaded version of {@link #isParentLockNeeded(org.jboss.cache.Fqn, org.jboss.cache.InvocationContext)} which
+    * takes in an {@link org.jboss.cache.InternalNode} instead of a {@link Fqn}.
     *
     * @param parent parent node to test
     * @return true if parent lock is needed, false otherwise.
     */
-   private boolean isParentLockNeeded(InternalNode parent)
-   {
+   private boolean isParentLockNeeded(InternalNode parent) {
       return lockParentForChildInsertRemove || (parent != null && parent.isLockForChildInsertRemove());
    }
 
@@ -468,8 +433,7 @@
     * @param ctx    invocation context
     * @return true if parent lock is needed, false otherwise.
     */
-   private boolean isParentLockNeeded(Fqn parent, InvocationContext ctx)
-   {
+   private boolean isParentLockNeeded(Fqn parent, InvocationContext ctx) {
       ReadCommittedNode parentNodeTmp = (ReadCommittedNode) ctx.lookUpNode(parent);
       InternalNode in = parentNodeTmp == null ? dataContainer.peekInternalNode(parent, true) : parentNodeTmp.getDelegationTarget();
       return isParentLockNeeded(in);

Added: core/branches/3.0.X/src/test/java/org/jboss/cache/api/ReAddDeletedNodeTest.java
===================================================================
--- core/branches/3.0.X/src/test/java/org/jboss/cache/api/ReAddDeletedNodeTest.java	                        (rev 0)
+++ core/branches/3.0.X/src/test/java/org/jboss/cache/api/ReAddDeletedNodeTest.java	2009-02-19 13:07:43 UTC (rev 7732)
@@ -0,0 +1,40 @@
+package org.jboss.cache.api;
+
+import org.jboss.cache.AbstractSingleCacheTest;
+import org.jboss.cache.CacheSPI;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.UnitTestCacheFactory;
+import org.testng.annotations.Test;
+
+import javax.transaction.TransactionManager;
+
+ at Test(groups = {"functional"}, sequential = true, testName = "api.ReAddDeletedNodeTest")
+public class ReAddDeletedNodeTest extends AbstractSingleCacheTest {
+   private CacheSPI<String, String> cache;
+
+   public CacheSPI createCache() {
+      // start a single cache instance
+      UnitTestCacheFactory<String, String> cf = new UnitTestCacheFactory<String, String>();
+      cache = (CacheSPI<String, String>) cf.createCache("configs/local-tx.xml", false, getClass());
+      cache.getConfiguration().setEvictionConfig(null);
+      cache.start();
+      return cache;
+   }
+
+   public void testReAdd() throws Exception {
+      TransactionManager tm = cache.getTransactionManager();
+      Fqn<String> testFqn = Fqn.fromElements("a", "a", "a");
+
+      tm.begin();
+      cache.put(testFqn, "x", "x");
+      cache.removeNode(testFqn.getParent());
+      cache.put(testFqn, "x", "x");
+      assert cache.getNode(testFqn) != null : testFqn + " should not be null (before commit)";
+      assert cache.getNode(testFqn.getParent()) != null : testFqn.getParent() + " should not be null (before commit)";
+      assert cache.getNode(testFqn.getParent().getParent()) != null : testFqn.getParent().getParent() + " should not be null (before commit)";
+      tm.commit();
+      assert cache.getNode(testFqn) != null : testFqn + " should not be null (after commit)";
+      assert cache.getNode(testFqn.getParent()) != null : testFqn.getParent() + " should not be null (after commit)";
+      assert cache.getNode(testFqn.getParent().getParent()) != null : testFqn.getParent().getParent() + " should not be null (after commit)";
+   }
+}




More information about the jbosscache-commits mailing list