Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/Reporter.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/Reporter.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/Reporter.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/Reporter.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,45 @@
+/*
+ * 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.upgrade.nodestate.report;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A {@code Reporter} receives callbacks for every NodeState
+ * and PropertyState that was accessed via a {ReportingNodeState}
+ * instance.
+ */
+public interface Reporter {
+
+    /**
+     * Callback reporting that the given {@code nodeState} was accessed.
+     *
+     * @param nodeState The accessed {@code ReportingNodeState} instance.
+     */
+    void reportNode(@Nonnull final ReportingNodeState nodeState);
+
+    /**
+     * Callback reporting that the property named {@code propertyName}
+     * was accessed on the {@code parent} node.
+     *
+     * @param parent The parent node state of the reported property.
+     * @param propertyName The name of the reported property.
+     */
+    void reportProperty(@Nonnull final ReportingNodeState parent, @Nonnull 
final String propertyName);
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/ReportingNodeState.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/ReportingNodeState.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/ReportingNodeState.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/ReportingNodeState.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,118 @@
+/*
+ * 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.upgrade.nodestate.report;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.upgrade.nodestate.AbstractDecoratedNodeState;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * A decoration layer for NodeState instances that intercepts
+ * all accesses to NodeStates and PropertyStates (getters) and
+ * informs a {@link Reporter} via its callbacks that the respective
+ * NodeStates or PropertyStates have been accessed.
+ * <br>
+ * The decoration is deep, i.e. any child NodeStates will be
+ * decorated as well and will report to the same {@code Reporter}
+ * instance.
+ * <br>
+ * For convenience, a {@link PeriodicReporter} abstract class exists.
+ * This simplifies reporting every nth node/property only.
+ * <br>
+ * Note: Multiple accesses to the same node or property are each
+ * reported. Therefore if exactly counting unique accesses is a
+ * requirement, the reporter needs to take care of de-duplication.
+ *
+ * @see Reporter
+ * @see PeriodicReporter
+ * @see LoggingReporter
+ */
+public class ReportingNodeState extends AbstractDecoratedNodeState {
+
+    private final ReportingNodeState parent;
+    private final String name;
+    private final Reporter reporter;
+
+    /**
+     * Allows wrapping a NodeState as a ReportingNodeState. The wrapped
+     * NodeState is treated as the root of a tree (i.e. path is "/").
+     * <br>
+     * Any children accessed via this NodeState are also wrapped. Each
+     * wrapped NodeState is also reported to the provided Reporter.
+     *
+     * @param nodeState The NodeState to be wrapped.
+     * @param reporter The reporter to report to.
+     * @return the wrapped NodeState.
+     */
+    public static NodeState wrap(NodeState nodeState, Reporter reporter) {
+        return wrapAndReport(null, "/", nodeState, reporter);
+    }
+
+    private static NodeState wrapAndReport(@Nullable ReportingNodeState 
parent, @Nonnull String name,
+                                           @Nonnull NodeState delegate, 
@Nonnull Reporter reporter) {
+        final ReportingNodeState nodeState = new ReportingNodeState(parent, 
name, delegate, reporter);
+        reporter.reportNode(nodeState);
+        return nodeState;
+    }
+
+    private ReportingNodeState(ReportingNodeState parent, String name, 
NodeState delegate, Reporter reporter) {
+        super(delegate);
+        this.parent = parent;
+        this.name = name;
+        this.reporter = reporter;
+    }
+
+    /**
+     * ReportingNodeState instances provide access to their path via their
+     * parent hierarchy. Note that calculating the path on every access may
+     * incur a significant performance penalty.
+     *
+     * @return The path of the ReportingNodeState instance, assuming that
+     *         the first wrapped instance is the root node.
+     */
+    public String getPath() {
+        if (parent == null) {
+            return name;
+        }
+        return PathUtils.concat(this.parent.getPath(), name);
+    }
+
+    @Nonnull
+    @Override
+    protected NodeState decorateChild(@Nonnull final String name, @Nonnull 
final NodeState delegateChild) {
+        return wrapAndReport(this, name, delegateChild, reporter);
+    }
+
+    @Override
+    @CheckForNull
+    protected PropertyState decorateProperty(@Nonnull final PropertyState 
delegatePropertyState) {
+        reporter.reportProperty(this, delegatePropertyState.getName());
+        return delegatePropertyState;
+    }
+
+    @Override
+    public String toString() {
+        return "ReportingNodeState{" + getPath() + ", " + delegate.toString() 
+ "}";
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,113 @@
+/*
+ * 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.upgrade.security;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static 
org.apache.jackrabbit.oak.spi.security.user.UserConstants.NT_REP_AUTHORIZABLE_FOLDER;
+
+/**
+ * There are occasions where in old JR2 repositories not all ancestors on
+ * the users path are of type {@code rep:AuthorizableFolder}, thus leading
+ * to exceptions after repository upgrade.
+ * <br>
+ * In order to avoid such situations, this hook verifies that all nodes on
+ * the users and groups paths are of type {@code rep:AuthorizableFolder} and
+ * fixes the node-type if it is incorrect.
+ */
+public class AuthorizableFolderEditor extends DefaultEditor {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(AuthorizableFolderEditor.class);
+
+    private final NodeBuilder currentBuilder;
+
+    private final String groupsPath;
+
+    private final String usersPath;
+
+    private final String path;
+
+    public AuthorizableFolderEditor(final NodeBuilder builder, final String 
path, final String groupsPath, final String usersPath) {
+        this.currentBuilder = builder;
+        this.groupsPath = groupsPath;
+        this.usersPath = usersPath;
+        this.path = path;
+    }
+
+    public static EditorProvider provider(final String groupsPath, final 
String usersPath) {
+        return new EditorProvider() {
+            @Override
+            public Editor getRootEditor(final NodeState before,
+                                        final NodeState after,
+                                        final NodeBuilder builder,
+                                        final CommitInfo info) throws 
CommitFailedException {
+                return new AuthorizableFolderEditor(builder, "/", groupsPath, 
usersPath);
+            }
+        };
+    }
+
+    @Override
+    public void propertyAdded(final PropertyState after) throws 
CommitFailedException {
+         propertyChanged(null, after);
+    }
+
+    @Override
+    public void propertyChanged(final PropertyState before, final 
PropertyState after) throws CommitFailedException {
+        if (JCR_PRIMARYTYPE.equals(after.getName())) {
+            String nodeType = after.getValue(Type.STRING);
+            LOG.debug("Found {}/jcr:primaryType = {}", path, nodeType);
+            if (!nodeType.equals(NT_REP_AUTHORIZABLE_FOLDER) && 
expectAuthorizableFolder(path)) {
+                LOG.info("Changed {}/jcr:primaryType from {} to {}", path, 
nodeType, NT_REP_AUTHORIZABLE_FOLDER);
+                currentBuilder.setProperty(JCR_PRIMARYTYPE, 
NT_REP_AUTHORIZABLE_FOLDER, Type.NAME);
+            }
+        }
+    }
+
+    private boolean expectAuthorizableFolder(final String path) {
+        return !PathUtils.denotesRoot(path)
+                && (PathUtils.isAncestor(path, groupsPath) || 
path.equals(groupsPath)
+                || PathUtils.isAncestor(path, usersPath) || 
path.equals(usersPath));
+    }
+
+    @Override
+    public Editor childNodeAdded(final String name, final NodeState after) 
throws CommitFailedException {
+        return childNodeChanged(name, null, after);
+    }
+
+    @Override
+    public Editor childNodeChanged(final String name, final NodeState before, 
final NodeState after) throws CommitFailedException {
+        final String childPath = PathUtils.concat(path, name);
+        if (expectAuthorizableFolder(childPath)) {
+            LOG.debug("Found {} - descending", childPath);
+            return new AuthorizableFolderEditor(currentBuilder.child(name), 
childPath, groupsPath, usersPath);
+        } else {
+            return null;
+        }
+    }
+}

Modified: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/GroupEditor.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/GroupEditor.java?rev=1792993&r1=1792992&r2=1792993&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/GroupEditor.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/GroupEditor.java
 Fri Apr 28 07:16:13 2017
@@ -108,7 +108,7 @@ class GroupEditor extends DefaultEditor
 
     @Override
     public Editor childNodeChanged(String name, NodeState before, NodeState 
after) {
-        throw new IllegalStateException("changed node during upgrade copy not 
expected: " + state.path + "/" + name);
+        return null;
     }
 
     @Override

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,114 @@
+/*
+ * 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.upgrade.version;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_CREATED;
+import static org.apache.jackrabbit.JcrConstants.NT_VERSION;
+
+import java.util.Calendar;
+import java.util.Iterator;
+
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate;
+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.oak.upgrade.DescendantsIterator;
+import org.apache.jackrabbit.oak.upgrade.nodestate.NodeStateCopier;
+import org.apache.jackrabbit.util.ISO8601;
+
+import static 
org.apache.jackrabbit.oak.plugins.version.VersionConstants.VERSION_STORE_PATH;
+
+import static 
org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getRelativeVersionHistoryPath;
+import static 
org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getVersionHistoryNodeState;
+
+/**
+ * This class allows to copy the version history, optionally filtering it with 
a
+ * given date.
+ */
+public class VersionCopier {
+
+    private final TypePredicate isVersion;
+
+    private final NodeState sourceVersionStorage;
+
+    private final NodeBuilder targetVersionStorage;
+
+    private final NodeBuilder targetRoot;
+
+    public VersionCopier(NodeBuilder targetRoot, NodeState 
sourceVersionStorage, NodeBuilder targetVersionStorage) {
+        this.isVersion = new TypePredicate(targetRoot.getNodeState(), 
NT_VERSION);
+        this.sourceVersionStorage = sourceVersionStorage;
+        this.targetVersionStorage = targetVersionStorage;
+        this.targetRoot = targetRoot;
+    }
+
+    public static void copyVersionStorage(NodeBuilder targetRoot, NodeState 
sourceVersionStorage, NodeBuilder targetVersionStorage, 
VersionCopyConfiguration config) {
+        final Iterator<NodeState> versionStorageIterator = new 
DescendantsIterator(sourceVersionStorage, 3);
+        final VersionCopier versionCopier = new VersionCopier(targetRoot, 
sourceVersionStorage, targetVersionStorage);
+
+        while (versionStorageIterator.hasNext()) {
+            final NodeState versionHistoryBucket = 
versionStorageIterator.next();
+            for (String versionHistory : 
versionHistoryBucket.getChildNodeNames()) {
+                versionCopier.copyVersionHistory(versionHistory, 
config.getOrphanedMinDate());
+            }
+        }
+    }
+
+    /**
+     * Copy history filtering versions using passed date and returns {@code
+     * true} if the history has been copied.
+     * 
+     * @param versionableUuid
+     *            Name of the version history node
+     * @param minDate
+     *            Only versions older than this date will be copied
+     * @return {@code true} if at least one version has been copied
+     */
+    public boolean copyVersionHistory(String versionableUuid, Calendar 
minDate) {
+        final String versionHistoryPath = 
getRelativeVersionHistoryPath(versionableUuid);
+        final NodeState sourceVersionHistory = 
getVersionHistoryNodeState(sourceVersionStorage, versionableUuid);
+        final Calendar lastModified = 
getVersionHistoryLastModified(sourceVersionHistory);
+
+        if (sourceVersionHistory.exists() && (lastModified.after(minDate) || 
minDate.getTimeInMillis() == 0)) {
+            NodeStateCopier.builder()
+                    .include(versionHistoryPath)
+                    .merge(VERSION_STORE_PATH)
+                    .copy(sourceVersionStorage, targetVersionStorage);
+            return true;
+        }
+        return false;
+    }
+
+    private Calendar getVersionHistoryLastModified(final NodeState 
versionHistory) {
+        Calendar youngest = Calendar.getInstance();
+        youngest.setTimeInMillis(0);
+        for (final ChildNodeEntry entry : 
versionHistory.getChildNodeEntries()) {
+            final NodeState version = entry.getNodeState();
+            if (!isVersion.apply(version)) {
+                continue;
+            }
+            if (version.hasProperty(JCR_CREATED)) {
+                final Calendar created = 
ISO8601.parse(version.getProperty(JCR_CREATED).getValue(Type.DATE));
+                if (created.after(youngest)) {
+                    youngest = created;
+                }
+            }
+        }
+        return youngest;
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,71 @@
+/*
+ * 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.upgrade.version;
+
+import java.util.Calendar;
+
+/**
+ * This class allows to configure the behaviour of the version copier.
+ */
+public class VersionCopyConfiguration {
+
+    private Calendar copyVersions;
+
+    private Calendar copyOrphanedVersions;
+
+    public VersionCopyConfiguration() {
+        final Calendar epoch = Calendar.getInstance();
+        epoch.setTimeInMillis(0);
+        this.copyVersions = epoch;
+        this.copyOrphanedVersions = epoch;
+    }
+
+    public void setCopyVersions(Calendar copyVersions) {
+        this.copyVersions = copyVersions;
+    }
+
+    public void setCopyOrphanedVersions(Calendar copyOrphanedVersions) {
+        this.copyOrphanedVersions = copyOrphanedVersions;
+    }
+
+    public Calendar getVersionsMinDate() {
+        return copyVersions;
+    }
+
+    public Calendar getOrphanedMinDate() {
+        if (copyVersions == null) {
+            return copyVersions;
+        } else if (copyOrphanedVersions != null && 
copyVersions.after(copyOrphanedVersions)) {
+            return copyVersions;
+        } else {
+            return copyOrphanedVersions;
+        }
+    }
+
+    public boolean isCopyVersions() {
+        return copyVersions != null;
+    }
+
+    public boolean skipOrphanedVersionsCopy() {
+        return copyVersions == null || copyOrphanedVersions == null;
+    }
+
+    public boolean isCopyAll() {
+        return copyVersions != null && copyVersions.getTimeInMillis() == 0 && 
copyOrphanedVersions != null && copyOrphanedVersions.getTimeInMillis() == 0;
+    }
+
+}
\ No newline at end of file

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,92 @@
+/*
+ * 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.upgrade.version;
+
+import static com.google.common.collect.Iterables.concat;
+import static java.util.Collections.singleton;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONSTORAGE;
+import static 
org.apache.jackrabbit.oak.plugins.version.VersionConstants.REP_VERSIONSTORAGE;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.base.Joiner;
+
+public class VersionHistoryUtil {
+
+    public static String getRelativeVersionHistoryPath(String versionableUuid) 
{
+        return Joiner.on('/').join(concat(
+                singleton(""),
+                getRelativeVersionHistoryPathSegments(versionableUuid),
+                singleton(versionableUuid)));
+    }
+
+    /**
+     * Constructs the version history path based on the versionable's UUID.
+     *
+     * @param root The root NodeState below which to look for the version.
+     * @param versionableUuid The String representation of the versionable's 
UUID.
+     * @return The NodeState corresponding to the version history, or {@code 
null}
+     *         if it does not exist.
+     */
+    static NodeState getVersionHistoryNodeState(NodeState versionStorage, 
String versionableUuid) {
+        NodeState historyParent = versionStorage;
+        for (String segment : 
getRelativeVersionHistoryPathSegments(versionableUuid)) {
+            historyParent = historyParent.getChildNode(segment);
+        }
+        return historyParent.getChildNode(versionableUuid);
+    }
+
+    static NodeBuilder getVersionHistoryBuilder(NodeBuilder versionStorage, 
String versionableUuid) {
+        NodeBuilder history = versionStorage;
+        for (String segment : 
getRelativeVersionHistoryPathSegments(versionableUuid)) {
+            history = history.getChildNode(segment);
+        }
+        return history.getChildNode(versionableUuid);
+    }
+
+    private static List<String> getRelativeVersionHistoryPathSegments(String 
versionableUuid) {
+        final List<String> segments = new ArrayList<String>();
+        for (int i = 0; i < 3; i++) {
+            segments.add(versionableUuid.substring(i * 2, i * 2 + 2));
+        }
+        return segments;
+    }
+
+    public static NodeState getVersionStorage(NodeState root) {
+        return root.getChildNode(JCR_SYSTEM).getChildNode(JCR_VERSIONSTORAGE);
+    }
+
+    public static NodeBuilder getVersionStorage(NodeBuilder root) {
+        return root.getChildNode(JCR_SYSTEM).getChildNode(JCR_VERSIONSTORAGE);
+    }
+
+    public static NodeBuilder createVersionStorage(NodeBuilder root) {
+        NodeBuilder vs = root.child(JCR_SYSTEM).child(JCR_VERSIONSTORAGE);
+        if (!vs.hasProperty(JCR_PRIMARYTYPE)) {
+            vs.setProperty(JCR_PRIMARYTYPE, REP_VERSIONSTORAGE, Type.NAME);
+        }
+        return vs;
+    }
+
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,250 @@
+/*
+ * 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.upgrade.version;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate;
+import org.apache.jackrabbit.oak.plugins.version.ReadWriteVersionManager;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.Set;
+
+import static com.google.common.collect.ImmutableSet.of;
+import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.jackrabbit.JcrConstants.JCR_BASEVERSION;
+import static org.apache.jackrabbit.JcrConstants.JCR_ISCHECKEDOUT;
+import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_PREDECESSORS;
+import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONHISTORY;
+import static org.apache.jackrabbit.JcrConstants.MIX_REFERENCEABLE;
+import static org.apache.jackrabbit.JcrConstants.MIX_VERSIONABLE;
+import static 
org.apache.jackrabbit.oak.plugins.memory.MultiGenericPropertyState.nameProperty;
+import static 
org.apache.jackrabbit.oak.plugins.version.VersionConstants.MIX_REP_VERSIONABLE_PATHS;
+import static 
org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getVersionHistoryBuilder;
+import static 
org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getVersionStorage;
+
+/**
+ * The VersionableEditor provides two possible ways to handle
+ * versionable nodes:
+ * <ul>
+ *     <li>it can copy the version histories of versionable nodes, or</li>
+ *     <li>
+ *         it can skip copying version histories and remove the
+ *         {@code mix:versionable} mixin together with any related
+ *         properties (see {@link #removeVersionProperties(NodeBuilder)}).
+ *     </li>
+ * </ul>
+ */
+public class VersionableEditor extends DefaultEditor {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(VersionableEditor.class);
+
+    private static final Set<String> SKIPPED_PATHS = of("/oak:index", 
"/jcr:system/jcr:versionStorage");
+
+    private final Provider provider;
+
+    private final NodeBuilder rootBuilder;
+
+    private final NodeBuilder versionStorage;
+
+    private final TypePredicate isReferenceable;
+
+    private final TypePredicate isVersionable;
+
+    private final VersionCopier versionCopier;
+
+    private final ReadWriteVersionManager vMgr;
+
+    private String path;
+
+    private VersionableEditor(Provider provider, NodeBuilder rootBuilder) {
+        this.rootBuilder = rootBuilder;
+        this.versionStorage = getVersionStorage(rootBuilder);
+        this.vMgr = new ReadWriteVersionManager(versionStorage, rootBuilder);
+
+        this.provider = provider;
+        this.isVersionable = new TypePredicate(rootBuilder.getNodeState(), 
MIX_VERSIONABLE);
+        this.isReferenceable = new TypePredicate(rootBuilder.getNodeState(), 
MIX_REFERENCEABLE);
+        this.versionCopier = new VersionCopier(rootBuilder, 
getVersionStorage(provider.sourceRoot), versionStorage);
+        this.path = "/";
+
+    }
+
+    public static class Provider implements EditorProvider {
+
+        private final NodeState sourceRoot;
+
+        private final String workspaceName;
+
+        private final VersionCopyConfiguration config;
+
+        public Provider(NodeState sourceRoot, String workspaceName, 
VersionCopyConfiguration config) {
+            this.sourceRoot = sourceRoot;
+            this.workspaceName = workspaceName;
+            this.config = config;
+        }
+
+        @Override
+        public Editor getRootEditor(NodeState before, NodeState after, 
NodeBuilder rootBuilder, CommitInfo info) throws CommitFailedException {
+            return new VersionableEditor(this, rootBuilder);
+        }
+    }
+
+    @Override
+    public Editor childNodeAdded(String name, NodeState after) throws 
CommitFailedException {
+        final String path = PathUtils.concat(this.path, name);
+        // skip deleted nodes and well known paths that may not contain 
versionable nodes
+        if (after == null || SKIPPED_PATHS.contains(path)) {
+            return null;
+        }
+
+        // assign path field only after checking that we don't skip this 
subtree
+        this.path = path;
+
+        final VersionCopyConfiguration c = provider.config;
+        if (isVersionable.apply(after)) {
+            final String versionableUuid = getProperty(after, JCR_UUID, 
Type.STRING);
+            if (c.isCopyVersions() && c.skipOrphanedVersionsCopy()) {
+                copyVersionHistory(after);
+            } else if (c.isCopyVersions() && !c.skipOrphanedVersionsCopy()) {
+                // all version histories have been copied, but maybe the date
+                // range for orphaned entries is narrower
+                if (c.getOrphanedMinDate().after(c.getVersionsMinDate())) {
+                    copyVersionHistory(after);
+                }
+            }
+
+            if (isVersionHistoryExists(versionableUuid)) {
+                setVersionablePath(versionableUuid);
+            } else {
+                NodeBuilder versionableBuilder = getNodeBuilder(rootBuilder, 
this.path);
+                removeVersionProperties(versionableBuilder);
+                if (isVersionable.apply(versionableBuilder.getNodeState())) {
+                    logger.warn("Node {} is still versionable. Creating empty 
version history.", path);
+                    createEmptyHistory(versionableBuilder);
+                }
+            }
+        }
+
+        return this;
+    }
+
+    private boolean copyVersionHistory(NodeState versionable) {
+        assert versionable.exists();
+
+        final String versionableUuid = 
versionable.getProperty(JCR_UUID).getValue(Type.STRING);
+        return versionCopier.copyVersionHistory(versionableUuid, 
provider.config.getVersionsMinDate());
+    }
+
+    private void setVersionablePath(String versionableUuid) {
+        final NodeBuilder versionHistory = 
VersionHistoryUtil.getVersionHistoryBuilder(versionStorage, versionableUuid);
+        if (!versionHistory.hasProperty(provider.workspaceName)) {
+            versionHistory.setProperty(provider.workspaceName, path, 
Type.PATH);
+        }
+        addMixin(versionHistory, MIX_REP_VERSIONABLE_PATHS);
+    }
+
+    private boolean isVersionHistoryExists(String versionableUuid) {
+        return getVersionHistoryBuilder(versionStorage, 
versionableUuid).exists();
+    }
+
+    private void removeVersionProperties(final NodeBuilder versionableBuilder) 
{
+        assert versionableBuilder.exists();
+
+        removeMixin(versionableBuilder, MIX_VERSIONABLE);
+
+        // we don't know if the UUID is otherwise referenced,
+        // so make sure the node remains referencable
+        if (!isReferenceable.apply(versionableBuilder.getNodeState())) {
+            addMixin(versionableBuilder, MIX_REFERENCEABLE);
+        }
+
+        versionableBuilder.removeProperty(JCR_VERSIONHISTORY);
+        versionableBuilder.removeProperty(JCR_PREDECESSORS);
+        versionableBuilder.removeProperty(JCR_BASEVERSION);
+        versionableBuilder.removeProperty(JCR_ISCHECKEDOUT);
+    }
+
+    private void createEmptyHistory(NodeBuilder versionable) throws 
CommitFailedException {
+        vMgr.getOrCreateVersionHistory(versionable, 
Collections.<String,Object>emptyMap());
+    }
+
+    @Override
+    public Editor childNodeChanged(String name, NodeState before, NodeState 
after) throws CommitFailedException {
+        return childNodeAdded(name, after);
+    }
+
+    @Override
+    public Editor childNodeDeleted(String name, NodeState before) throws 
CommitFailedException {
+        return childNodeAdded(name, null);
+    }
+
+    @Override
+    public void leave(NodeState before, NodeState after) throws 
CommitFailedException {
+        this.path = PathUtils.getParentPath(this.path);
+    }
+
+    private static <T> T getProperty(NodeState state, String name, Type<T> 
type) {
+        if (state.hasProperty(name)) {
+            return state.getProperty(name).getValue(type);
+        }
+        return null;
+    }
+
+    private static NodeBuilder getNodeBuilder(NodeBuilder root, String path) {
+        NodeBuilder builder = root;
+        for (String name : PathUtils.elements(path)) {
+            builder = builder.getChildNode(name);
+        }
+        return builder;
+    }
+
+    private static void addMixin(NodeBuilder builder, String name) {
+        if (builder.hasProperty(JCR_MIXINTYPES)) {
+            final Set<String> mixins = 
newHashSet(builder.getProperty(JCR_MIXINTYPES).getValue(Type.NAMES));
+            if (mixins.add(name)) {
+                builder.setProperty(nameProperty(JCR_MIXINTYPES, mixins));
+            }
+        } else {
+            builder.setProperty(nameProperty(JCR_MIXINTYPES, of(name)));
+        }
+    }
+
+    private static void removeMixin(NodeBuilder builder, String name) {
+        if (builder.hasProperty(JCR_MIXINTYPES)) {
+            final Set<String> mixins = 
newHashSet(builder.getProperty(JCR_MIXINTYPES).getValue(Type.NAMES));
+            if (mixins.remove(name)) {
+                if (mixins.isEmpty()) {
+                    builder.removeProperty(JCR_MIXINTYPES);
+                } else {
+                    builder.setProperty(nameProperty(JCR_MIXINTYPES, mixins));
+                }
+            }
+        }
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,188 @@
+/*
+ * 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.upgrade.version;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate;
+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.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.jackrabbit.JcrConstants.JCR_BASEVERSION;
+import static org.apache.jackrabbit.JcrConstants.JCR_FROZENMIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_ISCHECKEDOUT;
+import static org.apache.jackrabbit.JcrConstants.JCR_PREDECESSORS;
+import static org.apache.jackrabbit.JcrConstants.JCR_ROOTVERSION;
+import static org.apache.jackrabbit.JcrConstants.JCR_SUCCESSORS;
+import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONHISTORY;
+import static org.apache.jackrabbit.JcrConstants.MIX_VERSIONABLE;
+import static org.apache.jackrabbit.JcrConstants.NT_FROZENNODE;
+import static org.apache.jackrabbit.JcrConstants.NT_VERSION;
+import static org.apache.jackrabbit.oak.api.Type.NAMES;
+import static org.apache.jackrabbit.oak.api.Type.REFERENCE;
+import static org.apache.jackrabbit.oak.api.Type.REFERENCES;
+import static 
org.apache.jackrabbit.oak.plugins.memory.MultiGenericPropertyState.nameProperty;
+import static 
org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getVersionHistoryNodeState;
+import static 
org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getVersionStorage;
+
+/**
+ * The VersionablePropertiesEditor adds missing versionable properties.
+ */
+public final class VersionablePropertiesEditor extends DefaultEditor {
+
+    private static final String MIX_SIMPLE_VERSIONABLE = 
"mix:simpleVersionable";
+
+    private static final Logger log = 
LoggerFactory.getLogger(VersionablePropertiesEditor.class);
+
+    private final NodeBuilder rootBuilder;
+
+    private final NodeBuilder versionStorage;
+
+    private final NodeBuilder builder;
+
+    private final TypePredicate isVersionable;
+
+    private final TypePredicate isSimpleVersionable;
+
+    private final TypePredicate isNtVersion;
+
+    private final TypePredicate isFrozenNode;
+
+    private VersionablePropertiesEditor(NodeBuilder rootBuilder) {
+        this.builder = rootBuilder;
+        this.rootBuilder = rootBuilder;
+        this.versionStorage = getVersionStorage(rootBuilder);
+        this.isVersionable = new TypePredicate(rootBuilder.getNodeState(), 
MIX_VERSIONABLE);
+        this.isSimpleVersionable = new 
TypePredicate(rootBuilder.getNodeState(), MIX_SIMPLE_VERSIONABLE);
+        this.isNtVersion = new TypePredicate(rootBuilder.getNodeState(), 
NT_VERSION);
+        this.isFrozenNode = new TypePredicate(rootBuilder.getNodeState(), 
NT_FROZENNODE);
+    }
+
+    private VersionablePropertiesEditor(VersionablePropertiesEditor parent, 
NodeBuilder builder) {
+        this.builder = builder;
+        this.rootBuilder = parent.rootBuilder;
+        this.versionStorage = parent.versionStorage;
+        this.isVersionable = parent.isVersionable;
+        this.isSimpleVersionable = parent.isSimpleVersionable;
+        this.isNtVersion = parent.isNtVersion;
+        this.isFrozenNode = parent.isFrozenNode;
+    }
+
+    public static class Provider implements EditorProvider {
+        @Override
+        public Editor getRootEditor(NodeState before, NodeState after, 
NodeBuilder builder, CommitInfo info)
+                throws CommitFailedException {
+            return new VersionablePropertiesEditor(builder);
+        }
+
+        @Override
+        public String toString() {
+            return "VersionablePropertiesEditor";
+        }
+    }
+
+    @Override
+    public Editor childNodeAdded(String name, NodeState after) throws 
CommitFailedException {
+        NodeBuilder nodeBuilder = builder.getChildNode(name);
+        if (isVersionable.apply(after)) {
+            fixProperties(nodeBuilder);
+        } else if (isFrozenNode.apply(after)) {
+            updateFrozenMixins(nodeBuilder);
+        }
+        return new VersionablePropertiesEditor(this, nodeBuilder);
+    }
+
+    @Override
+    public Editor childNodeChanged(String name, NodeState before, NodeState 
after) throws CommitFailedException {
+        return childNodeAdded(name, after);
+    }
+
+    private static boolean updateFrozenMixins(NodeBuilder builder) {
+        if (builder.hasProperty(JCR_FROZENMIXINTYPES)) {
+            final Set<String> mixins = 
newHashSet(builder.getProperty(JCR_FROZENMIXINTYPES).getValue(NAMES));
+            if (mixins.remove(MIX_SIMPLE_VERSIONABLE)) {
+                mixins.add(MIX_VERSIONABLE);
+                builder.setProperty(nameProperty(JCR_FROZENMIXINTYPES, 
mixins));
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void fixProperties(NodeBuilder node) {
+        NodeState versionHistory = 
getVersionHistoryNodeState(versionStorage.getNodeState(), 
node.getString(JCR_UUID));
+        if (!versionHistory.exists()) {
+            log.warn("No version history for {}", node);
+            return;
+        }
+
+        Set<String> updated = new HashSet<String>();
+        if (!node.hasProperty(JCR_VERSIONHISTORY)) {
+            node.setProperty(JCR_VERSIONHISTORY, 
versionHistory.getString(JCR_UUID), REFERENCE);
+            updated.add(JCR_VERSIONHISTORY);
+        }
+
+        String baseVersion = null;
+        if (!node.hasProperty(JCR_BASEVERSION)) {
+            baseVersion = getLastVersion(versionHistory);
+            node.setProperty(JCR_BASEVERSION, baseVersion, REFERENCE);
+            updated.add(JCR_BASEVERSION);
+        }
+
+        if (!node.hasProperty(JCR_PREDECESSORS)) {
+            baseVersion = baseVersion == null ? getLastVersion(versionHistory) 
: baseVersion;
+
+            List<String> predecessors = new ArrayList<String>();
+            if (node.getBoolean(JCR_ISCHECKEDOUT)) {
+                predecessors.add(baseVersion);
+            }
+            node.setProperty(JCR_PREDECESSORS, predecessors, REFERENCES);
+            updated.add(JCR_PREDECESSORS);
+        }
+
+        if (!updated.isEmpty()) {
+            log.info("Updated versionable properties {} for {}", updated, 
node);
+        }
+    }
+
+    private String getLastVersion(NodeState versionHistory) {
+        NodeState lastVersion = versionHistory.getChildNode(JCR_ROOTVERSION);
+        for (ChildNodeEntry child : versionHistory.getChildNodeEntries()) {
+            NodeState v = child.getNodeState();
+            if (!isNtVersion.apply(v)) {
+                continue;
+            }
+            if (v.getProperty(JCR_SUCCESSORS).count() == 0) { // no successors
+                lastVersion = v;
+            }
+        }
+        return lastVersion.getString(JCR_UUID);
+    }
+}

Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/logback.xml
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/logback.xml?rev=1792993&view=auto
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/logback.xml 
(added)
+++ jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/logback.xml Fri 
Apr 28 07:16:13 2017
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  ~ 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.
+  -->
+<configuration>
+
+  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+    <!-- encoders are assigned the type
+         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+    <encoder>
+      <pattern>%d{dd.MM.yyyy HH:mm:ss.SSS} [%thread] *%level*  %logger - 
%message%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="info">
+    <appender-ref ref="CONSOLE" />
+  </root>
+</configuration>
\ No newline at end of file

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/upgrade_usage.txt
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/upgrade_usage.txt?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/upgrade_usage.txt 
(added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/upgrade_usage.txt 
Fri Apr 28 07:16:13 2017
@@ -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.
+
+Usage: ${command} [options] jcr2_source [destination]
+       (to upgrade a JCR 2 repository)
+
+       ${command} [options] source destination
+       (to migrate an Oak repository)
+
+# jcr2_source
+
+It's a path to the repository directory and optionally the repository.xml file,
+for instance:
+
+  crx-quickstart/repository
+  my-instance/repository somewhere-else/repository.xml
+
+If there is no destination, repository will be converted to a segment node 
store
+in-place. Old files will be moved to the repository/crx2 directory.
+
+# source / destination
+
+An descriptor of the Oak node store. Possible options:
+
+  * path to the segment-tar store
+  * segment-old:/path/to/classic/segment
+  * jdbc:... (requires passing username and password as separate parameters)
+  * mongodb://host:port/database
+
+# node store options
+
+--cache <int>             Cache size in MB (default: 256)
+--disable-mmap            Disable memory mapped file access for Segment Store
+
+--src-password            Source rdb password
+--src-user                Source rdb user
+--user                    Target rdb user
+--password                Target rdb password
+
+# migration options
+
+--copy-binaries           Copy binary content. Use this to disable use of
+                            existing DataStore in new repo
+--early-shutdown          Shutdown the source JCR2 repository after nodes are
+                            copied and before the commit hooks are applied
+--fail-on-error           Fail completely if nodes can't be read from the
+                            source repo
+--ignore-missing-binaries Proceed with the migration even if the binaries
+                            are missing
+
+# version store options
+
+--copy-orphaned-versions  Allows to skip copying orphaned versions. Parameters:
+                            { true | false | yyyy-mm-dd }. Defaults to true.
+--copy-versions           Copy the version storage. Parameters:
+                            { true | false | yyyy-mm-dd }. Defaults to true.
+
+# paths options
+
+--include-paths           Comma-separated list of paths to include during copy.
+--merge-paths             Comma-separated list of paths to merge during copy.
+--exclude-paths           Comma-separated list of paths to exclude during copy.
+
+# source blob store options
+
+--src-datastore           Datastore directory to be used as a source
+                            FileDataStore
+--src-fileblobstore       Datastore directory to be used as a source
+                            FileBlobStore
+--src-s3datastore         Datastore directory to be used for the source S3
+--src-s3config            Configuration file for the source S3DataStore
+
+# destination blob store options
+
+--datastore               Datastore directory to be used as a target
+                            FileDataStore
+--fileblobstore           Datastore directory to be used as a target
+                            FileBlobStore
+--s3datastore             Datastore directory to be used for the target S3
+--s3config                Configuration file for the target S3DataStore
+
+# other options
+
+-?, -h, --help            Show help
+--skip-init               Don't initialize the destination repository
+--skip-name-check         Don't look for long-named nodes at the beginning of
+                          the migration
\ No newline at end of file

Modified: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java?rev=1792993&r1=1792992&r2=1792993&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java
 Fri Apr 28 07:16:13 2017
@@ -18,7 +18,11 @@
  */
 package org.apache.jackrabbit.oak.upgrade;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
@@ -30,75 +34,151 @@ import javax.jcr.SimpleCredentials;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.api.JackrabbitRepository;
 import org.apache.jackrabbit.api.JackrabbitSession;
 import org.apache.jackrabbit.core.RepositoryImpl;
 import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.apache.jackrabbit.mk.core.MicroKernelImpl;
 import org.apache.jackrabbit.oak.Oak;
 import org.apache.jackrabbit.oak.jcr.Jcr;
-import org.apache.jackrabbit.oak.kernel.KernelNodeStore;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.apache.jackrabbit.oak.stats.Clock;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
 
 public abstract class AbstractRepositoryUpgradeTest {
 
-    protected static final Credentials CREDENTIALS = new 
SimpleCredentials("admin", "admin".toCharArray());
+    public static final Credentials CREDENTIALS = new 
SimpleCredentials("admin", "admin".toCharArray());
+
+    protected static NodeStore targetNodeStore;
+
+    private static File testDirectory;
 
-    private static Repository targetRepository;
+    private JackrabbitRepository targetRepository;
 
     @BeforeClass
-    public static void init() {
+    public static void init() throws InterruptedException {
         // ensure that we create a new repository for the next test
-        targetRepository = null;
+        targetNodeStore = null;
+        testDirectory = createTestDirectory();
+    }
+
+    protected static File createTestDirectory() throws InterruptedException {
+        final File dir = new File("target", "upgrade-" + 
Clock.SIMPLE.getTimeIncreasing());
+        FileUtils.deleteQuietly(dir);
+        return dir;
+    }
+
+    protected NodeStore createTargetNodeStore() {
+        return new SegmentNodeStore();
     }
 
     @Before
     public synchronized void upgradeRepository() throws Exception {
-        if (targetRepository == null) {
-            File directory = new File(
-                    "target", "upgrade-" + Clock.SIMPLE.getTimeIncreasing());
-            FileUtils.deleteQuietly(directory);
-
+        if (targetNodeStore == null) {
+            File directory = getTestDirectory();
             File source = new File(directory, "source");
             source.mkdirs();
-
-            InputStream repoConfig = getRepositoryConfig();
-            RepositoryConfig config;
-            if (repoConfig == null) {
-                config = RepositoryConfig.install(source);
-            } else {
-                OutputStream out = FileUtils.openOutputStream(new File(source, 
"repository.xml"));
-                IOUtils.copy(repoConfig, out);
-                out.close();
-                repoConfig.close();
-                config = RepositoryConfig.create(source);
-            }
-            RepositoryImpl repository = RepositoryImpl.create(config);
+            RepositoryImpl repository = createSourceRepository(source);
+            Session session = repository.login(CREDENTIALS);
             try {
-                createSourceContent(repository);
+                createSourceContent(session);
             } finally {
+                session.save();
+                session.logout();
                 repository.shutdown();
             }
-            NodeStore target = new KernelNodeStore(new MicroKernelImpl());
-            RepositoryUpgrade.copy(source, target);
-            targetRepository = new Jcr(new Oak(target)).createRepository();
+            final NodeStore target = getTargetNodeStore();
+            doUpgradeRepository(source, target);
+            targetNodeStore = target;
+        }
+    }
+
+    @After
+    public synchronized void shutdownTargetRepository() {
+        if (targetRepository != null) {
+            targetRepository.shutdown();
+            targetRepository = null;
+        }
+    }
+
+    protected synchronized NodeStore getTargetNodeStore() {
+        if (targetNodeStore == null) {
+            targetNodeStore = createTargetNodeStore();
+        }
+        return targetNodeStore;
+    }
+
+    protected static File getTestDirectory() {
+        return testDirectory;
+    }
+
+    protected RepositoryImpl createSourceRepository(File repositoryHome) 
throws IOException, RepositoryException {
+        InputStream repoConfig = getRepositoryConfig();
+        RepositoryConfig config;
+        if (repoConfig == null) {
+            config = RepositoryConfig.install(repositoryHome);
+        } else {
+            OutputStream out = FileUtils.openOutputStream(new 
File(repositoryHome, "repository.xml"));
+            IOUtils.copy(repoConfig, out);
+            out.close();
+            repoConfig.close();
+            config = RepositoryConfig.create(repositoryHome);
         }
+        return RepositoryImpl.create(config);
+    }
+
+
+    protected void doUpgradeRepository(File source, NodeStore target)throws 
RepositoryException, IOException{
+        RepositoryUpgrade.copy(source, target);
     }
 
-    public InputStream getRepositoryConfig() {
+    public InputStream getRepositoryConfig(){
         return null;
     }
 
     public Repository getTargetRepository() {
+        if (targetRepository == null) {
+            targetRepository = (JackrabbitRepository) new Jcr(new Oak(
+                    targetNodeStore)).createRepository();
+        }
         return targetRepository;
     }
 
-    public JackrabbitSession createAdminSession() throws RepositoryException {
-        return (JackrabbitSession) getTargetRepository().login(CREDENTIALS);
+    public JackrabbitSession createAdminSession()throws RepositoryException{
+        return(JackrabbitSession)getTargetRepository().login(CREDENTIALS);
     }
 
-    protected abstract void createSourceContent(Repository repository) throws 
Exception;
+    protected abstract void createSourceContent(Session session) throws 
Exception;
 
+    protected void assertExisting(final String... paths) throws 
RepositoryException {
+        final Session session = createAdminSession();
+        try {
+            assertExisting(session, paths);
+        } finally {
+            session.logout();
+        }
+    }
+
+    protected void assertExisting(final Session session, final String... 
paths) throws RepositoryException {
+        for (final String path : paths) {
+            assertTrue("node " + path + " should exist", 
session.nodeExists(path));
+        }
+    }
+
+    protected void assertMissing(final String... paths) throws 
RepositoryException {
+        final Session session = createAdminSession();
+        try {
+            assertMissing(session, paths);
+        } finally {
+            session.logout();
+        }
+    }
+
+    protected void assertMissing(final Session session, final String... paths) 
throws RepositoryException {
+        for (final String path : paths) {
+            assertFalse("node " + path + " should not exist", 
session.nodeExists(path));
+        }
+    }
 }

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,127 @@
+/*
+ * 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.upgrade;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Test;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import java.io.InputStream;
+
+import static org.junit.Assert.assertThat;
+
+public class AuthorizableFolderEditorTest extends 
AbstractRepositoryUpgradeTest {
+
+    // this repository config sets the groupsPath and usersPath to match
+    // this tests expectations
+    private static final String REPOSITORY_XML_FILE = 
"repository-groupmember.xml";
+
+    private static final String TEST_GROUP = 
"AuthorizableFolderEditorTest-Group";
+
+    private static final String TEST_USER = 
"AuthorizableFolderEditorTest-User";
+
+    private static final String HOME_PATH = "/home";
+
+    private static final String GROUPS_PATH = HOME_PATH + "/groups";
+
+    private static final String USERS_PATH = HOME_PATH + "/users";
+
+    private static final String CONTROL_PATH = HOME_PATH + "/control";
+
+    @Override
+    protected void createSourceContent(final Session session) throws Exception 
{
+        UserManager userMgr = ((JackrabbitSession) session).getUserManager();
+        userMgr.autoSave(false);
+        Group group = userMgr.createGroup(TEST_GROUP);
+        User user = userMgr.createUser(TEST_USER, "secret");
+        group.addMember(user);
+        session.save();
+
+        // simulate the error, set node types to incorrect values
+        Node home = session.getNode("/home");
+        home.setPrimaryType(JcrConstants.NT_UNSTRUCTURED);
+        home.getNode("users").setPrimaryType(JcrConstants.NT_UNSTRUCTURED);
+        home.getNode("groups").setPrimaryType(JcrConstants.NT_UNSTRUCTURED);
+        home.addNode("control", JcrConstants.NT_UNSTRUCTURED);
+        session.save();
+    }
+
+    @Override
+    public InputStream getRepositoryConfig() {
+        return 
getClass().getClassLoader().getResourceAsStream(REPOSITORY_XML_FILE);
+    }
+
+    @Test
+    public void verifyCorrectedNodeTypes() throws RepositoryException {
+        final Session session = createAdminSession();
+        assertExisting(session, HOME_PATH, USERS_PATH, GROUPS_PATH, 
CONTROL_PATH);
+
+        assertThat(session.getNode(HOME_PATH), 
hasNodeType(UserConstants.NT_REP_AUTHORIZABLE_FOLDER));
+        assertThat(session.getNode(USERS_PATH), 
hasNodeType(UserConstants.NT_REP_AUTHORIZABLE_FOLDER));
+        assertThat(session.getNode(GROUPS_PATH), 
hasNodeType(UserConstants.NT_REP_AUTHORIZABLE_FOLDER));
+        assertThat(session.getNode(CONTROL_PATH), 
hasNodeType(JcrConstants.NT_UNSTRUCTURED));
+    }
+
+    private static Matcher<? super Node> hasNodeType(final String 
expectedNodeType) {
+        return new TypeSafeMatcher<Node>() {
+
+            private String path;
+
+            @Override
+            protected boolean matchesSafely(final Node node) {
+                path = getPath(node);
+                return getNodeTypeName(node).equals(expectedNodeType);
+            }
+
+            @Override
+            public void describeTo(final Description description) {
+                description.appendText("the node " + path + " to be of type 
").appendValue(expectedNodeType);
+            }
+
+            @Override
+            protected void describeMismatchSafely(final Node node, final 
Description mismatchDescription) {
+                mismatchDescription.appendText(" was 
").appendValue(getNodeTypeName(node));
+            }
+
+            private String getNodeTypeName(final Node node) {
+                try {
+                    return node.getPrimaryNodeType().getName();
+                } catch (RepositoryException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+
+            private String getPath(final Node node) {
+                try {
+                    return node.getPath();
+                } catch (RepositoryException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,138 @@
+/*
+ * 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.upgrade;
+
+import static org.apache.jackrabbit.JcrConstants.MIX_VERSIONABLE;
+import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Credentials;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.version.VersionHistory;
+import javax.jcr.version.VersionIterator;
+import javax.jcr.version.VersionManager;
+
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.commons.cnd.CndImporter;
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.jcr.Jcr;
+import org.apache.jackrabbit.oak.jcr.repository.RepositoryImpl;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.upgrade.util.NodeStateTestUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class BrokenVersionableTest {
+
+    private static final Credentials CREDENTIALS = new 
SimpleCredentials("admin", "admin".toCharArray());
+
+    private NodeStore targetNodeStore;
+
+    private RepositoryImpl targetRepository;
+
+    @Before
+    public synchronized void upgradeRepository() throws Exception {
+        targetNodeStore = new SegmentNodeStore();
+        targetRepository = (RepositoryImpl) new Jcr(new 
Oak(targetNodeStore)).createRepository();
+        NodeStore source = createSourceContent();
+        RepositorySidegrade sidegrade = new RepositorySidegrade(source, 
targetNodeStore);
+        sidegrade.setCopyOrphanedVersions(null);
+        sidegrade.copy();
+    }
+
+    @After
+    public synchronized void shutdownRepository() {
+        targetRepository.shutdown();
+        targetRepository = null;
+        targetNodeStore = null;
+    }
+
+    private NodeStore createSourceContent() throws Exception {
+        SegmentNodeStore source = new SegmentNodeStore();
+        RepositoryImpl repository = (RepositoryImpl) new Jcr(new 
Oak(source)).createRepository();
+        Session session = repository.login(CREDENTIALS);
+        List<String> versionHistoryPaths = new ArrayList<String>();
+        try {
+            CndImporter.registerNodeTypes(new StringReader("<test = 
'http://jackrabbit.apache.org/ns/test'>\n"
+                    + "[test:Versionable] > nt:unstructured, 
mix:versionable"), session);
+
+            Node root = session.getRootNode();
+
+            Node versionable1 = root.addNode("versionable1", NT_UNSTRUCTURED);
+            versionable1.addMixin(MIX_VERSIONABLE);
+            versionable1.addNode("child", NT_UNSTRUCTURED);
+
+            Node versionable2 = root.addNode("versionable2", 
"test:Versionable");
+            versionable2.addNode("child", NT_UNSTRUCTURED);
+
+            session.save();
+
+            VersionManager vMgr = session.getWorkspace().getVersionManager();
+            vMgr.checkin("/versionable1");
+            vMgr.checkin("/versionable2");
+            
versionHistoryPaths.add(vMgr.getVersionHistory("/versionable1").getPath());
+            
versionHistoryPaths.add(vMgr.getVersionHistory("/versionable2").getPath());
+        } finally {
+            session.logout();
+            repository.shutdown();
+        }
+
+        // remove version history to corrupt the JCR repository structure
+        NodeBuilder rootBuilder = source.getRoot().builder();
+        for (String versionHistoryPath : versionHistoryPaths) {
+            NodeStateTestUtils.createOrGetBuilder(rootBuilder, 
versionHistoryPath).remove();
+        }
+        source.merge(rootBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        return source;
+    }
+
+    public JackrabbitSession createAdminSession() throws RepositoryException {
+        return (JackrabbitSession) targetRepository.login(CREDENTIALS);
+    }
+
+    @Test
+    public void verifyNoVersionable() throws RepositoryException {
+        Session session = createAdminSession();
+        VersionManager vMgr = session.getWorkspace().getVersionManager();
+        try {
+            
assertFalse(session.getNode("/versionable1").isNodeType(MIX_VERSIONABLE));
+
+            Node versionable2 = session.getNode("/versionable2");
+            assertTrue(versionable2.isNodeType(MIX_VERSIONABLE));
+            VersionHistory history = 
vMgr.getVersionHistory(versionable2.getPath());
+            VersionIterator versions = history.getAllVersions();
+            assertEquals("jcr:rootVersion", versions.nextVersion().getName());
+            assertFalse(versions.hasNext());
+        } finally {
+            session.logout();
+        }
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyCheckpointsTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyCheckpointsTest.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyCheckpointsTest.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyCheckpointsTest.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,166 @@
+/*
+ * 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.upgrade;
+
+import com.google.common.base.Joiner;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.upgrade.cli.AbstractOak2OakTest;
+import org.apache.jackrabbit.oak.upgrade.cli.OakUpgrade;
+import org.apache.jackrabbit.oak.upgrade.cli.container.BlobStoreContainer;
+import org.apache.jackrabbit.oak.upgrade.cli.container.FileDataStoreContainer;
+import org.apache.jackrabbit.oak.upgrade.cli.container.NodeStoreContainer;
+import 
org.apache.jackrabbit.oak.upgrade.cli.container.SegmentNodeStoreContainer;
+import org.apache.jackrabbit.oak.upgrade.cli.parser.CliArgumentException;
+import org.apache.jackrabbit.oak.upgrade.cli.parser.DatastoreArguments;
+import org.apache.jackrabbit.oak.upgrade.cli.parser.MigrationCliArguments;
+import org.apache.jackrabbit.oak.upgrade.cli.parser.MigrationOptions;
+import org.apache.jackrabbit.oak.upgrade.cli.parser.OptionParserFactory;
+import org.apache.jackrabbit.oak.upgrade.cli.parser.StoreArguments;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.fail;
+
+@RunWith(Parameterized.class)
+public class CopyCheckpointsTest extends AbstractOak2OakTest {
+
+    private enum Result {
+        EXCEPTION, CHECKPOINTS_MISSING, CHECKPOINTS_COPIED
+    }
+
+    private static final Logger log = 
LoggerFactory.getLogger(CopyCheckpointsTest.class);
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() throws IOException {
+        List<Object[]> params = new ArrayList<Object[]>();
+
+        BlobStoreContainer blob = new FileDataStoreContainer();
+        params.add(new Object[]{
+                "Fails on missing blobstore",
+                new SegmentNodeStoreContainer(blob),
+                new SegmentNodeStoreContainer(blob),
+                asList(),
+                Result.EXCEPTION
+        });
+        params.add(new Object[]{
+                "Suppress the warning",
+                new SegmentNodeStoreContainer(blob),
+                new SegmentNodeStoreContainer(blob),
+                asList("--skip-checkpoints"),
+                Result.CHECKPOINTS_MISSING
+        });
+        params.add(new Object[]{
+                "Source data store defined, checkpoints migrated",
+                new SegmentNodeStoreContainer(blob),
+                new SegmentNodeStoreContainer(blob),
+                asList("--src-datastore=" + blob.getDescription()),
+                Result.CHECKPOINTS_COPIED
+        });
+        return params;
+    }
+
+    private final NodeStoreContainer source;
+
+    private final NodeStoreContainer destination;
+
+    private final List<String> args;
+
+    private final Result expectedResult;
+
+    public CopyCheckpointsTest(String name, NodeStoreContainer source, 
NodeStoreContainer destination, List<String> args, Result expectedResult) 
throws IOException, CliArgumentException {
+        this.source = source;
+        this.destination = destination;
+        this.args = args;
+        this.expectedResult = expectedResult;
+
+        this.source.clean();
+        this.destination.clean();
+    }
+
+    @Override
+    protected NodeStoreContainer getSourceContainer() {
+        return source;
+    }
+
+    @Override
+    protected NodeStoreContainer getDestinationContainer() {
+        return destination;
+    }
+
+    @Override
+    protected String[] getArgs() {
+        List<String> result = new ArrayList<String>(args);
+        result.addAll(asList("--disable-mmap", source.getDescription(), 
destination.getDescription()));
+        return result.toArray(new String[result.size()]);
+    }
+
+    @Before
+    @Override
+    public void prepare() throws Exception {
+        NodeStore source = getSourceContainer().open();
+        try {
+            initContent(source);
+        } finally {
+            getSourceContainer().close();
+        }
+
+        String[] args = getArgs();
+        log.info("oak2oak {}", Joiner.on(' ').join(args));
+        try {
+            MigrationCliArguments cliArgs = new 
MigrationCliArguments(OptionParserFactory.create().parse(args));
+            MigrationOptions options = new MigrationOptions(cliArgs);
+            StoreArguments stores = new StoreArguments(options, 
cliArgs.getArguments());
+            DatastoreArguments datastores = new DatastoreArguments(options, 
stores, stores.srcUsesEmbeddedDatastore());
+            OakUpgrade.migrate(options, stores, datastores);
+        } catch(RuntimeException e) {
+            if (expectedResult == Result.EXCEPTION) {
+                return;
+            } else {
+                throw e;
+            }
+        }
+        if (expectedResult == Result.EXCEPTION) {
+            fail("Migration should fail");
+        }
+        createSession();
+    }
+
+    @Test
+    @Override
+    public void validateMigration() throws RepositoryException, IOException, 
CliArgumentException {
+        switch (expectedResult) {
+            case CHECKPOINTS_COPIED:
+                verifyCheckpoint();
+                break;
+
+            case CHECKPOINTS_MISSING:
+                verifyEmptyAsync();
+                break;
+        }
+    }
+}
\ No newline at end of file

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyNodeTypesUpgradeTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyNodeTypesUpgradeTest.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyNodeTypesUpgradeTest.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyNodeTypesUpgradeTest.java
 Fri Apr 28 07:16:13 2017
@@ -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.upgrade;
+
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.commons.cnd.CndImporter;
+import org.junit.Test;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.PropertyDefinition;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import static org.junit.Assert.assertEquals;
+
+public class CopyNodeTypesUpgradeTest extends AbstractRepositoryUpgradeTest {
+
+    @Override
+    protected void createSourceContent(Session session) throws Exception {
+        final Reader cnd = new 
InputStreamReader(getClass().getResourceAsStream("/test-nodetypes.cnd"));
+        CndImporter.registerNodeTypes(cnd, session);
+    }
+
+    @Test
+    public void customNodeTypesAreRegistered() throws RepositoryException {
+        final JackrabbitSession adminSession = createAdminSession();
+        final NodeTypeManager nodeTypeManager = 
adminSession.getWorkspace().getNodeTypeManager();
+        final NodeType testFolderNodeType = 
nodeTypeManager.getNodeType("test:Folder");
+        final NodeDefinition[] cnd = 
testFolderNodeType.getChildNodeDefinitions();
+        final PropertyDefinition[] pd = 
testFolderNodeType.getPropertyDefinitions();
+        assertEquals("More than one child node definition", 1, cnd.length);
+        assertEquals("Incorrect default primary type", "test:Folder", 
cnd[0].getDefaultPrimaryTypeName());
+        assertEquals("More than two property definitions", 4, pd.length);
+        adminSession.logout();
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistorySidegradeTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistorySidegradeTest.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistorySidegradeTest.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistorySidegradeTest.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,64 @@
+/*
+ * 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.upgrade;
+
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.jcr.Jcr;
+import org.apache.jackrabbit.oak.jcr.repository.RepositoryImpl;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import 
org.apache.jackrabbit.oak.upgrade.util.VersionCopyTestUtils.VersionCopySetup;
+import org.junit.Before;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import java.io.IOException;
+
+public class CopyVersionHistorySidegradeTest extends CopyVersionHistoryTest {
+
+    private static NodeStore sourceNodeStore;
+
+    @Before
+    @Override
+    public void upgradeRepository() throws Exception {
+        if (sourceNodeStore == null) {
+            sourceNodeStore = new MemoryNodeStore();
+            RepositoryImpl repository = (RepositoryImpl) new Jcr(new 
Oak(sourceNodeStore)).createRepository();
+            Session session = repository.login(CREDENTIALS);
+            try {
+                createSourceContent(session);
+            } finally {
+                session.logout();
+                repository.shutdown();
+            }
+        }
+    }
+
+    @Override
+    protected Session performCopy(VersionCopySetup setup) throws 
RepositoryException, IOException {
+        final NodeStore targetNodeStore = new MemoryNodeStore();
+        final RepositorySidegrade sidegrade = new 
RepositorySidegrade(sourceNodeStore, targetNodeStore);
+        setup.setup(sidegrade.versionCopyConfiguration);
+        sidegrade.copy();
+
+        repository = (RepositoryImpl) new Jcr(new 
Oak(targetNodeStore)).createRepository();
+        Session s = 
repository.login(AbstractRepositoryUpgradeTest.CREDENTIALS);
+        sessions.add(s);
+        return s;
+    }
+}


Reply via email to