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 to...@apache.org on 2016/05/17 14:43:30 UTC

svn commit: r1744284 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ oak-core/src/test/java/org/apache/jackrabbit/oak/ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/version/ oak-core/src/test/jav...

Author: tomekr
Date: Tue May 17 14:43:30 2016
New Revision: 1744284

URL: http://svn.apache.org/viewvc?rev=1744284&view=rev
Log:
OAK-4370: Unreferenced empty VersionHistories should be deleted automatically

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/OrphanedVersionCleaner.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionHook.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableCollector.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManager.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/Utils.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditor.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditorProvider.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManagerTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/VersionStorageTest.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/OrphanedVersionCleaner.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/OrphanedVersionCleaner.java?rev=1744284&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/OrphanedVersionCleaner.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/OrphanedVersionCleaner.java Tue May 17 14:43:30 2016
@@ -0,0 +1,99 @@
+/*
+ * 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.version;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONSTORAGE;
+
+/**
+ * This editor removes empty version histories for nodes that are no longer versionable
+ * (eg. because were deleted). The class won't remove histories for nodes which
+ * UUIDs are present in the {@link OrphanedVersionCleaner#existingVersionables}.
+ * The set should be used to skip processing moved/renamed nodes and it can be
+ * filled by the related {@link VersionableCollector} editor.
+ */
+class OrphanedVersionCleaner extends DefaultEditor {
+
+    private final ReadWriteVersionManager vMgr;
+
+    private final Set<String> existingVersionables;
+
+    OrphanedVersionCleaner(ReadWriteVersionManager vMgr, Set<String> existingVersionables) {
+        this.vMgr = vMgr;
+        this.existingVersionables = existingVersionables;
+    }
+
+    @Override
+    public void leave(NodeState before, NodeState after) throws CommitFailedException {
+        if (vMgr.isVersionable(before) && !vMgr.isVersionable(after)) {
+            String versionableUuid = Utils.uuidFromNode(before);
+            if (!existingVersionables.contains(versionableUuid)) {
+                vMgr.removeEmptyHistory(before);
+            }
+        }
+    }
+
+    @Override
+    public Editor childNodeAdded(String name, NodeState after) throws CommitFailedException {
+        return this;
+    }
+
+    @Override
+    public Editor childNodeChanged(String name, NodeState before, NodeState after) throws CommitFailedException {
+        return this;
+    }
+
+    @Override
+    public Editor childNodeDeleted(String name, NodeState before) throws CommitFailedException {
+        return this;
+    }
+
+    static class Provider implements EditorProvider {
+
+        private final Set<String> existingVersionables;
+
+        Provider(Set<String> existingVersionables) {
+            this.existingVersionables = existingVersionables;
+        }
+
+        @Override
+        public Editor getRootEditor(NodeState before, NodeState after, NodeBuilder builder, CommitInfo info) throws CommitFailedException {
+            if (!builder.hasChildNode(JCR_SYSTEM)) {
+                return null;
+            }
+            NodeBuilder system = builder.child(JCR_SYSTEM);
+            if (!system.hasChildNode(JCR_VERSIONSTORAGE)) {
+                return null;
+            }
+            NodeBuilder versionStorage = system.child(JCR_VERSIONSTORAGE);
+            ReadWriteVersionManager vMgr = new ReadWriteVersionManager(versionStorage, builder);
+            return new OrphanedVersionCleaner(vMgr, existingVersionables);
+        }
+
+    }
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManager.java?rev=1744284&r1=1744283&r2=1744284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManager.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManager.java Tue May 17 14:43:30 2016
@@ -32,6 +32,8 @@ import org.apache.jackrabbit.oak.commons
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager;
 import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+import org.apache.jackrabbit.oak.plugins.tree.TreeFactory;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.util.TreeUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -324,4 +326,18 @@ public abstract class ReadOnlyVersionMan
         return getNodeTypeManager().isNodeType(
                 checkNotNull(tree), VersionConstants.MIX_VERSIONABLE);
     }
