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


Reply via email to