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 md...@apache.org on 2013/10/24 15:45:49 UTC

svn commit: r1535380 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/ oak-core/src/test/jav...

Author: mduerig
Date: Thu Oct 24 13:45:49 2013
New Revision: 1535380

URL: http://svn.apache.org/r1535380
Log:
OAK-783: Reflect Move and Rename upon Root#commit
Initial prototype of MoveDetector.

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/MoveDetector.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/MoveValidator.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/state/
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/state/MoveDetectorTest.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeBuilder.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/core/RootFuzzIT.java
    jackrabbit/oak/trunk/oak-it/mk/src/main/java/org/apache/jackrabbit/mk/test/MicroKernelIT.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeBuilder.java?rev=1535380&r1=1535379&r2=1535380&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeBuilder.java Thu Oct 24 13:45:49 2013
@@ -68,8 +68,12 @@ public class KernelNodeBuilder extends M
         if (newParent instanceof FastCopyMove) {
             checkNotNull(newParent);
             checkNotNull(newName);
-            return !isRoot() && exists() && !newParent.hasChildNode(newName) &&
+            boolean success = !isRoot() && exists() && !newParent.hasChildNode(newName) &&
                     ((FastCopyMove) newParent).moveFrom(this, newName);
+            if (success) {
+                annotateSourcePath(newParent.getChildNode(newName), getPath());
+            }
+            return success;
         } else {
             return super.moveTo(newParent, newName);
         }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java?rev=1535380&r1=1535379&r2=1535380&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java Thu Oct 24 13:45:49 2013
@@ -30,14 +30,15 @@ import java.io.InputStream;
 
 import javax.annotation.Nonnull;
 
