[jbosscache-commits] JBoss Cache SVN: r4646 - in core/trunk/src: main/java/org/jboss/cache/interceptors and 2 other directories.

jbosscache-commits at lists.jboss.org jbosscache-commits at lists.jboss.org
Thu Oct 18 21:22:34 EDT 2007


Author: mircea.markus
Date: 2007-10-18 21:22:34 -0400 (Thu, 18 Oct 2007)
New Revision: 4646

Added:
   core/trunk/src/test/java/org/jboss/cache/api/StructuralNodesTest.java
Modified:
   core/trunk/src/main/java/org/jboss/cache/AbstractNode.java
   core/trunk/src/main/java/org/jboss/cache/Node.java
   core/trunk/src/main/java/org/jboss/cache/NodeSPI.java
   core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java
   core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.java
   core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.java
   core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java
   core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java
Log:
JBCACHE-1153 - structural nodes

Modified: core/trunk/src/main/java/org/jboss/cache/AbstractNode.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/AbstractNode.java	2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/AbstractNode.java	2007-10-19 01:22:34 UTC (rev 4646)
@@ -16,6 +16,7 @@
    protected Map<Object, Node<K, V>> children;
    protected Fqn fqn;
    protected boolean resident;
+   protected boolean structural;
 
    public boolean isDeleted()
    {
@@ -54,6 +55,10 @@
    }
 
 
+   public boolean isStructural()
+   {
+      return structural;
+   }
 
    public boolean equals(Object another)
    {

Modified: core/trunk/src/main/java/org/jboss/cache/Node.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/Node.java	2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/Node.java	2007-10-19 01:22:34 UTC (rev 4646)
@@ -292,6 +292,17 @@
     */
    void setResident(boolean resident);
 
+    /**
+     * Structural nodes are the nodes created to support other nodes that hold data. E.g. the operation
+     * cache.put("/a/b/c","key","value") the nodes "/a" and "/a/b" are created only for supporting the existence of
+     * "/a/b/c" which holds data -"/a" and "/a/b" are structural nodes.
+     * A node is no longer considered structural when it's attribut map is changed, i.e. attributes are added.
+     * Structural nodes are not considered for viction, i.e. they are marked as resident. When a node moves to
+     * non-structural state it will automatically be marked as non-resident. Structural nodes might be marked as
+     * non-resident and considered for eviction. This property is internally computed (as described bellow) and is not replicated.
+     */
+   boolean isStructural();
+
    /**
     * Tests whether this node is configured to be exclusively locked when inserting or removing children.
     * <p />

Modified: core/trunk/src/main/java/org/jboss/cache/NodeSPI.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/NodeSPI.java	2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/NodeSPI.java	2007-10-19 01:22:34 UTC (rev 4646)
@@ -437,4 +437,11 @@
     * @return true if the node has one or more child nodes; false otherwise.
     */
    boolean hasChildrenDirect();
+
+   /**
+    * It is internally decided (i.e. wthin the cache, one cannot specify it from the outside)
+    * whether a node is structural or not - so this method fits better here than in Node interface.
+    * @see org.jboss.cache.Node#isStructural()
+    */
+   void setStructural(boolean structural);
 }

Modified: core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java	2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java	2007-10-19 01:22:34 UTC (rev 4646)
@@ -754,4 +754,10 @@
    {
       this.lockForChildInsertRemove = lockForChildInsertRemove;
    }
+
+   public void setStructural(boolean structural)
+   {
+      this.setResident(structural);
+      this.structural = structural;
+   }
 }

Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.java	2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.java	2007-10-19 01:22:34 UTC (rev 4646)
@@ -181,6 +181,7 @@
                if (isTargetFqn && !workspace.isVersioningImplicit()) versionToPassIn = version;
 
                NodeSPI newUnderlyingChildNode = workspaceNode.createChild(childName, workspaceNode.getNode(), cache, versionToPassIn);
+               newUnderlyingChildNode.setStructural(true);
 
                // now assign "workspaceNode" to the new child created.
                workspaceNode = nodeFactory.createWorkspaceNode(newUnderlyingChildNode, workspace);

Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.java	2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.java	2007-10-19 01:22:34 UTC (rev 4646)
@@ -211,6 +211,7 @@
                Map mergedData = workspaceNode.getMergedData();
                underlyingNode.clearDataDirect();
                underlyingNode.putAllDirect(mergedData);
