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 mr...@apache.org on 2015/05/07 15:55:53 UTC

svn commit: r1678211 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/document/ test/java/org/apache/jackrabbit/oak/plugins/document/

Author: mreutegg
Date: Thu May  7 13:55:53 2015
New Revision: 1678211

URL: http://svn.apache.org/r1678211
Log:
OAK-2850: Flag states from revision of an external change

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java?rev=1678211&r1=1678210&r2=1678211&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java Thu May  7 13:55:53 2015
@@ -83,6 +83,7 @@ public class DocumentNodeState extends A
     final Revision rev;
     Revision lastRevision;
     final Revision rootRevision;
+    final boolean fromExternalChange;
     final Map<String, PropertyState> properties;
     final boolean hasChildren;
 
@@ -97,7 +98,7 @@ public class DocumentNodeState extends A
     DocumentNodeState(@Nonnull DocumentNodeStore store, @Nonnull String path,
                       @Nonnull Revision rev, boolean hasChildren) {
         this(store, path, rev, new HashMap<String, PropertyState>(),
-                hasChildren, null, null);
+                hasChildren, null, null, false);
     }
 
     private DocumentNodeState(@Nonnull DocumentNodeStore store,
@@ -106,12 +107,14 @@ public class DocumentNodeState extends A
                               @Nonnull Map<String, PropertyState> properties,
                               boolean hasChildren,
                               @Nullable Revision lastRevision,
-                              @Nullable Revision rootRevision) {
+                              @Nullable Revision rootRevision,
+                              boolean fromExternalChange) {
         this.store = checkNotNull(store);
         this.path = checkNotNull(path);
         this.rev = checkNotNull(rev);
         this.lastRevision = lastRevision;
         this.rootRevision = rootRevision != null ? rootRevision : rev;
+        this.fromExternalChange = fromExternalChange;
         this.hasChildren = hasChildren;
         this.properties = checkNotNull(properties);
     }
