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) {