+               underlyingNode.setStructural(workspaceNode.isStructural());
                updateVersion = true;
             }
 

Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java	2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java	2007-10-19 01:22:34 UTC (rev 4646)
@@ -78,6 +78,7 @@
             case MethodDeclarations.putKeyValMethodLocal_id:
                log.trace("Creating nodes if necessary");
                createNodes((Fqn) args[1], ctx.getGlobalTransaction());
+               markNodeNonStructural(ctx);
                break;
          }
 
@@ -194,6 +195,14 @@
             log.trace("bypassed locking as method " + m.getName() + "() doesn't require locking");
          }
       }
+      //this should be executed before calling eviction code/super.invoke()
+      //else, all nodes would be seen as resident by the eviction interceptor
+      if (m.getMethodId() == MethodDeclarations.putDataMethodLocal_id ||
+            m.getMethodId() == MethodDeclarations.putDataEraseMethodLocal_id ||
+            m.getMethodId() == MethodDeclarations.putKeyValMethodLocal_id)
+      {
+         this.markNodeNonStructural(ctx);
+      }
       if (m.getMethodId() == MethodDeclarations.lockMethodLocal_id)
       {
          return null;
@@ -227,6 +236,19 @@
       return o;
    }
 
+   private void markNodeNonStructural(InvocationContext ctx)
+   {
+      MethodCall m = ctx.getMethodCall();
+      Object[] args = m.getArgs();
+      Fqn fqn = (Fqn) args[1];
+      NodeSPI nodeSpi = cache.getRoot().getChildDirect(fqn);
+      if (nodeSpi == null)
+      {
+         throw new IllegalStateException("Could not add attribute to a NULL node!");
+      }
+      nodeSpi.setStructural(false);
+   }
+
    private long getLockAcquisitionTimeout(InvocationContext ctx)
    {
       long timeout = lock_acquisition_timeout;
@@ -307,6 +329,7 @@
          if (child_node == null && createIfNotExists)
          {
             child_node = n.addChildDirect(new Fqn(child_name));
+            child_node.setStructural(true);
             created = true;
          }
 
@@ -467,7 +490,11 @@
          Fqn childFqn = new Fqn(child_name);
 
          NodeSPI child_node = n.getChildDirect(childFqn);
-         if (child_node == null) child_node = n.addChildDirect(childFqn);
+         if (child_node == null)
+         {
+            child_node = n.addChildDirect(childFqn);
+            child_node.setStructural(true);
+         }
          // test if this node needs to be 'undeleted'
          // reverse the "remove" if the node has been previously removed in the same tx, if this operation is a put()
          if (gtx != null && needToReverseRemove(child_node, tx_table.get(gtx), NodeLock.LockType.WRITE, false, true))

Modified: core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java	2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java	2007-10-19 01:22:34 UTC (rev 4646)
@@ -103,6 +103,7 @@
    public void putAll(Map<K, V> data)
    {
       realPut(data, false);
+      this.structural = false;
       modified = true;
    }
 
@@ -110,11 +111,13 @@
    {
       clearData();
       putAll(data);
+      this.structural = false;
    }
 
    public V put(K key, V value)
    {
       modified = true;
+      this.structural = false;
       return optimisticDataMap.put(key, value);
 
    }

Added: core/trunk/src/test/java/org/jboss/cache/api/StructuralNodesTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/api/StructuralNodesTest.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/api/StructuralNodesTest.java	2007-10-19 01:22:34 UTC (rev 4646)
@@ -0,0 +1,143 @@
+package org.jboss.cache.api;
+
+import org.jboss.cache.CacheImpl;
+import org.jboss.cache.DefaultCacheFactory;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.misc.TestingUtil;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import org.testng.annotations.Test;
+import org.testng.annotations.AfterMethod;
+
+import javax.transaction.TransactionManager;
+import javax.transaction.SystemException;
+import javax.transaction.NotSupportedException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Testser class for strctural nodes functionality.
+ *
+ * @author <a href="mailto:mircea.markus at jboss.com">Mircea Markus</a>
+ * @since 2.1.0
+ */
+ at Test(groups = {"functional"})
+public class StructuralNodesTest
+{
+   private CacheImpl<Object, Object> cache;
+
+
+   @AfterMethod(alwaysRun = true)
+   public void closeCache()
+   {
+      cache.remove("/");
+      cache.stop();
+   }
+
+   /**
+    * Tests that when creaitng a node its parents are marked by default as structural, and are by default resident.
+    * When modifying the attribute map, the node is not structural anymore and also is not resident.
+    */
+   public void testHappyFlowPessimistic()
+   {
+      Configuration cacheConfig = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, false);
+      cache = (CacheImpl<Object, Object>) DefaultCacheFactory.getInstance().createCache(cacheConfig, true);
+      cache.put("/a/b/c/d/e","k","v");
+
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/a")).isStructural());
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/a")).isResident());
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b")).isStructural());
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident());
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b/c")).isStructural());
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("a/b/c")).isResident());
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("a/b/c/d")).isStructural());
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("a/b/c/d")).isResident());
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("a/b/c/d/e")).isStructural());
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("a/b/c/d/e")).isResident());
+
+      //now add some attributes to some nodes - this should make them both non-structural and non-resident
+      Map data = new HashMap();
+      cache.put("/a", data);
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/a")).isStructural());
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/a")).isResident());
+   }
+
+
+   /**
+    * There is a bunch of method calls that will make the structural nodes not structural.
+    * This tests that for all those method calls the 'structural' flag is set to false.
+    */
+   public void testMakeNonStructural()
+   {
+      Configuration cacheConfig = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, false);
+      cache = (CacheImpl<Object, Object>) DefaultCacheFactory.getInstance().createCache(cacheConfig, true);
+
+
+
+      cache.put("/put/putIfAbsent/putAll/test","k","v");
+      
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/put")).isStructural());
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent")).isStructural());
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent/putAll")).isStructural());
+
+      cache.getRoot().getChild(Fqn.fromString("/put")).put("k","v");
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/put")).isStructural());
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/put")).isResident());
+
+      cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent")).putIfAbsent("k","v");
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent")).isStructural());
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent")).isResident());
+
+      cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent/putAll")).putAll(new HashMap<Object,Object>());
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent/putAll")).isStructural());
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent/putAll")).isResident());
+
+   }
+
+   /**
+    * When we use optimisic locking, check that nodes made non-structural during transaction are non-structural after commiting.
+    */
+   public void testOptmisticLockingWithCommit() throws Exception
+   {
+      Configuration config = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, false);
+      config.setNodeLockingOptimistic(true);
+      cache = (CacheImpl<Object, Object>) DefaultCacheFactory.getInstance().createCache(config, true);
+      cache.put("/a/b/c","k","v");
+
+      TransactionManager txManager = cache.getTransactionManager();
+      txManager.begin();
+      cache.getRoot().getChild(Fqn.fromString("/a/b")).put("key","value");
+      txManager.commit();
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/a")).isStructural());
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/a")).isResident());
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b")).isStructural());
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident());
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b/c")).isStructural());
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b/c")).isResident());
+   }
+
+   /**
+    * When we use optimisic locking, check that nodes made non-structural during transaction are structural after rollback.
+    */
+   public void testOptmisticLockingWithRollback() throws Exception
+   {
+      Configuration config = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, false);
+      config.setNodeLockingOptimistic(true);
+      cache = (CacheImpl<Object, Object>) DefaultCacheFactory.getInstance().createCache(config, true);
+      cache.put("/a/b/c","k","v");
+
+      TransactionManager txManager = cache.getTransactionManager();
+      txManager.begin();
+      cache.getRoot().getChild(Fqn.fromString("/a/b")).put("key","value");
+      txManager.rollback();
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/a")).isStructural());
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/a")).isResident());
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b")).isStructural());
+      assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident());
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b/c")).isStructural());
+      assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b/c")).isResident());
+   }
+
+}




More information about the jbosscache-commits mailing list