+
+    /**
+     * Returns {@code true} if the given {@code versionableCandidate} is of type
+     * {@code mix:versionable}; {@code false} otherwise.
+     *
+     * @param versionableCandidate node state to check.
+     * @return whether the {@code versionableCandidate} is versionable.
+     */
+    boolean isVersionable(NodeState versionableCandidate) {
+        // this is not 100% correct, because t.getPath() will
+        // not return the correct path for node after, but is
+        // sufficient to check if it is versionable
+        return isVersionable(TreeFactory.createReadOnlyTree(versionableCandidate));
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java?rev=1744284&r1=1744283&r2=1744284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java Tue May 17 14:43:30 2016
@@ -42,9 +42,12 @@ import org.apache.jackrabbit.oak.namepat
 import org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyBuilder;
 import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate;
 import org.apache.jackrabbit.oak.plugins.tree.RootFactory;
 import org.apache.jackrabbit.oak.plugins.tree.TreeFactory;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.util.ISO8601;
 import org.apache.jackrabbit.util.Text;
 
@@ -78,12 +81,14 @@ public class ReadWriteVersionManager ext
 
     private final NodeBuilder versionStorageNode;
     private final NodeBuilder workspaceRoot;
+    private final TypePredicate isVersion;
     private ReadOnlyNodeTypeManager ntMgr;
 
     public ReadWriteVersionManager(NodeBuilder versionStorageNode,
                             NodeBuilder workspaceRoot) {
         this.versionStorageNode = checkNotNull(versionStorageNode);
         this.workspaceRoot = checkNotNull(workspaceRoot);
+        this.isVersion = new TypePredicate(workspaceRoot.getNodeState(), NT_VERSION);
     }
 
     @Nonnull
@@ -305,6 +310,18 @@ public class ReadWriteVersionManager ext
         labels.removeProperty(label);
     }
 
+    /**
+     * Removes the version history if it's empty.
+     *
+     * @param versionable the versionable node.
+     */
+    void removeEmptyHistory(@Nonnull NodeState versionable) {
+        NodeBuilder history = getVersionHistory(versionable);
+        if (isEmptyHistory(history.getNodeState())) {
+            history.remove();
+        }
+    }
+
     // TODO: more methods that modify versions
 
     //------------------------------< internal >--------------------------------
@@ -560,4 +577,55 @@ public class ReadWriteVersionManager ext
         }
         return null;
     }
