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/07/01 14:35:24 UTC

svn commit: r1688633 - in /jackrabbit/oak/branches/1.2: ./ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/

Author: mreutegg
Date: Wed Jul  1 12:35:23 2015
New Revision: 1688633

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

Merged revision 1678211 from trunk

Modified:
    jackrabbit/oak/branches/1.2/   (props changed)
    jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
    jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
    jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java

Propchange: jackrabbit/oak/branches/1.2/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Wed Jul  1 12:35:23 2015
@@ -1,3 +1,3 @@
 /jackrabbit/oak/branches/1.0:1665962
-/jackrabbit/oak/trunk:1672350,1672468,1672537,1672603,1672642,1672644,1672834-1672835,1673351,1673410,1673414-1673415,1673436,1673644,1673662-1673664,1673669,1673695,1673738,1673787,1673791,1674046,1674065,1674075,1674107,1674228,1674780,1674880,1675054-1675055,1675319,1675332,1675354,1675357,1675382,1675555,1675566,1675593,1676198,1676237,1676407,1676458,1676539,1676670,1676693,1676703,1676725,1677579,1677581,1677609,1677611,1677774,1677788,1677797,1677804,1677806,1677939,1677991,1678173,1678323,1678758,1678938,1678954,1679144,1679165,1679191,1679232,1679235,1679503,1679958,1679961,1680182,1680222,1680232,1680236,1680461,1680633,1680643,1680747,1680805-1680806,1680903,1681282,1681767,1681918,1682218,1682235,1682437,1682494,1682555,1682855,1682904,1683059,1683089,1683213,1683249,1683259,1683278,1683323,1683687,1683700,1684174-1684175,1684186,1684376,1684442,1684561,1684570,1684601,1684618,1684868,1685023,1685370,1685552,1685589,1685840,1685999,1686097,1686162,1686229,1686234,1686253
 ,1686414,1686780,1686854,1686857,1686971,1687053-1687055,1687198,1687220,1687239-1687240,1687301,1687441,1687553,1688089-1688090,1688172,1688349,1688421,1688436,1688453,1688616,1688622
+/jackrabbit/oak/trunk:1672350,1672468,1672537,1672603,1672642,1672644,1672834-1672835,1673351,1673410,1673414-1673415,1673436,1673644,1673662-1673664,1673669,1673695,1673738,1673787,1673791,1674046,1674065,1674075,1674107,1674228,1674780,1674880,1675054-1675055,1675319,1675332,1675354,1675357,1675382,1675555,1675566,1675593,1676198,1676237,1676407,1676458,1676539,1676670,1676693,1676703,1676725,1677579,1677581,1677609,1677611,1677774,1677788,1677797,1677804,1677806,1677939,1677991,1678173,1678211,1678323,1678758,1678938,1678954,1679144,1679165,1679191,1679232,1679235,1679503,1679958,1679961,1680182,1680222,1680232,1680236,1680461,1680633,1680643,1680747,1680805-1680806,1680903,1681282,1681767,1681918,1682218,1682235,1682437,1682494,1682555,1682855,1682904,1683059,1683089,1683213,1683249,1683259,1683278,1683323,1683687,1683700,1684174-1684175,1684186,1684376,1684442,1684561,1684570,1684601,1684618,1684868,1685023,1685370,1685552,1685589,1685840,1685999,1686097,1686162,1686229,1686234
 ,1686253,1686414,1686780,1686854,1686857,1686971,1687053-1687055,1687198,1687220,1687239-1687240,1687301,1687441,1687553,1688089-1688090,1688172,1688349,1688421,1688436,1688453,1688616,1688622
 /jackrabbit/trunk:1345480

Modified: jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java?rev=1688633&r1=1688632&r2=1688633&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java (original)
+++ jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java Wed Jul  1 12:35:23 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);
         }
     }
 
@@ -486,7 +512,7 @@ public class DocumentNodeState extends A
                     @Nonnull
                     @Override
                     public NodeState getNodeState() {
-                        return input.withRootRevision(rootRevision);
+                        return input.withRootRevision(rootRevision, fromExternalChange);
                     }
                 };
             }

Modified: jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java?rev=1688633&r1=1688632&r2=1688633&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java (original)
+++ jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java Wed Jul  1 12:35:23 2015
@@ -1782,7 +1782,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/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java?rev=1688633&r1=1688632&r2=1688633&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java (original)
+++ jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java Wed Jul  1 12:35:23 2015
@@ -25,18 +25,31 @@ 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.mk.api.MicroKernelException;
+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;
@@ -51,24 +64,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);
@@ -115,10 +119,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
@@ -162,12 +162,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
@@ -176,8 +170,6 @@ public class ClusterTest {
         DocumentMK mk2 = createMK(0);
         assertEquals(1, mk1.getClusterInfo().getId());
         assertEquals(2, mk2.getClusterInfo().getId());
-        mk1.dispose();
-        mk2.dispose();
     }
 
     @Test
@@ -192,13 +184,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();
     }
 
     /**
@@ -244,10 +234,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
@@ -309,9 +295,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
@@ -347,9 +330,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
@@ -368,20 +348,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);
     }
@@ -389,8 +417,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();
@@ -404,8 +432,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) {