+import com.google.common.base.Objects;
+import com.google.common.io.ByteStreams;
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.spi.state.MoveDetector;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
-import com.google.common.io.ByteStreams;
-
 /**
  * In-memory node state builder.
  * <p>
@@ -326,6 +327,9 @@ public class MemoryNodeBuilder implement
             return false;
         } else {
             if (newParent.exists()) {
+                if (!isNew()) {
+                    annotateSourcePath(this, getPath());
+                }
                 NodeState nodeState = getNodeState();
                 newParent.setChildNode(newName, nodeState);
                 remove();
@@ -337,6 +341,16 @@ public class MemoryNodeBuilder implement
         }
     }
 
+    protected static void annotateSourcePath(NodeBuilder builder, String path) {
+        PropertyState base = builder.getBaseState().getProperty(MoveDetector.SOURCE_PATH);
+        PropertyState head = builder.getNodeState().getProperty(MoveDetector.SOURCE_PATH);
+        if (Objects.equal(base, head)) {
+            if (!builder.hasProperty(MoveDetector.SOURCE_PATH)) {
+                builder.setProperty(MoveDetector.SOURCE_PATH, path);
+            }
+        }
+    }
+
     @Override
     public boolean copyTo(NodeBuilder newParent, String newName) {
         if (isRoot()) {

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/MoveDetector.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/MoveDetector.java?rev=1535380&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/MoveDetector.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/MoveDetector.java Thu Oct 24 13:45:49 2013
@@ -0,0 +1,112 @@
+/*
+ * 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.spi.state;
+
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.spi.commit.DefaultValidator;
+import org.apache.jackrabbit.oak.spi.commit.Validator;
+
+/**
+ * A {@code MoveDetector} is a validator that can detect certain move operations
+ * and reports these to the wrapped {@link MoveValidator} by calling
+ * {@link MoveValidator#move(String, String, NodeState)}. That method is called additional
+ * to {@link MoveValidator#childNodeAdded(String, NodeState)} for the destination of the move
+ * operation and {@link MoveValidator#childNodeDeleted(String, NodeState)} for the source of
+ * the move operation.
+ * <p>
+ * Detection of move operations relies on the presence of the {@link #SOURCE_PATH} property.
+ * New nodes with this property set have been moved from the path indicated by the value of the
+ * property to its current location.
+ * <p>
+ * Limitations:
+ * <ul>
+ *     <li>Moving a moved node only reports one move from the original source to the final
+ *     target.</li>
+ *     <li>Moving a transiently added node is not reported as a move operation but as an
+ *     add operation on the move target.</li>
+ *     <li>Moving a child node of a transiently moved node is not reported as a move operation
+ *     but as an add operation on the move target.</li>
+ *     <li>Moving a node back and forth to its original location is not reported at all.</li>
+ * </ul>
+ */
+public class MoveDetector extends DefaultValidator {
+    public static final String SOURCE_PATH = ":source-path";
+
+    private final MoveValidator moveValidator;
+    private final String path;
+
+    private MoveDetector(MoveValidator moveValidator, String path) {
+        this.moveValidator = moveValidator;
+        this.path = path;
+    }
+
+    public MoveDetector(MoveValidator moveValidator) {
+        this(moveValidator, "/");
+    }
+
+    @Override
+    public void propertyAdded(PropertyState after) throws CommitFailedException {
+        moveValidator.propertyAdded(after);
+    }
+
+    @Override
+    public void propertyChanged(PropertyState before, PropertyState after) throws CommitFailedException {
+        moveValidator.propertyChanged(before, after);
+    }
+
+    @Override
+    public void propertyDeleted(PropertyState before) throws CommitFailedException {
+        moveValidator.propertyDeleted(before);
+    }
+
+    @Override
+    public Validator childNodeAdded(String name, NodeState after) throws CommitFailedException {
+        PropertyState sourceProperty = after.getProperty(SOURCE_PATH);
+        if (sourceProperty != null) {
+            String sourcePath = sourceProperty.getValue(STRING);
+            String destPath = concat(path, name);
+            moveValidator.move(sourcePath, destPath, after);
+        }
+        MoveValidator childDiff = moveValidator.childNodeAdded(name, after);
+        return childDiff == null
+                ? null
+                : new MoveDetector(childDiff, concat(path, name));
+    }
+
+    @Override
+    public Validator childNodeChanged(String name, NodeState before, NodeState after) throws CommitFailedException {
+        MoveValidator childDiff = moveValidator.childNodeChanged(name, before, after);
+        return childDiff == null
+                ? null
+                : new MoveDetector(childDiff, concat(path, name));
+    }
+
+    @Override
+    public Validator childNodeDeleted(String name, NodeState before) throws CommitFailedException {
+        MoveValidator childDiff = moveValidator.childNodeDeleted(name, before);
+        return childDiff == null
+                ? null
+                : new MoveDetector(childDiff, concat(path, name));
+    }
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/MoveValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/MoveValidator.java?rev=1535380&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/MoveValidator.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/MoveValidator.java Thu Oct 24 13:45:49 2013
@@ -0,0 +1,54 @@
+/*
+ * 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.spi.state;
+
+import javax.annotation.CheckForNull;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.commit.Validator;
+
+/**
+ * A validator that also receives notifications about moved nodes.
+ * @see MoveDetector
+ */
+public interface MoveValidator extends Validator {
+
+    /**
+     * Called when a moved node has been detected.
+     *
+     * @param sourcePath  path of the node before the move
+     * @param destPath    path of the node after the move
+     * @param moved       the moved node state
+     * @throws CommitFailedException  if validation fails.
+     */
+    void move(String sourcePath, String destPath, NodeState moved) throws CommitFailedException;
+
+    @Override
+    @CheckForNull
+    MoveValidator childNodeAdded(String name, NodeState after) throws CommitFailedException;
+
+    @Override
+    @CheckForNull
+    MoveValidator childNodeChanged(String name, NodeState before, NodeState after) throws CommitFailedException;
+
+    @Override
+    @CheckForNull
+    MoveValidator childNodeDeleted(String name, NodeState before) throws CommitFailedException;
+}

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/core/RootFuzzIT.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/core/RootFuzzIT.java?rev=1535380&r1=1535379&r2=1535380&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/core/RootFuzzIT.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/core/RootFuzzIT.java Thu Oct 24 13:45:49 2013
@@ -26,7 +26,6 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.core.RootFuzzIT.Operation.Save;
 import static org.apache.jackrabbit.oak.core.RootFuzzIT.Operation.SetProperty;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -40,8 +39,6 @@ import org.apache.jackrabbit.oak.api.Roo
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.core.RootFuzzIT.Operation.Rebase;
-import org.apache.jackrabbit.oak.kernel.JsopDiff;
-import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.junit.After;
 import org.junit.Before;
@@ -129,11 +126,7 @@ public class RootFuzzIT {
             checkEqual(root1.getTree("/"), root2.getTree("/"));
             if (op instanceof Save) {
                 root2.commit();
-                NodeState tree1 = store1.getRoot();
-                NodeState tree2 = store2.getRoot();
-                if (!tree1.equals(tree2)) {
-                    fail("seed: " + SEED + ", " + JsopDiff.diffToJsop(tree1, tree2));
-                }
+                checkEqual(root1.getTree("/"), root2.getTree("/"));
             }
         }
     }

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/state/MoveDetectorTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/state/MoveDetectorTest.java?rev=1535380&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/state/MoveDetectorTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/state/MoveDetectorTest.java Thu Oct 24 13:45:49 2013
@@ -0,0 +1,162 @@
+/*
+ * 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.spi.state;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
+import org.apache.jackrabbit.oak.spi.commit.EditorDiff;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MoveDetectorTest {
+    private NodeState root;
+
+    @Before
+    public void setup() {
+        NodeBuilder rootBuilder = EmptyNodeState.EMPTY_NODE.builder();
+        NodeBuilder test = rootBuilder.child("test");
+        test.setProperty("a", 1);
+        test.setProperty("b", 2);
+        test.setProperty("c", 3);
+        test.child("x");
+        test.child("y");
+        test.child("z");
+        root = rootBuilder.getNodeState();
+    }
+
+    /**
+     * Test whether we can detect a single move
+     * @throws CommitFailedException
+     */
+    @Test
+    public void simpleMove() throws CommitFailedException {
+        NodeState moved = move(root.builder(), "/test/x", "/test/y/xx").getNodeState();
+        FindSingleMove findSingleMove = new FindSingleMove("/test/x", "/test/y/xx");
+        MoveDetector moveDetector = new MoveDetector(findSingleMove);
+        CommitFailedException exception = EditorDiff.process(moveDetector, root, moved);
+        if (exception != null) {
+            throw exception;
+        }
+        assertTrue(findSingleMove.found());
+    }
+
+    /**
+     * Moving a moved node is reported as a single move from the original source
+     * to the final destination.
+     * @throws CommitFailedException
+     */
+    @Test
+    public void moveMoved() throws CommitFailedException {
+        NodeBuilder rootBuilder = root.builder();
+        move(rootBuilder, "/test/x", "/test/y/xx");
+        NodeState moved = move(rootBuilder, "/test/y/xx", "/test/z/xxx").getNodeState();
+        FindSingleMove findSingleMove = new FindSingleMove("/test/x", "/test/z/xxx");
+        MoveDetector moveDetector = new MoveDetector(findSingleMove);
+        CommitFailedException exception = EditorDiff.process(moveDetector, root, moved);
+        if (exception != null) {
+            throw exception;
+        }
+        assertTrue(findSingleMove.found());
+    }
+
+    //------------------------------------------------------------< private >---
+
+    private static NodeBuilder move(NodeBuilder builder, String source, String dest) {
+        NodeBuilder sourceBuilder = getBuilder(builder, source);
+        NodeBuilder destParentBuilder = getBuilder(builder, PathUtils.getParentPath(dest));
+        sourceBuilder.moveTo(destParentBuilder, PathUtils.getName(dest));
+        return builder;
+    }
+
+    private static NodeBuilder getBuilder(NodeBuilder builder, String path) {
+        for (String name : PathUtils.elements(path)) {
+            builder = builder.getChildNode(name);
+        }
+        return builder;
+    }
+
+    private static class FindSingleMove implements MoveValidator {
+        private final String sourcePath;
+        private final String destPath;
+
+        private boolean found;
+
+        private FindSingleMove(String sourcePath, String destPath) {
+            this.sourcePath = sourcePath;
+            this.destPath = destPath;
+        }
+
+        @Override
+        public void move(String sourcePath, String destPath, NodeState moved) throws CommitFailedException {
+            if (found) {
+                throw new CommitFailedException("Test", 0, "There should only be a single move operation");
+            }
+
+            assertEquals(this.sourcePath, sourcePath);
+            assertEquals(this.destPath, destPath);
+            found = true;
+        }
+
+        @Override
+        public void enter(NodeState before, NodeState after) throws CommitFailedException {
+        }
+
+        @Override
+        public void leave(NodeState before, NodeState after) throws CommitFailedException {
+        }
+
+        @Override
+        public void propertyAdded(PropertyState after) {
+        }
+
+        @Override
+        public void propertyChanged(PropertyState before, PropertyState after) {
+        }
+
+        @Override
+        public void propertyDeleted(PropertyState before) {
+        }
+
+        @Override
+        public MoveValidator childNodeAdded(String name, NodeState after) {
+            return null;
+        }
+
+        @Override
+        public MoveValidator childNodeChanged(String name, NodeState before, NodeState after) {
+            return this;
+        }
+
+        @Override
+        public MoveValidator childNodeDeleted(String name, NodeState before) {
+            return null;
+        }
+
+        public boolean found() {
+            return found;
+        }
+    }
+
+}

Modified: jackrabbit/oak/trunk/oak-it/mk/src/main/java/org/apache/jackrabbit/mk/test/MicroKernelIT.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-it/mk/src/main/java/org/apache/jackrabbit/mk/test/MicroKernelIT.java?rev=1535380&r1=1535379&r2=1535380&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-it/mk/src/main/java/org/apache/jackrabbit/mk/test/MicroKernelIT.java (original)
+++ jackrabbit/oak/trunk/oak-it/mk/src/main/java/org/apache/jackrabbit/mk/test/MicroKernelIT.java Thu Oct 24 13:45:49 2013
@@ -929,6 +929,7 @@ public class MicroKernelIT extends Abstr
         assertFalse(mk.nodeExists("/test", null));
         assertTrue(mk.nodeExists("/moved", null));
         JSONObject obj1 = parseJSONObject(mk.getNodes("/moved", null, 99, 0, -1, null));
+        obj1.remove(":source-path");
         assertEquals(obj, obj1);
     }