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);
}