+
+    /**
+     * Gets the version history for the given
+     * {@code versionable} node.
+     *
+     * @param versionable the versionable node.
+     * @return the version history node.
+     * @throws IllegalArgumentException if the given node does not have a
+     *                                  {@code jcr:uuid} property.
+     */
+    @Nonnull
+    private NodeBuilder getVersionHistory(@Nonnull NodeState versionable) {
+        checkNotNull(versionable);
+        String vUUID = uuidFromNode(versionable);
+        String relPath = getVersionHistoryPath(vUUID);
+        NodeBuilder node = versionStorageNode;
+        for (Iterator<String> it = PathUtils.elements(relPath).iterator(); it.hasNext(); ) {
+            String name = it.next();
+            node = node.getChildNode(name);
+            if (!node.exists()) {
+                throw new IllegalArgumentException("No version history for this node");
+            }
+        }
+        return node;
+    }
+
+    /**
+     * Checks whether the passed node history hasn't been modified since its
+     * creation. It means that: (1) there's just one version, called jcr:rootVersion
+     * and (2) there are no custom labels.
+     *
+     * @param versionHistory to test
+     * @return {@code true} if the version history hasn't been changed yet
+     */
+    private boolean isEmptyHistory(NodeState versionHistory) {
+        for (ChildNodeEntry entry : versionHistory.getChildNodeEntries()) {
+            String name = entry.getName();
+            NodeState node = entry.getNodeState();
+            if (!JCR_ROOTVERSION.equals(name) && isVersion.apply(node)) {
+                return false; // a checked-in version
+            }
+        }
+        NodeState labels = versionHistory.getChildNode(JCR_VERSIONLABELS);
+        for (PropertyState prop : labels.getProperties()) {
+            if (prop.getType() == Type.REFERENCE) {
+                return false; // custom label
+            }
+        }
+        return true;
+    }
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/Utils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/Utils.java?rev=1744284&r1=1744283&r2=1744284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/Utils.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/Utils.java Tue May 17 14:43:30 2016
@@ -29,6 +29,7 @@ import org.apache.jackrabbit.oak.api.Com
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 /**
  * {@code Utils} provide some utility methods.
@@ -47,6 +48,11 @@ public final class Utils {
     @Nonnull
     static String uuidFromNode(@Nonnull NodeBuilder node)
             throws IllegalArgumentException {
+        return uuidFromNode(node.getNodeState());
+    }
+
+    @Nonnull
+    static String uuidFromNode(@Nonnull NodeState node) {
         PropertyState p = checkNotNull(node).getProperty(JCR_UUID);
         if (p == null) {
             throw new IllegalArgumentException("Not referenceable");

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditor.java?rev=1744284&r1=1744283&r2=1744284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditor.java Tue May 17 14:43:30 2016
@@ -187,10 +187,7 @@ class VersionEditor implements Editor {
      */
     private boolean isVersionable() {
         if (isVersionable == null) {
-            // this is not 100% correct, because t.getPath() will
-            // not return the correct path for node after, but is
-            // sufficient to check if it is versionable
-            isVersionable = vMgr.isVersionable(TreeFactory.createReadOnlyTree(after));
+            isVersionable = vMgr.isVersionable(after);
         }
         return isVersionable;
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditorProvider.java?rev=1744284&r1=1744283&r2=1744284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionEditorProvider.java Tue May 17 14:43:30 2016
@@ -16,8 +16,6 @@
  */
 package org.apache.jackrabbit.oak.plugins.version;
 
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Service;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.commit.CompositeEditor;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
@@ -30,9 +28,15 @@ import org.apache.jackrabbit.oak.spi.sta
 import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
 import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_VERSIONSTORAGE;
 
-@Component
-@Service(EditorProvider.class)
-public class VersionEditorProvider implements EditorProvider {
+/**
+ * A provider creating two editors: {@link VersionEditor}
+ * {@link VersionStorageEditor}.
+ * <p>
+ * Historically, it has been used to initalize the Jcr repository. Now, the
+ * more general {@link VersionHook} should be passed there using the {@code with()}
+ * method.
+ */
+class VersionEditorProvider implements EditorProvider {
 
     @Override
     public Editor getRootEditor(

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionHook.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionHook.java?rev=1744284&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionHook.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionHook.java Tue May 17 14:43:30 2016
@@ -0,0 +1,91 @@
+/*
+ * 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.version;
+
+import com.google.common.base.Function;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EditorHook;
+import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.collect.Collections2.transform;
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.jackrabbit.oak.spi.commit.CompositeHook.compose;
+
+/**
+ * This class gathers together editors related to handling version storage:
+ * <ol>
+ *     <li>
+ *         {@link VersionEditorProvider}
+ *         <ul>
+ *             <li>
+ *                 {@link VersionEditor} - creates version history, handles
+ *                 checking-in, checking-out and restoring, prevents a
+ *                 checked-in node from being modified,
+ *             </li>
+ *             <li>
+ *                 {@link VersionStorageEditor} - validates changes on the
+ *                 version storage,
+ *             </li>
+ *         </ul>
+ *     </li>
+ *     <li>
+ *         {@link VersionableCollector} - collects all existing versionable
+ *         UUIDs, so assigned histories won't be removed in the next step,
+ *     </li>
+ *     <li>
+ *         {@link OrphanedVersionCleaner} - removes all histories that are
+ *         empty and have no longer a parent versionable node.
+ *     </li>
+ * </ol>
+ *
+ */
+@Component
+@Service(CommitHook.class)
+public class VersionHook implements CommitHook {
+
+    @Nonnull
+    @Override
+    public NodeState processCommit(NodeState before, NodeState after, CommitInfo info) throws CommitFailedException {
+        Set<String> existingVersionables = newHashSet();
+
+        List<EditorProvider> providers = newArrayList();
+        providers.add(new VersionEditorProvider());
+        providers.add(new VersionableCollector.Provider(existingVersionables));
+        providers.add(new OrphanedVersionCleaner.Provider(existingVersionables));
+
+        return compose(transform(providers, new Function<EditorProvider, CommitHook>() {
+            @Nullable
+            @Override
+            public CommitHook apply(@Nullable EditorProvider input) {
+                return new EditorHook(input);
+            }
+        })).processCommit(before, after, info);
+    }
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableCollector.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableCollector.java?rev=1744284&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableCollector.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableCollector.java Tue May 17 14:43:30 2016
@@ -0,0 +1,93 @@
+/*
+ * 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.version;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import java.util.Set;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONSTORAGE;
+
+/**
+ * This editor puts UUIDs of all processed versionables into
+ * {@link VersionableCollector#existingVersionables} set. The main purpose
+ * is to handle moved/renamed versionable nodes in the {@link OrphanedVersionCleaner}.
+ */
+class VersionableCollector extends DefaultEditor {
+
+    private final ReadWriteVersionManager vMgr;
+
+    private final Set<String> existingVersionables;
+
+    VersionableCollector(ReadWriteVersionManager vMgr, Set<String> existingVersionables) {
+        this.vMgr = vMgr;
+        this.existingVersionables = existingVersionables;
+    }
+
+    @Override
+    public void leave(NodeState before, NodeState after) throws CommitFailedException {
+        if (vMgr.isVersionable(after)) {
+            existingVersionables.add(Utils.uuidFromNode(after));
+        }
+    }
+
+    @Override
+    public Editor childNodeAdded(String name, NodeState after) throws CommitFailedException {
+        return this;
+    }
+
+    @Override
+    public Editor childNodeChanged(String name, NodeState before, NodeState after) throws CommitFailedException {
+        return this;
+    }
+
+    @Override
+    public Editor childNodeDeleted(String name, NodeState before) throws CommitFailedException {
+        return this;
+    }
+
+    static class Provider implements EditorProvider {
+
+        private final Set<String> existingVersionables;
+
+        Provider(Set<String> existingVersionables) {
+            this.existingVersionables = existingVersionables;
+        }
+
+        @Override
+        public Editor getRootEditor(NodeState before, NodeState after, NodeBuilder builder, CommitInfo info) throws CommitFailedException {
+            if (!builder.hasChildNode(JCR_SYSTEM)) {
+                return null;
+            }
+            NodeBuilder system = builder.child(JCR_SYSTEM);
+            if (!system.hasChildNode(JCR_VERSIONSTORAGE)) {
+                return null;
+            }
+            NodeBuilder versionStorage = system.child(JCR_VERSIONSTORAGE);
+            ReadWriteVersionManager vMgr = new ReadWriteVersionManager(versionStorage, builder);
+            return new VersionableCollector(vMgr, existingVersionables);
+        }
+
+    }
+}

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java?rev=1744284&r1=1744283&r2=1744284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java Tue May 17 14:43:30 2016
@@ -52,7 +52,7 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.nodetype.TypeEditorProvider;
 import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent;
 import org.apache.jackrabbit.oak.plugins.value.ValueFactoryImpl;
-import org.apache.jackrabbit.oak.plugins.version.VersionEditorProvider;
+import org.apache.jackrabbit.oak.plugins.version.VersionHook;
 import org.apache.jackrabbit.oak.security.SecurityProviderImpl;
 import org.apache.jackrabbit.oak.spi.commit.EditorHook;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
@@ -84,7 +84,7 @@ public abstract class AbstractSecurityTe
     public void before() throws Exception {
         Oak oak = new Oak()
                 .with(new InitialContent())
-                .with(new EditorHook(new VersionEditorProvider()))
+                .with(new VersionHook())
                 .with(JcrConflictHandler.createJcrConflictHandler())
                 .with(new NamespaceEditorProvider())
                 .with(new ReferenceEditorProvider())

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManagerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManagerTest.java?rev=1744284&r1=1744283&r2=1744284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManagerTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManagerTest.java Tue May 17 14:43:30 2016
@@ -17,9 +17,11 @@
 package org.apache.jackrabbit.oak.plugins.version;
 
 import javax.annotation.Nonnull;
+import javax.jcr.RepositoryException;
 
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
@@ -32,6 +34,7 @@ import org.junit.Test;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.jackrabbit.JcrConstants.JCR_ISCHECKEDOUT;
+import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -166,4 +169,50 @@ public class ReadOnlyVersionManagerTest
         assertNotNull(t);
         assertFalse(t.exists());
     }
+
+    @Test
+    public void testRemoveEmptyHistoryAfterRemovingVersionable() throws RepositoryException, CommitFailedException {
+        NodeUtil node = new NodeUtil(root.getTree("/"));
+        NodeUtil testVersionable = node.addChild("testVersionable", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        TreeUtil.addMixin(testVersionable.getTree(), JcrConstants.MIX_VERSIONABLE, root.getTree(NodeTypeConstants.NODE_TYPES_PATH), null);
+        root.commit();
+
+        String historyPath = versionManager.getVersionHistory(testVersionable.getTree()).getPath();
+        assertTrue(root.getTree(historyPath).exists());
+
+        testVersionable.getTree().remove();
+        root.commit();
+
+        assertFalse(root.getTree(historyPath).exists());
+    }
+
+    @Test
+    public void testPreserveNonEmptyHistoryAfterRemovingVersionable() throws RepositoryException, CommitFailedException {
+        String historyPath = versionManager.getVersionHistory(versionable).getPath();
+        assertTrue(root.getTree(historyPath).exists());
+
+        versionable.remove();
+        root.commit();
+
+        assertTrue(root.getTree(historyPath).exists());
+    }
+
+    @Test
+    public void testPreserveHistoryAfterMovingVersionable() throws RepositoryException, CommitFailedException {
+        NodeUtil node = new NodeUtil(root.getTree("/"));
+        NodeUtil testVersionable = node.addChild("testVersionable", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        TreeUtil.addMixin(testVersionable.getTree(), JcrConstants.MIX_VERSIONABLE, root.getTree(NodeTypeConstants.NODE_TYPES_PATH), null);
+        root.commit();
+
+        Tree history = versionManager.getVersionHistory(testVersionable.getTree());
+        assertTrue(history.exists());
+        String historyUuid = history.getProperty(JCR_UUID).getValue(Type.STRING);
+
+        assertTrue(root.move("/testVersionable", "/testVersionable2"));
+        root.commit();
+
+        history = versionManager.getVersionHistory(root.getTree("/testVersionable2"));
+        assertTrue(history.exists());
+        assertEquals(historyUuid, history.getProperty(JCR_UUID).getValue(Type.STRING));
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/VersionStorageTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/VersionStorageTest.java?rev=1744284&r1=1744283&r2=1744284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/VersionStorageTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/VersionStorageTest.java Tue May 17 14:43:30 2016
@@ -23,13 +23,11 @@ import javax.jcr.security.AccessControlM
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
 import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
-import org.apache.jackrabbit.oak.Oak;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
 import org.apache.jackrabbit.oak.plugins.version.ReadOnlyVersionManager;
 import org.apache.jackrabbit.oak.plugins.version.VersionConstants;
-import org.apache.jackrabbit.oak.plugins.version.VersionEditorProvider;
 import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
 import org.apache.jackrabbit.oak.util.TreeUtil;
 import org.junit.Test;

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java?rev=1744284&r1=1744283&r2=1744284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java Tue May 17 14:43:30 2016
@@ -46,13 +46,12 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.nodetype.TypeEditorProvider;
 import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent;
 import org.apache.jackrabbit.oak.plugins.observation.CommitRateLimiter;
-import org.apache.jackrabbit.oak.plugins.version.VersionEditorProvider;
+import org.apache.jackrabbit.oak.plugins.version.VersionHook;
 import org.apache.jackrabbit.oak.query.QueryEngineSettings;
 import org.apache.jackrabbit.oak.security.SecurityProviderImpl;
 import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.CompositeConflictHandler;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
-import org.apache.jackrabbit.oak.spi.commit.EditorHook;
 import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
 import org.apache.jackrabbit.oak.spi.commit.Observer;
 import org.apache.jackrabbit.oak.spi.commit.PartialConflictHandler;
@@ -112,7 +111,7 @@ public class Jcr {
         if (initialize) {
             with(new InitialContent());
 
-            with(new EditorHook(new VersionEditorProvider()));
+            with(new VersionHook());
 
             with(new SecurityProviderImpl());