You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ju...@apache.org on 2012/10/20 23:38:25 UTC

svn commit: r1400521 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java test/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilderTest.java

Author: jukka
Date: Sat Oct 20 21:38:25 2012
New Revision: 1400521

URL: http://svn.apache.org/viewvc?rev=1400521&view=rev
Log:
OAK-170: Child node state builder

Improved state tracking in node builders, including internal
revision counters to optimize the common case where no changes
have been made.

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilderTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java?rev=1400521&r1=1400520&r2=1400521&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java Sat Oct 20 21:38:25 2012
@@ -76,16 +76,28 @@ public class MemoryNodeBuilder implement
             ImmutableMap.<String, NodeState>of());
 
     /**
-     * Parent state builder reference, or {@code null} once this builder
-     * has been connected.
+     * Parent builder, or {@code null} for a root builder.
      */
-    private MemoryNodeBuilder parent;
+    private final MemoryNodeBuilder parent;
 
     /**
-     * Name of this child node within the parent builder, or {@code null}
-     * once this builder has been connected.
+     * Name of this child node within the parent builder,
+     * or {@code null} for a root builder.
      */
-    private String name;
+    private final String name;
+
+    /**
+     * Root builder, or {@code this} for the root itself.
+     */
+    private final MemoryNodeBuilder root;
+
+    /**
+     * Internal revision counter that is incremented in the root builder
+     * whenever anything changes in the tree below. Each builder instance
+     * has its own copy of the revision counter, for quickly checking whether
+     * any state changes are needed.
+     */
+    private long revision;
 
     /**
      * The read state of this builder. Originally the immutable base state
@@ -111,6 +123,10 @@ public class MemoryNodeBuilder implement
             MemoryNodeBuilder parent, String name, NodeState base) {
         this.parent = checkNotNull(parent);
         this.name = checkNotNull(name);
+
+        this.root = parent.root;
+        this.revision = parent.revision;
+
         this.readState = checkNotNull(base);
         this.writeState = null;
     }
@@ -123,51 +139,60 @@ public class MemoryNodeBuilder implement
     public MemoryNodeBuilder(NodeState base) {
         this.parent = null;
         this.name = null;
+
+        this.root = this;
+        this.revision = 0;
+
         MutableNodeState mstate = new MutableNodeState(checkNotNull(base));
         this.readState = mstate;
         this.writeState = mstate;
     }
 
     private NodeState read() {
-        if (writeState == null) {
+        if (revision != root.revision) {
+            assert(parent != null); // root never gets here
             parent.read();
             MutableNodeState pstate = parent.writeState;
             if (pstate != null) {
                 MutableNodeState mstate = pstate.nodes.get(name);
-                if (mstate == null && pstate.nodes.containsKey(name)) {
-                    throw new IllegalStateException(
-                            "This builder refers to a node that has"
-                            + " been removed from its parent.");
-                }
-                if (mstate != null) {
-                    parent = null;
-                    name = null;
+                if (mstate == null) {
+                    if (pstate.nodes.containsKey(name)) {
+                        throw new IllegalStateException(
+                                "This node has been removed.");
+                    }
+                } else if (mstate != writeState) {
                     readState = mstate;
                     writeState = mstate;
                 }
             }
+            revision = root.revision;
         }
         return readState;
     }
 
     private MutableNodeState write() {
-        if (writeState == null) {
-            MutableNodeState pstate = parent.write();
+        return write(root.revision + 1);
+    }
+
+    private MutableNodeState write(long newRevision) {
+        if (writeState == null || revision != root.revision) {
+            assert(parent != null); // root never gets here
+            MutableNodeState pstate = parent.write(newRevision);
             MutableNodeState mstate = pstate.nodes.get(name);
             if (mstate == null) {
                 if (pstate.nodes.containsKey(name)) {
                     throw new IllegalStateException(
-                            "This builder refers to a node that has"
-                            + " been removed from its parent.");
+                            "This node has been removed.");
                 }
                 mstate = new MutableNodeState(readState);
                 pstate.nodes.put(name, mstate);
             }
-            parent = null;
-            name = null;
-            readState = mstate;
-            writeState = mstate;
+            if (mstate != writeState) {
+                readState = mstate;
+                writeState = mstate;
+            }
         }
+        revision = newRevision;
         return writeState;
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilderTest.java?rev=1400521&r1=1400520&r2=1400521&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilderTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilderTest.java Sat Oct 20 21:38:25 2012
@@ -60,10 +60,10 @@ public class MemoryNodeBuilderTest {
         NodeBuilder childB = root.child("x");
 
         childB.setProperty("test", "foo");
+
         childA.setProperty("test", "bar");
-        assertEquals(
-                "bar",
-                childB.getProperty("test").getValue(STRING));
+        assertEquals("bar", childA.getProperty("test").getValue(STRING));
+        assertEquals("bar", childB.getProperty("test").getValue(STRING));
     }
 
     @Test
@@ -73,8 +73,14 @@ public class MemoryNodeBuilderTest {
         NodeBuilder childB = root.child("x");
 
         childB.setProperty("test", "foo");
+
         childA.removeProperty("test");
+        assertNull(childA.getProperty("test"));
         assertNull(childB.getProperty("test"));
+
+        childA.setProperty("test", "bar");
+        assertEquals("bar", childA.getProperty("test").getValue(STRING));
+        assertEquals("bar", childB.getProperty("test").getValue(STRING));
     }
 
     @Test
@@ -84,18 +90,28 @@ public class MemoryNodeBuilderTest {
         NodeBuilder childB = root.child("x");
 
         assertFalse(childA.hasChildNode("test"));
+        assertFalse(childB.hasChildNode("test"));
+
         childB.child("test");
         assertTrue(childA.hasChildNode("test"));
+        assertTrue(childB.hasChildNode("test"));
     }
 
-    @Test(expected = IllegalStateException.class)
+    @Test
     public void testConnectOnRemoveNode() {
         NodeBuilder root = new MemoryNodeBuilder(BASE);
         NodeBuilder child = root.child("x");
 
         root.removeNode("x");
-        child.getChildNodeCount(); // should throw ISE
-        fail();
+        try {
+            child.getChildNodeCount();
+            fail();
+        } catch (IllegalStateException e) {
+            // expected
+        }
+
+        root.child("x");
+        assertEquals(0, child.getChildNodeCount()); // reconnect!
     }
 
 }