@@ -120,20 +123,43 @@ public class DocumentNodeState extends A
      * Creates a copy of this {@code DocumentNodeState} with the
      * {@link #rootRevision} set to the given {@code root} revision. This method
      * returns {@code this} instance if the given {@code root} revision is
-     * the same as the one in this instance.
+     * the same as the one in this instance and the {@link #fromExternalChange}
+     * flags are equal.
      *
      * @param root the root revision for the copy of this node state.
-     * @return a copy of this node state with the given root revision.
+     * @param externalChange if the {@link #fromExternalChange} flag must be
+     *                       set on the returned node state.
+     * @return a copy of this node state with the given root revision and
+     *          external change flag.
      */
-    DocumentNodeState withRootRevision(@Nonnull Revision root) {
-        if (rootRevision.equals(root)) {
+    private DocumentNodeState withRootRevision(@Nonnull Revision root,
+                                               boolean externalChange) {
+        if (rootRevision.equals(root) && fromExternalChange == externalChange) {
             return this;
         } else {
             return new DocumentNodeState(store, path, rev, properties,
-                    hasChildren, lastRevision, root);
+                    hasChildren, lastRevision, root, externalChange);
         }
     }
 
+    /**
+     * @return a copy of this {@code DocumentNodeState} with the
+     *          {@link #fromExternalChange} flag set to {@code true}.
+     */
+    @Nonnull
+    DocumentNodeState fromExternalChange() {
+        return new DocumentNodeState(store, path, rev, properties, hasChildren,
+                lastRevision, rootRevision, true);
+    }
+
+    /**
+     * @return {@code true} if this node state was created as a result of an
+     *          external change; {@code false} otherwise.
+     */
+    boolean isFromExternalChange() {
+        return fromExternalChange;
+    }
+
     @Nonnull
     Revision getRevision() {
         return rev;
@@ -228,7 +254,7 @@ public class DocumentNodeState extends A
             checkValidName(name);
             return EmptyNodeState.MISSING_NODE;
         } else {
-            return child.withRootRevision(rootRevision);
+            return child.withRootRevision(rootRevision, fromExternalChange);
         }
     }
 
@@ -485,7 +511,7 @@ public class DocumentNodeState extends A
                     @Nonnull
                     @Override
                     public NodeState getNodeState() {
-                        return input.withRootRevision(rootRevision);
+                        return input.withRootRevision(rootRevision, fromExternalChange);
                     }
                 };
             }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java?rev=1678211&r1=1678210&r2=1678211&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java Thu May  7 13:55:53 2015
@@ -1768,7 +1768,7 @@ public final class DocumentNodeStore
                 // the new head revision is after other revisions
                 setHeadRevision(newRevision());
                 if (dispatchChange) {
-                    dispatcher.contentChanged(getRoot(), null);
+                    dispatcher.contentChanged(getRoot().fromExternalChange(), null);
                 }
             } finally {
                 backgroundOperationLock.writeLock().unlock();

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java?rev=1678211&r1=1678210&r2=1678211&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java Thu May  7 13:55:53 2015
@@ -25,17 +25,30 @@ import static org.junit.Assert.assertTru
 import static org.junit.Assert.fail;
 
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.mongodb.DB;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore;
 import org.apache.jackrabbit.oak.spi.blob.BlobStore;
 import org.apache.jackrabbit.oak.spi.blob.MemoryBlobStore;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
+import org.apache.jackrabbit.oak.spi.commit.Observer;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.DefaultNodeStateDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.json.simple.JSONObject;
 import org.json.simple.parser.JSONParser;
 import org.junit.After;
@@ -50,24 +63,15 @@ public class ClusterTest {
     private static final boolean MONGO_DB = false;
     // private static final boolean MONGO_DB = true;
 
+    private List<DocumentMK> mks = Lists.newArrayList();
     private MemoryDocumentStore ds;
     private MemoryBlobStore bs;
 
     @Test
     public void threeNodes() throws Exception {
-        MemoryDocumentStore ds = new MemoryDocumentStore();
-        MemoryBlobStore bs = new MemoryBlobStore();
-        DocumentMK.Builder builder;
-
-        builder = new DocumentMK.Builder();
-        builder.setDocumentStore(ds).setBlobStore(bs).setAsyncDelay(0);
-        DocumentMK mk1 = builder.setClusterId(1).open();
-        builder = new DocumentMK.Builder();
-        builder.setDocumentStore(ds).setBlobStore(bs).setAsyncDelay(0);
-        DocumentMK mk2 = builder.setClusterId(2).open();
-        builder = new DocumentMK.Builder();
-        builder.setDocumentStore(ds).setBlobStore(bs).setAsyncDelay(0);
-        DocumentMK mk3 = builder.setClusterId(3).open();
+        DocumentMK mk1 = createMK(1, 0);
+        DocumentMK mk2 = createMK(2, 0);
+        DocumentMK mk3 = createMK(3, 0);
 
         mk1.commit("/", "+\"test\":{}", null, null);
         mk2.commit("/", "+\"a\":{}", null, null);
@@ -114,10 +118,6 @@ public class ClusterTest {
         assertEquals(1L, obj.get("x"));
         assertEquals(2L, obj.get("y"));
         assertEquals(0L, obj.get(":childNodeCount"));
-
-        mk1.dispose();
-        mk2.dispose();
-        mk3.dispose();
     }
 
     @Test
@@ -161,12 +161,6 @@ public class ClusterTest {
         DocumentMK mk5 = createMK(5, 0, ds, bs);
         mk5.commit("/", "-\"a\"", null, null);
         mk5.commit("/", "+\"a\": {}", null, null);
-
-        mk1.dispose();
-        mk2.dispose();
-        mk3.dispose();
-        mk4.dispose();
-        mk5.dispose();
     }
 
     @Test
@@ -175,8 +169,6 @@ public class ClusterTest {
         DocumentMK mk2 = createMK(0);
         assertEquals(1, mk1.getClusterInfo().getId());
         assertEquals(2, mk2.getClusterInfo().getId());
-        mk1.dispose();
-        mk2.dispose();
     }
 
     @Test
@@ -191,13 +183,11 @@ public class ClusterTest {
         // mk1.merge only becomes visible to mk2 after async delay
         // therefore dispose mk1 now to make sure it flushes
         // unsaved last revisions
-        mk1.dispose();
+        disposeMK(mk1);
 
         DocumentMK mk2 = createMK(2);
         String nodes = mk2.getNodes("/", null, 0, 0, 100, null);
         assertEquals("{\"branchVisible\":{},\"regular\":{},\":childNodeCount\":2}", nodes);
-
-        mk2.dispose();
     }
 
     /**
@@ -243,10 +233,6 @@ public class ClusterTest {
         assertEquals(1, diff.added.size());
         assertEquals(Sets.newHashSet("/mk3"), diff.added);
         assertEquals(new HashSet<String>(), diff.deleted);
-
-        mk1.dispose();
-        mk2.dispose();
-        mk3.dispose();
     }
 
     @Test
@@ -308,9 +294,6 @@ public class ClusterTest {
         String n1 = mk1.getNodes("/", mk1.getHeadRevision(), 0, 0, 10, null);
         String n2 = mk2.getNodes("/", mk2.getHeadRevision(), 0, 0, 10, null);
         assertEquals(n1, n2);
-
-        mk1.dispose();
-        mk2.dispose();
     }
 
     @Test
@@ -346,9 +329,6 @@ public class ClusterTest {
         // so now it should be available
         m2h = mk2.getNodes("/", mk2.getHeadRevision(), 0, 0, 5, null);
         assertEquals("{\"test\":{},\":childNodeCount\":1}", m2h);
-
-        mk1.dispose();
-        mk2.dispose();
     }
 
     @Test
@@ -367,20 +347,68 @@ public class ClusterTest {
             // expected
         }
         mk2.commit("/", "+\"a\": {}", null, null);
+    }
 
-        mk1.dispose();
-        mk2.dispose();
+    @Test
+    public void fromExternalChange() throws Exception {
+        final List<DocumentNodeState> rootStates1 = Lists.newArrayList();
+        DocumentNodeStore ns1 = createMK(1, 0).getNodeStore();
+        ns1.addObserver(new Observer() {
+            @Override
+            public void contentChanged(@Nonnull NodeState root,
+                                       @Nullable CommitInfo info) {
+                rootStates1.add((DocumentNodeState) root);
+            }
+        });
+        final List<DocumentNodeState> rootStates2 = Lists.newArrayList();
+        DocumentNodeStore ns2 = createMK(2, 0).getNodeStore();
+        ns2.addObserver(new Observer() {
+            @Override
+            public void contentChanged(@Nonnull NodeState root,
+                                       @Nullable CommitInfo info) {
+                rootStates2.add((DocumentNodeState) root);
+            }
+        });
+        rootStates1.clear();
+        rootStates2.clear();
+
+        NodeBuilder builder = ns1.getRoot().builder();
+        builder.child("foo");
+        merge(ns1, builder);
+
+        assertEquals(1, rootStates1.size());
+        assertEquals(0, rootStates2.size());
+        assertFalse(rootStates1.get(0).isFromExternalChange());
+
+        ns1.runBackgroundOperations();
+        ns2.runBackgroundOperations();
+
+        assertEquals(1, rootStates1.size());
+        assertEquals(1, rootStates2.size());
+        assertTrue(rootStates2.get(0).isFromExternalChange());
+        NodeState foo = rootStates2.get(0).getChildNode("foo");
+        assertTrue(foo instanceof DocumentNodeState);
+        assertTrue(((DocumentNodeState) foo).isFromExternalChange());
     }
 
     @Before
     @After
     public void clear() {
+        for (DocumentMK mk : mks) {
+            mk.dispose();
+        }
+        mks.clear();
         if (MONGO_DB) {
             DB db = MongoUtils.getConnection().getDB();
             MongoUtils.dropCollections(db);
         }
     }
 
+    private static NodeState merge(NodeStore store, NodeBuilder builder)
+            throws CommitFailedException {
+        return store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+    }
+
     private DocumentMK createMK(int clusterId) {
         return createMK(clusterId, 10);
     }
@@ -388,8 +416,8 @@ public class ClusterTest {
     private DocumentMK createMK(int clusterId, int asyncDelay) {
         if (MONGO_DB) {
             DB db = MongoUtils.getConnection().getDB();
-            return new DocumentMK.Builder().setMongoDB(db)
-                    .setClusterId(clusterId).setAsyncDelay(asyncDelay).open();
+            return register(new DocumentMK.Builder().setMongoDB(db)
+                    .setClusterId(clusterId).setAsyncDelay(asyncDelay).open());
         } else {
             if (ds == null) {
                 ds = new MemoryDocumentStore();
@@ -403,8 +431,23 @@ public class ClusterTest {
 
     private DocumentMK createMK(int clusterId, int asyncDelay,
                              DocumentStore ds, BlobStore bs) {
-        return new DocumentMK.Builder().setDocumentStore(ds).setBlobStore(bs)
-                .setClusterId(clusterId).setAsyncDelay(asyncDelay).open();
+        return register(new DocumentMK.Builder().setDocumentStore(ds)
+                .setBlobStore(bs).setClusterId(clusterId)
+                .setAsyncDelay(asyncDelay).open());
+    }
+
+    private DocumentMK register(DocumentMK mk) {
+        mks.add(mk);
+        return mk;
+    }
+
+    private void disposeMK(DocumentMK mk) {
+        mk.dispose();
+        for (int i = 0; i < mks.size(); i++) {
+            if (mks.get(i) == mk) {
+                mks.remove(i);
+            }
+        }
     }
 
     private void traverse(NodeState node, String path) {