Author: stillalex
Date: Tue Jul 25 12:17:46 2017
New Revision: 1802922
URL: http://svn.apache.org/viewvc?rev=1802922&view=rev
Log:
OAK-6491 Add JCR_CREATED support to JcrLastModifiedConflictHandler
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandlerOursTest.java
(with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandlerTheirsTest.java
(with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/JcrLastModifiedConflictHandlerTest.java
(with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/ThreeWayConflictHandlerTest.java
(with props)
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrConflictHandler.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrLastModifiedConflictHandler.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrConflictHandler.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrConflictHandler.java?rev=1802922&r1=1802921&r2=1802922&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrConflictHandler.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrConflictHandler.java
Tue Jul 25 12:17:46 2017
@@ -35,7 +35,7 @@ public final class JcrConflictHandler {
*/
public static CompositeConflictHandler createJcrConflictHandler() {
return new CompositeConflictHandler(ImmutableList.of(
- ConflictHandlers.wrap(new JcrLastModifiedConflictHandler()),
+ new JcrLastModifiedConflictHandler(),
ConflictHandlers.wrap(new ChildOrderConflictHandler()),
new AnnotatingConflictHandler()));
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrLastModifiedConflictHandler.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrLastModifiedConflictHandler.java?rev=1802922&r1=1802921&r2=1802922&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrLastModifiedConflictHandler.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrLastModifiedConflictHandler.java
Tue Jul 25 12:17:46 2017
@@ -20,86 +20,70 @@
package org.apache.jackrabbit.oak.plugins.commit;
import static org.apache.jackrabbit.JcrConstants.JCR_LASTMODIFIED;
+import static org.apache.jackrabbit.JcrConstants.JCR_CREATED;
import static org.apache.jackrabbit.util.ISO8601.parse;
import java.util.Calendar;
+import javax.annotation.Nonnull;
+
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.spi.commit.PartialConflictHandler;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
-import org.apache.jackrabbit.oak.spi.state.NodeState;
-public class JcrLastModifiedConflictHandler implements PartialConflictHandler {
+/**
+ * Conflict Handler that merges concurrent updates to
+ * {@code org.apache.jackrabbit.JcrConstants.JCR_LASTMODIFIED} by picking the
+ * older of the 2 conflicting dates and
+ * {@code org.apache.jackrabbit.JcrConstants.JCR_CREATED} by picking the newer
+ * of the 2 conflicting dates.
+ */
+public class JcrLastModifiedConflictHandler extends
DefaultThreeWayConflictHandler {
+
+ public JcrLastModifiedConflictHandler() {
+ super(Resolution.IGNORED);
+ }
+ @Nonnull
@Override
- public Resolution addExistingProperty(NodeBuilder parent, PropertyState
ours,
- PropertyState theirs) {
- if (isLastModified(ours)) {
+ public Resolution addExistingProperty(NodeBuilder parent, PropertyState
ours, PropertyState theirs) {
+ if (isModifiedOrCreated(ours.getName())) {
merge(parent, ours, theirs);
return Resolution.MERGED;
}
- return null;
- }
-
- @Override
- public Resolution changeDeletedProperty(NodeBuilder parent, PropertyState
ours) {
- return null;
+ return Resolution.IGNORED;
}
+ @Nonnull
@Override
- public Resolution changeChangedProperty(NodeBuilder parent, PropertyState
ours,
- PropertyState theirs) {
- if (isLastModified(ours)) {
+ public Resolution changeChangedProperty(NodeBuilder parent, PropertyState
ours, PropertyState theirs,
+ PropertyState base) {
+ if (isModifiedOrCreated(ours.getName())) {
merge(parent, ours, theirs);
return Resolution.MERGED;
}
- return null;
+ return Resolution.IGNORED;
}
- private static void merge(NodeBuilder parent, PropertyState ours,
- PropertyState theirs) {
+ private static void merge(NodeBuilder parent, PropertyState ours,
PropertyState theirs) {
Calendar o = parse(ours.getValue(Type.DATE));
Calendar t = parse(theirs.getValue(Type.DATE));
- // pick & set newer one
- if (o.before(t)) {
- parent.setProperty(JCR_LASTMODIFIED, t);
+ if (JCR_CREATED.equals(ours.getName())) {
+ parent.setProperty(ours.getName(), pick(o, t, true));
} else {
- parent.setProperty(JCR_LASTMODIFIED, o);
+ parent.setProperty(ours.getName(), pick(o, t, false));
}
}
- private static boolean isLastModified(PropertyState p) {
- return JCR_LASTMODIFIED.equals(p.getName());
- }
-
- @Override
- public Resolution deleteDeletedProperty(NodeBuilder parent, PropertyState
ours) {
- return null;
- }
-
- @Override
- public Resolution deleteChangedProperty(NodeBuilder parent, PropertyState
theirs) {
- return null;
- }
-
- @Override
- public Resolution addExistingNode(NodeBuilder parent, String name,
NodeState ours, NodeState theirs) {
- return null;
- }
-
- @Override
- public Resolution changeDeletedNode(NodeBuilder parent, String name,
NodeState ours) {
- return null;
- }
-
- @Override
- public Resolution deleteChangedNode(NodeBuilder parent, String name,
NodeState theirs) {
- return null;
+ private static Calendar pick(Calendar a, Calendar b, boolean jcrCreated) {
+ if (a.before(b)) {
+ return jcrCreated ? a : b;
+ } else {
+ return jcrCreated ? b : a;
+ }
}
- @Override
- public Resolution deleteDeletedNode(NodeBuilder parent, String name) {
- return null;
+ private static boolean isModifiedOrCreated(String name) {
+ return JCR_LASTMODIFIED.equals(name) || JCR_CREATED.equals(name);
}
}
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandlerOursTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandlerOursTest.java?rev=1802922&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandlerOursTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandlerOursTest.java
Tue Jul 25 12:17:46 2017
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.commit;
+
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DefaultThreeWayConflictHandlerOursTest {
+
+ private static final String OUR_VALUE = "our value";
+ private static final String THEIR_VALUE = "their value";
+
+ private Root ourRoot;
+ private Root theirRoot;
+
+ @Before
+ public void setUp() throws CommitFailedException {
+ ContentSession session = new Oak()
+ .with(new OpenSecurityProvider())
+ .with(DefaultThreeWayConflictHandler.OURS)
+ .createContentSession();
+
+ // Add test content
+ Root root = session.getLatestRoot();
+ Tree tree = root.getTree("/");
+ tree.setProperty("a", 1);
+ tree.setProperty("b", 2);
+ tree.setProperty("c", 3);
+ tree.addChild("x");
+ tree.addChild("y");
+ tree.addChild("z");
+ root.commit();
+
+ ourRoot = session.getLatestRoot();
+ theirRoot = session.getLatestRoot();
+ }
+
+ @After
+ public void tearDown() {
+ ourRoot = null;
+ theirRoot = null;
+ }
+
+ @Test
+ public void testAddExistingProperty() throws CommitFailedException {
+ theirRoot.getTree("/").setProperty("p", THEIR_VALUE);
+ theirRoot.getTree("/").setProperty("q", THEIR_VALUE);
+ ourRoot.getTree("/").setProperty("p", OUR_VALUE);
+ ourRoot.getTree("/").setProperty("q", OUR_VALUE);
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ PropertyState p = ourRoot.getTree("/").getProperty("p");
+ assertNotNull(p);
+ assertEquals(OUR_VALUE, p.getValue(STRING));
+
+ PropertyState q = ourRoot.getTree("/").getProperty("q");
+ assertNotNull(q);
+ assertEquals(OUR_VALUE, p.getValue(STRING));
+ }
+
+ @Test
+ public void testChangeDeletedProperty() throws CommitFailedException {
+ theirRoot.getTree("/").removeProperty("a");
+ ourRoot.getTree("/").setProperty("a", OUR_VALUE);
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ PropertyState p = ourRoot.getTree("/").getProperty("a");
+ assertNotNull(p);
+ assertEquals(OUR_VALUE, p.getValue(STRING));
+ }
+
+ @Test
+ public void testChangeChangedProperty() throws CommitFailedException {
+ theirRoot.getTree("/").setProperty("a", THEIR_VALUE);
+ ourRoot.getTree("/").setProperty("a", OUR_VALUE);
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ PropertyState p = ourRoot.getTree("/").getProperty("a");
+ assertNotNull(p);
+ assertEquals(OUR_VALUE, p.getValue(STRING));
+ }
+
+ @Test
+ public void testDeleteDeletedProperty() throws CommitFailedException {
+ theirRoot.getTree("/").removeProperty("a");
+ ourRoot.getTree("/").removeProperty("a");
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ PropertyState p = ourRoot.getTree("/").getProperty("a");
+ assertNull(p);
+ }
+
+ @Test
+ public void testDeleteChangedProperty() throws CommitFailedException {
+ theirRoot.getTree("/").setProperty("a", THEIR_VALUE);
+ ourRoot.getTree("/").removeProperty("a");
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ PropertyState p = ourRoot.getTree("/").getProperty("a");
+ assertNull(p);
+ }
+
+ @Test
+ public void testAddExistingNode() throws CommitFailedException {
+ theirRoot.getTree("/").addChild("n").setProperty("p", THEIR_VALUE);
+ ourRoot.getTree("/").addChild("n").setProperty("p", OUR_VALUE);
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ Tree n = ourRoot.getTree("/n");
+ assertTrue(n.exists());
+ assertEquals(OUR_VALUE, n.getProperty("p").getValue(STRING));
+ }
+
+ @Test
+ public void testChangeDeletedNode() throws CommitFailedException {
+ theirRoot.getTree("/x").remove();
+ ourRoot.getTree("/x").setProperty("p", OUR_VALUE);
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ Tree n = ourRoot.getTree("/x");
+ assertTrue(n.exists());
+ assertEquals(OUR_VALUE, n.getProperty("p").getValue(STRING));
+ }
+
+ @Test
+ public void testDeleteChangedNode() throws CommitFailedException {
+ theirRoot.getTree("/x").setProperty("p", THEIR_VALUE);
+ ourRoot.getTree("/x").remove();
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ Tree n = ourRoot.getTree("/x");
+ assertFalse(n.exists());
+ }
+
+ @Test
+ public void testDeleteDeletedNode() throws CommitFailedException {
+ theirRoot.getTree("/x").remove();
+ ourRoot.getTree("/x").remove();
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ assertFalse(ourRoot.getTree("/x").exists());
+ }
+
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandlerOursTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandlerTheirsTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandlerTheirsTest.java?rev=1802922&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandlerTheirsTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandlerTheirsTest.java
Tue Jul 25 12:17:46 2017
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.commit;
+
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DefaultThreeWayConflictHandlerTheirsTest {
+
+ private static final String OUR_VALUE = "our value";
+ private static final String THEIR_VALUE = "their value";
+
+ private Root ourRoot;
+ private Root theirRoot;
+
+ @Before
+ public void setUp() throws CommitFailedException {
+ ContentSession session = new Oak()
+ .with(new OpenSecurityProvider())
+ .with(DefaultThreeWayConflictHandler.THEIRS)
+ .createContentSession();
+
+ // Add test content
+ Root root = session.getLatestRoot();
+ Tree tree = root.getTree("/");
+ tree.setProperty("a", 1);
+ tree.setProperty("b", 2);
+ tree.setProperty("c", 3);
+ tree.addChild("x");
+ tree.addChild("y");
+ tree.addChild("z");
+ root.commit();
+
+ ourRoot = session.getLatestRoot();
+ theirRoot = session.getLatestRoot();
+ }
+
+ @After
+ public void tearDown() {
+ ourRoot = null;
+ theirRoot = null;
+ }
+
+ @Test
+ public void testAddExistingProperty() throws CommitFailedException {
+ theirRoot.getTree("/").setProperty("p", THEIR_VALUE);
+ theirRoot.getTree("/").setProperty("q", THEIR_VALUE);
+ ourRoot.getTree("/").setProperty("p", OUR_VALUE);
+ ourRoot.getTree("/").setProperty("q", OUR_VALUE);
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ PropertyState p = ourRoot.getTree("/").getProperty("p");
+ assertNotNull(p);
+ assertEquals(THEIR_VALUE, p.getValue(STRING));
+
+ PropertyState q = ourRoot.getTree("/").getProperty("q");
+ assertNotNull(q);
+ assertEquals(THEIR_VALUE, p.getValue(STRING));
+ }
+
+ @Test
+ public void testChangeDeletedProperty() throws CommitFailedException {
+ theirRoot.getTree("/").removeProperty("a");
+ ourRoot.getTree("/").setProperty("a", OUR_VALUE);
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ PropertyState p = ourRoot.getTree("/").getProperty("a");
+ assertNull(p);
+ }
+
+ @Test
+ public void testChangeChangedProperty() throws CommitFailedException {
+ theirRoot.getTree("/").setProperty("a", THEIR_VALUE);
+ ourRoot.getTree("/").setProperty("a", OUR_VALUE);
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ PropertyState p = ourRoot.getTree("/").getProperty("a");
+ assertNotNull(p);
+ assertEquals(THEIR_VALUE, p.getValue(STRING));
+ }
+
+ @Test
+ public void testDeleteDeletedProperty() throws CommitFailedException {
+ theirRoot.getTree("/").removeProperty("a");
+ ourRoot.getTree("/").removeProperty("a");
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ PropertyState p = ourRoot.getTree("/").getProperty("a");
+ assertNull(p);
+ }
+
+ @Test
+ public void testDeleteChangedProperty() throws CommitFailedException {
+ theirRoot.getTree("/").setProperty("a", THEIR_VALUE);
+ ourRoot.getTree("/").removeProperty("a");
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ PropertyState p = ourRoot.getTree("/").getProperty("a");
+ assertNotNull(p);
+ assertEquals(THEIR_VALUE, p.getValue(STRING));
+ }
+
+ @Test
+ public void testAddExistingNode() throws CommitFailedException {
+ theirRoot.getTree("/").addChild("n").setProperty("p", THEIR_VALUE);
+ ourRoot.getTree("/").addChild("n").setProperty("p", OUR_VALUE);
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ Tree n = ourRoot.getTree("/n");
+ assertNotNull(n);
+ assertEquals(THEIR_VALUE, n.getProperty("p").getValue(STRING));
+ }
+
+ @Test
+ public void testChangeDeletedNode() throws CommitFailedException {
+ theirRoot.getTree("/x").remove();
+ ourRoot.getTree("/x").setProperty("p", OUR_VALUE);
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ Tree n = ourRoot.getTree("/x");
+ assertFalse(n.exists());
+ }
+
+ @Test
+ public void testDeleteChangedNode() throws CommitFailedException {
+ theirRoot.getTree("/x").setProperty("p", THEIR_VALUE);
+ ourRoot.getTree("/x").remove();
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ Tree n = ourRoot.getTree("/x");
+ assertTrue(n.exists());
+ assertEquals(THEIR_VALUE, n.getProperty("p").getValue(STRING));
+ }
+
+ @Test
+ public void testDeleteDeletedNode() throws CommitFailedException {
+ theirRoot.getTree("/x").remove();
+ ourRoot.getTree("/x").remove();
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ assertFalse(ourRoot.getTree("/x").exists());
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandlerTheirsTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/JcrLastModifiedConflictHandlerTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/JcrLastModifiedConflictHandlerTest.java?rev=1802922&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/JcrLastModifiedConflictHandlerTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/JcrLastModifiedConflictHandlerTest.java
Tue Jul 25 12:17:46 2017
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.commit;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_CREATED;
+import static org.apache.jackrabbit.JcrConstants.JCR_LASTMODIFIED;
+import static org.apache.jackrabbit.oak.api.Type.DATE;
+
+import java.util.Calendar;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler;
+import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
+import org.apache.jackrabbit.util.ISO8601;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class JcrLastModifiedConflictHandlerTest {
+
+ @Test
+ public void updates() throws Exception {
+
+ ContentRepository repo = newRepo(new JcrLastModifiedConflictHandler());
+ Root root = login(repo);
+ setup(root);
+
+ Root ourRoot = login(repo);
+ Root theirRoot = login(repo);
+
+ PropertyState p0 = createDateProperty(JCR_CREATED);
+ ourRoot.getTree("/c").setProperty(p0);
+
+ TimeUnit.MILLISECONDS.sleep(50);
+ theirRoot.getTree("/c").setProperty(createDateProperty(JCR_CREATED));
+
+ ourRoot.commit();
+ theirRoot.commit();
+
+ root.refresh();
+ Assert.assertEquals(p0, root.getTree("/c").getProperty(JCR_CREATED));
+ TimeUnit.MILLISECONDS.sleep(50);
+
+ ourRoot.refresh();
+ theirRoot.refresh();
+
+
ourRoot.getTree("/c").setProperty(createDateProperty(JCR_LASTMODIFIED));
+ TimeUnit.MILLISECONDS.sleep(50);
+ PropertyState p1 = createDateProperty(JCR_LASTMODIFIED);
+ theirRoot.getTree("/c").setProperty(p1);
+
+ ourRoot.commit();
+ theirRoot.commit();
+
+ root.refresh();
+ Assert.assertEquals(p1,
root.getTree("/c").getProperty(JCR_LASTMODIFIED));
+ }
+
+ public static PropertyState createDateProperty(@Nonnull String name) {
+ String now = ISO8601.format(Calendar.getInstance());
+ return PropertyStates.createProperty(name, now, DATE);
+ }
+
+ private static ContentRepository newRepo(ThreeWayConflictHandler handler) {
+ return new Oak().with(new
OpenSecurityProvider()).with(handler).createContentRepository();
+ }
+
+ private static Root login(ContentRepository repo) throws Exception {
+ return repo.login(null, null).getLatestRoot();
+ }
+
+ private static void setup(Root root) throws Exception {
+ Tree tree = root.getTree("/");
+ tree.addChild("c");
+ root.commit();
+ }
+
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/JcrLastModifiedConflictHandlerTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/ThreeWayConflictHandlerTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/ThreeWayConflictHandlerTest.java?rev=1802922&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/ThreeWayConflictHandlerTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/ThreeWayConflictHandlerTest.java
Tue Jul 25 12:17:46 2017
@@ -0,0 +1,357 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.commit;
+
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler;
+import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ThreeWayConflictHandlerTest {
+
+ @Test
+ public void addExistingProperty() throws Exception {
+ AtomicBoolean called = new AtomicBoolean(false);
+ ThreeWayConflictHandler handler = new ErrorThreeWayConflictHandler() {
+
+ @Override
+ public Resolution addExistingProperty(NodeBuilder parent,
PropertyState ours, PropertyState theirs) {
+ called.set(true);
+ assertEquals("ours", ours.getValue(STRING));
+ assertEquals("theirs", theirs.getValue(STRING));
+ return Resolution.IGNORED;
+ }
+ };
+
+ ContentRepository repo = newRepo(handler);
+ Root root = login(repo);
+ setup(root);
+
+ Root ourRoot = login(repo);
+ Root theirRoot = login(repo);
+
+ theirRoot.getTree("/c").setProperty("p0", "theirs");
+ ourRoot.getTree("/c").setProperty("p0", "ours");
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ assertTrue(called.get());
+ }
+
+ @Test
+ public void changeDeletedProperty() throws Exception {
+ AtomicBoolean called = new AtomicBoolean(false);
+ ThreeWayConflictHandler handler = new ErrorThreeWayConflictHandler() {
+
+ @Override
+ public Resolution changeDeletedProperty(NodeBuilder parent,
PropertyState ours, PropertyState base) {
+ called.set(true);
+ assertEquals("ours", ours.getValue(STRING));
+ assertEquals("base", base.getValue(STRING));
+ return Resolution.IGNORED;
+ }
+ };
+
+ ContentRepository repo = newRepo(handler);
+ Root root = login(repo);
+ setup(root);
+
+ Root ourRoot = login(repo);
+ Root theirRoot = login(repo);
+
+ theirRoot.getTree("/c").removeProperty("p");
+ ourRoot.getTree("/c").setProperty("p", "ours");
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ assertTrue(called.get());
+ }
+
+ @Test
+ public void changeChangedProperty() throws Exception {
+ AtomicBoolean called = new AtomicBoolean(false);
+ ThreeWayConflictHandler handler = new ErrorThreeWayConflictHandler() {
+
+ @Override
+ public Resolution changeChangedProperty(NodeBuilder parent,
PropertyState ours, PropertyState theirs,
+ PropertyState base) {
+ called.set(true);
+ assertEquals("ours", ours.getValue(STRING));
+ assertEquals("theirs", theirs.getValue(STRING));
+ assertEquals("base", base.getValue(STRING));
+ return Resolution.IGNORED;
+ }
+ };
+
+ ContentRepository repo = newRepo(handler);
+ Root root = login(repo);
+ setup(root);
+
+ Root ourRoot = login(repo);
+ Root theirRoot = login(repo);
+
+ theirRoot.getTree("/c").setProperty("p", "theirs");
+ ourRoot.getTree("/c").setProperty("p", "ours");
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ assertTrue(called.get());
+ }
+
+ @Test
+ public void deleteDeletedProperty() throws Exception {
+ AtomicBoolean called = new AtomicBoolean(false);
+ ThreeWayConflictHandler handler = new ErrorThreeWayConflictHandler() {
+
+ @Override
+ public Resolution deleteDeletedProperty(NodeBuilder parent,
PropertyState base) {
+ called.set(true);
+ assertEquals("base", base.getValue(STRING));
+ return Resolution.IGNORED;
+ }
+ };
+
+ ContentRepository repo = newRepo(handler);
+ Root root = login(repo);
+ setup(root);
+
+ Root ourRoot = login(repo);
+ Root theirRoot = login(repo);
+
+ theirRoot.getTree("/c").removeProperty("p");
+ ourRoot.getTree("/c").removeProperty("p");
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ assertTrue(called.get());
+ }
+
+ @Test
+ public void deleteChangedProperty() throws Exception {
+ AtomicBoolean called = new AtomicBoolean(false);
+ ThreeWayConflictHandler handler = new ErrorThreeWayConflictHandler() {
+
+ @Override
+ public Resolution deleteChangedProperty(NodeBuilder parent,
PropertyState theirs, PropertyState base) {
+ called.set(true);
+ assertEquals("theirs", theirs.getValue(STRING));
+ assertEquals("base", base.getValue(STRING));
+ return Resolution.IGNORED;
+ }
+ };
+
+ ContentRepository repo = newRepo(handler);
+ Root root = login(repo);
+ setup(root);
+
+ Root ourRoot = login(repo);
+ Root theirRoot = login(repo);
+
+ theirRoot.getTree("/c").setProperty("p", "theirs");
+ ourRoot.getTree("/c").removeProperty("p");
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ assertTrue(called.get());
+ }
+
+ @Test
+ public void changeDeletedNode() throws Exception {
+ AtomicBoolean called = new AtomicBoolean(false);
+ ThreeWayConflictHandler handler = new ErrorThreeWayConflictHandler() {
+
+ @Override
+ public Resolution changeDeletedNode(NodeBuilder parent, String
name, NodeState ours, NodeState base) {
+ called.set(true);
+ assertTrue(ours.hasProperty("p"));
+ assertTrue(base.hasProperty("p"));
+ assertEquals("ours", ours.getProperty("p").getValue(STRING));
+ assertEquals("base", base.getProperty("p").getValue(STRING));
+ return Resolution.IGNORED;
+ }
+ };
+
+ ContentRepository repo = newRepo(handler);
+ Root root = login(repo);
+ setup(root);
+
+ Root ourRoot = login(repo);
+ Root theirRoot = login(repo);
+
+ theirRoot.getTree("/c").remove();
+ ourRoot.getTree("/c").setProperty("p", "ours");
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ assertTrue(called.get());
+ }
+
+ @Test
+ public void deleteChangedNode() throws Exception {
+ AtomicBoolean called = new AtomicBoolean(false);
+ ThreeWayConflictHandler handler = new ErrorThreeWayConflictHandler() {
+
+ @Override
+ public Resolution deleteChangedNode(NodeBuilder parent, String
name, NodeState theirs, NodeState base) {
+ called.set(true);
+ assertTrue(theirs.hasProperty("p"));
+ assertTrue(base.hasProperty("p"));
+ assertEquals("theirs",
theirs.getProperty("p").getValue(STRING));
+ assertEquals("base", base.getProperty("p").getValue(STRING));
+ return Resolution.IGNORED;
+ }
+ };
+
+ ContentRepository repo = newRepo(handler);
+ Root root = login(repo);
+ setup(root);
+
+ Root ourRoot = login(repo);
+ Root theirRoot = login(repo);
+
+ theirRoot.getTree("/c").setProperty("p", "theirs");
+ ourRoot.getTree("/c").remove();
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ assertTrue(called.get());
+ }
+
+ @Test
+ public void deleteDeletedNode() throws Exception {
+ AtomicBoolean called = new AtomicBoolean(false);
+ ThreeWayConflictHandler handler = new ErrorThreeWayConflictHandler() {
+
+ @Override
+ public Resolution deleteDeletedNode(NodeBuilder parent, String
name, NodeState base) {
+ called.set(true);
+ assertTrue(base.hasProperty("p"));
+ assertEquals("base", base.getProperty("p").getValue(STRING));
+ return Resolution.IGNORED;
+ }
+ };
+
+ ContentRepository repo = newRepo(handler);
+ Root root = login(repo);
+ setup(root);
+
+ Root ourRoot = login(repo);
+ Root theirRoot = login(repo);
+
+ theirRoot.getTree("/c").remove();
+ ourRoot.getTree("/c").remove();
+
+ theirRoot.commit();
+ ourRoot.commit();
+
+ assertTrue(called.get());
+ }
+
+ private static ContentRepository newRepo(ThreeWayConflictHandler handler) {
+ return new Oak().with(new
OpenSecurityProvider()).with(handler).createContentRepository();
+ }
+
+ private static Root login(ContentRepository repo) throws Exception {
+ return repo.login(null, null).getLatestRoot();
+ }
+
+ private static void setup(Root root) throws Exception {
+ Tree tree = root.getTree("/");
+ tree.addChild("c").setProperty("p", "base");
+ root.commit();
+ }
+
+ private static class ErrorThreeWayConflictHandler implements
ThreeWayConflictHandler {
+
+ @Override
+ public Resolution addExistingProperty(NodeBuilder parent,
PropertyState ours, PropertyState theirs) {
+ Assert.fail("method should not be called");
+ return Resolution.IGNORED;
+ }
+
+ @Override
+ public Resolution changeDeletedProperty(NodeBuilder parent,
PropertyState ours, PropertyState base) {
+ Assert.fail("method should not be called");
+ return Resolution.IGNORED;
+ }
+
+ @Override
+ public Resolution changeChangedProperty(NodeBuilder parent,
PropertyState ours, PropertyState theirs,
+ PropertyState base) {
+ Assert.fail("method should not be called");
+ return Resolution.IGNORED;
+ }
+
+ @Override
+ public Resolution deleteDeletedProperty(NodeBuilder parent,
PropertyState base) {
+ Assert.fail("method should not be called");
+ return Resolution.IGNORED;
+ }
+
+ @Override
+ public Resolution deleteChangedProperty(NodeBuilder parent,
PropertyState theirs, PropertyState base) {
+ Assert.fail("method should not be called");
+ return Resolution.IGNORED;
+ }
+
+ @Override
+ public Resolution addExistingNode(NodeBuilder parent, String name,
NodeState ours, NodeState theirs) {
+ Assert.fail("method should not be called");
+ return Resolution.IGNORED;
+ }
+
+ @Override
+ public Resolution changeDeletedNode(NodeBuilder parent, String name,
NodeState ours, NodeState base) {
+ Assert.fail("method should not be called");
+ return Resolution.IGNORED;
+ }
+
+ @Override
+ public Resolution deleteChangedNode(NodeBuilder parent, String name,
NodeState theirs, NodeState base) {
+ Assert.fail("method should not be called");
+ return Resolution.IGNORED;
+ }
+
+ @Override
+ public Resolution deleteDeletedNode(NodeBuilder parent, String name,
NodeState base) {
+ Assert.fail("method should not be called");
+ return Resolution.IGNORED;
+ }
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/commit/ThreeWayConflictHandlerTest.java
------------------------------------------------------------------------------
svn:eol-style = native