Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java
 Fri Apr 28 07:18:26 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.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java
 Fri Apr 28 07:18:26 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.2/oak-upgrade/src/main/resources/logback.xml
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/resources/logback.xml?rev=1792995&view=auto
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-upgrade/src/main/resources/logback.xml 
(added)
+++ jackrabbit/oak/branches/1.2/oak-upgrade/src/main/resources/logback.xml Fri 
Apr 28 07:18:26 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.2/oak-upgrade/src/main/resources/upgrade_usage.txt
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/resources/upgrade_usage.txt?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/resources/upgrade_usage.txt 
(added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/resources/upgrade_usage.txt 
Fri Apr 28 07:18:26 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.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java?rev=1792995&r1=1792994&r2=1792995&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java
 Fri Apr 28 07:18:26 2017
@@ -18,6 +18,9 @@
  */
 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;
@@ -31,6 +34,7 @@ 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;
@@ -39,20 +43,20 @@ import org.apache.jackrabbit.oak.jcr.Jcr
 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;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 public abstract class AbstractRepositoryUpgradeTest {
 
-    protected static final Credentials CREDENTIALS = new 
SimpleCredentials("admin", "admin".toCharArray());
+    public static final Credentials CREDENTIALS = new 
SimpleCredentials("admin", "admin".toCharArray());
 
-    private static NodeStore targetNodeStore;
+    protected static NodeStore targetNodeStore;
 
     private static File testDirectory;
 
+    private JackrabbitRepository targetRepository;
+
     @BeforeClass
     public static void init() throws InterruptedException {
         // ensure that we create a new repository for the next test
@@ -77,9 +81,12 @@ public abstract class AbstractRepository
             File source = new File(directory, "source");
             source.mkdirs();
             RepositoryImpl repository = createSourceRepository(source);
+            Session session = repository.login(CREDENTIALS);
             try {
-                createSourceContent(repository);
+                createSourceContent(session);
             } finally {
+                session.save();
+                session.logout();
                 repository.shutdown();
             }
             final NodeStore target = getTargetNodeStore();
@@ -88,6 +95,14 @@ public abstract class AbstractRepository
         }
     }
 
+    @After
+    public synchronized void shutdownTargetRepository() {
+        if (targetRepository != null) {
+            targetRepository.shutdown();
+            targetRepository = null;
+        }
+    }
+
     protected synchronized NodeStore getTargetNodeStore() {
         if (targetNodeStore == null) {
             targetNodeStore = createTargetNodeStore();
@@ -115,7 +130,7 @@ public abstract class AbstractRepository
     }
 
 
-    protected void doUpgradeRepository(File source, NodeStore target)throws 
RepositoryException{
+    protected void doUpgradeRepository(File source, NodeStore target)throws 
RepositoryException, IOException{
         RepositoryUpgrade.copy(source, target);
     }
 
@@ -123,37 +138,47 @@ public abstract class AbstractRepository
         return null;
     }
 
-    public Repository getTargetRepository(){
-        return new Jcr(new Oak(targetNodeStore)).createRepository();
+    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);
     }
 
-    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 {
-            for (final String path : paths) {
-                final String relPath = path.substring(1);
-                assertTrue("node " + path + " should exist", 
session.getRootNode().hasNode(relPath));
-            }
+            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 {
-            for (final String path : paths) {
-                final String relPath = path.substring(1);
-                assertFalse("node " + path + " should not exist", 
session.getRootNode().hasNode(relPath));
-            }
+            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.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java
 Fri Apr 28 07:18:26 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.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java
 Fri Apr 28 07:18:26 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.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyCheckpointsTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyCheckpointsTest.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyCheckpointsTest.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyCheckpointsTest.java
 Fri Apr 28 07:18:26 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

Modified: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyNodeTypesUpgradeTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyNodeTypesUpgradeTest.java?rev=1792995&r1=1792994&r2=1792995&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyNodeTypesUpgradeTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyNodeTypesUpgradeTest.java
 Fri Apr 28 07:18:26 2017
@@ -18,12 +18,8 @@ package org.apache.jackrabbit.oak.upgrad
 
 import org.apache.jackrabbit.api.JackrabbitSession;
 import org.apache.jackrabbit.commons.cnd.CndImporter;
-import org.apache.jackrabbit.commons.cnd.CompactNodeTypeDefReader;
 import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.nodetype.NodeDefinition;
@@ -32,15 +28,13 @@ import javax.jcr.nodetype.NodeTypeManage
 import javax.jcr.nodetype.PropertyDefinition;
 import java.io.InputStreamReader;
 import java.io.Reader;
-import java.io.StringReader;
 
 import static org.junit.Assert.assertEquals;
 
 public class CopyNodeTypesUpgradeTest extends AbstractRepositoryUpgradeTest {
 
     @Override
-    protected void createSourceContent(Repository repository) throws Exception 
{
-        final Session session = repository.login(CREDENTIALS);
+    protected void createSourceContent(Session session) throws Exception {
         final Reader cnd = new 
InputStreamReader(getClass().getResourceAsStream("/test-nodetypes.cnd"));
         CndImporter.registerNodeTypes(cnd, session);
     }
@@ -55,5 +49,6 @@ public class CopyNodeTypesUpgradeTest ex
         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.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistorySidegradeTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistorySidegradeTest.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistorySidegradeTest.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistorySidegradeTest.java
 Fri Apr 28 07:18:26 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;
+    }
+}

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,398 @@
+/*
+ * 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.JCR_PREDECESSORS;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONHISTORY;
+import static org.apache.jackrabbit.JcrConstants.MIX_VERSIONABLE;
+import static 
org.apache.jackrabbit.oak.plugins.version.VersionConstants.MIX_REP_VERSIONABLE_PATHS;
+import static 
org.apache.jackrabbit.oak.upgrade.util.VersionCopyTestUtils.createLabeledVersions;
+import static 
org.apache.jackrabbit.oak.upgrade.util.VersionCopyTestUtils.getOrAddNodeWithMixins;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionHistory;
+import javax.jcr.version.VersionManager;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.jackrabbit.core.RepositoryContext;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+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.state.NodeStore;
+import org.apache.jackrabbit.oak.upgrade.util.VersionCopyTestUtils;
+import 
org.apache.jackrabbit.oak.upgrade.util.VersionCopyTestUtils.VersionCopySetup;
+import org.apache.jackrabbit.oak.upgrade.version.VersionCopyConfiguration;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Test;
+
+public class CopyVersionHistoryTest extends AbstractRepositoryUpgradeTest {
+
+    private static final String VERSIONABLES_PATH_PREFIX = "/versionables/";
+
+    private static final String VERSIONABLES_OLD = "old";
+
+    private static final String VERSIONABLES_OLD_ORPHANED = "oldOrphaned";
+
+    private static final String VERSIONABLES_YOUNG = "young";
+
+    private static final String VERSIONABLES_YOUNG_ORPHANED = "youngOrphaned";
+
+    protected RepositoryImpl repository;
+
+    protected List<Session> sessions = Lists.newArrayList();
+
+    private static Calendar betweenHistories;
+
+    private static Map<String, String> pathToVersionHistory = 
Maps.newHashMap();
+
+    /**
+     * Home directory of source repository.
+     */
+    private static File source;
+
+    private static String[] MIXINS;
+
+    @Override
+    protected void createSourceContent(Session session) throws Exception {
+
+        if (hasSimpleVersioningSupport(session.getRepository())) {
+            MIXINS = new String[] { "mix:simpleVersionable", MIX_VERSIONABLE };
+        } else {
+            MIXINS = new String[] { MIX_VERSIONABLE };
+        }
+
+        final Node root = session.getRootNode();
+
+        for (final String mixinType : MIXINS) {
+            final Node parent = VersionCopyTestUtils.getOrAddNode(root, 
rel(VERSIONABLES_PATH_PREFIX + mixinType));
+
+            final Node oldNode = getOrAddNodeWithMixins(parent, 
VERSIONABLES_OLD, mixinType);
+            pathToVersionHistory.put(oldNode.getPath(), 
createLabeledVersions(oldNode));
+
+            final Node oldOrphanNode = getOrAddNodeWithMixins(parent, 
VERSIONABLES_OLD_ORPHANED, mixinType);
+            pathToVersionHistory.put(oldOrphanNode.getPath(), 
createLabeledVersions(oldOrphanNode));
+        }
+
+        Thread.sleep(10);
+        betweenHistories = Calendar.getInstance();
+        Thread.sleep(10);
+
+        for (final String mixinType : MIXINS) {
+            final Node parent = VersionCopyTestUtils.getOrAddNode(root, 
rel(VERSIONABLES_PATH_PREFIX + mixinType));
+
+            final Node youngNode = getOrAddNodeWithMixins(parent, 
VERSIONABLES_YOUNG, mixinType);
+            pathToVersionHistory.put(youngNode.getPath(), 
createLabeledVersions(youngNode));
+
+            final Node youngOrphanNode = getOrAddNodeWithMixins(parent, 
VERSIONABLES_YOUNG_ORPHANED, mixinType);
+            pathToVersionHistory.put(youngOrphanNode.getPath(), 
createLabeledVersions(youngOrphanNode));
+
+            // create orphaned version histories by deleting the original nodes
+            parent.getNode(VERSIONABLES_OLD_ORPHANED).remove();
+            parent.getNode(VERSIONABLES_YOUNG_ORPHANED).remove();
+        }
+
+        session.save();
+    }
+
+    private boolean hasSimpleVersioningSupport(final Repository repository) {
+        return 
Boolean.parseBoolean(repository.getDescriptor(Repository.OPTION_SIMPLE_VERSIONING_SUPPORTED));
+    }
+
+    @Override
+    protected void doUpgradeRepository(File source, NodeStore target) throws 
RepositoryException {
+        // abuse this method to capture the source repo directory
+        CopyVersionHistoryTest.source = source;
+    }
+
+    @AfterClass
+    public static void teardown() {
+        CopyVersionHistoryTest.pathToVersionHistory.clear();
+        CopyVersionHistoryTest.source = null;
+    }
+
+    @Test
+    public void copyAllVersions() throws RepositoryException, IOException {
+        Session session = performCopy(new VersionCopySetup() {
+            @Override
+            public void setup(VersionCopyConfiguration config) {
+                // copying all versions is enabled by default
+            }
+        });
+
+        assertVersionableProperties(session, VERSIONABLES_OLD, 
VERSIONABLES_YOUNG);
+        assertExistingHistories(session,
+                VERSIONABLES_OLD, VERSIONABLES_OLD_ORPHANED, 
VERSIONABLES_YOUNG, VERSIONABLES_YOUNG_ORPHANED);
+        assertVersionablePaths(session, VERSIONABLES_OLD, VERSIONABLES_YOUNG);
+        assertVersionsCanBeRestored(session, VERSIONABLES_OLD, 
VERSIONABLES_YOUNG);
+    }
+
+    @Test
+    public void referencedSinceDate() throws RepositoryException, IOException {
+        Session session = performCopy(new VersionCopySetup() {
+            @Override
+            public void setup(VersionCopyConfiguration config) {
+                config.setCopyVersions(betweenHistories);
+            }
+        });
+
+        assertVersionableProperties(session, VERSIONABLES_YOUNG);
+        assertExistingHistories(session, VERSIONABLES_YOUNG, 
VERSIONABLES_YOUNG_ORPHANED);
+        assertVersionablePaths(session, VERSIONABLES_YOUNG);
+        assertMissingHistories(session, VERSIONABLES_OLD, 
VERSIONABLES_OLD_ORPHANED);
+        assertVersionsCanBeRestored(session, VERSIONABLES_YOUNG);
+    }
+
+    @Test
+    public void referencedOlderThanOrphaned() throws RepositoryException, 
IOException {
+        Session session = performCopy(new VersionCopySetup() {
+            @Override
+            public void setup(VersionCopyConfiguration config) {
+                config.setCopyOrphanedVersions(betweenHistories);
+            }
+        });
+
+        assertVersionableProperties(session, VERSIONABLES_OLD, 
VERSIONABLES_YOUNG);
+        assertExistingHistories(session, VERSIONABLES_OLD, VERSIONABLES_YOUNG, 
VERSIONABLES_YOUNG_ORPHANED);
+        assertVersionablePaths(session, VERSIONABLES_OLD, VERSIONABLES_YOUNG);
+        assertMissingHistories(session, VERSIONABLES_OLD_ORPHANED);
+        assertVersionsCanBeRestored(session, VERSIONABLES_OLD, 
VERSIONABLES_YOUNG);
+    }
+
+    @Test
+    public void onlyReferenced() throws RepositoryException, IOException {
+        Session session = performCopy(new VersionCopySetup() {
+            @Override
+            public void setup(VersionCopyConfiguration config) {
+                config.setCopyOrphanedVersions(null);
+            }
+        });
+        assertVersionableProperties(session, VERSIONABLES_OLD, 
VERSIONABLES_YOUNG);
+        assertExistingHistories(session, VERSIONABLES_OLD, VERSIONABLES_YOUNG);
+        assertVersionablePaths(session, VERSIONABLES_OLD, VERSIONABLES_YOUNG);;
+        assertMissingHistories(session, VERSIONABLES_OLD_ORPHANED, 
VERSIONABLES_YOUNG_ORPHANED);
+        assertVersionsCanBeRestored(session, VERSIONABLES_OLD, 
VERSIONABLES_YOUNG);
+    }
+
+    @Test
+    public void onlyReferencedAfterDate() throws RepositoryException, 
IOException {
+        Session session = performCopy(new VersionCopySetup() {
+            @Override
+            public void setup(VersionCopyConfiguration config) {
+                config.setCopyVersions(betweenHistories);
+                config.setCopyOrphanedVersions(null);
+            }
+        });
+        assertVersionableProperties(session, VERSIONABLES_YOUNG);
+        assertExistingHistories(session, VERSIONABLES_YOUNG);
+        assertVersionablePaths(session, VERSIONABLES_YOUNG);
+        assertMissingHistories(session, VERSIONABLES_OLD, 
VERSIONABLES_OLD_ORPHANED, VERSIONABLES_YOUNG_ORPHANED);
+        assertVersionsCanBeRestored(session, VERSIONABLES_YOUNG);
+    }
+
+    @Test
+    public void overrideOrphaned() throws RepositoryException, IOException {
+        Session session = performCopy(new VersionCopySetup() {
+            @Override
+            public void setup(VersionCopyConfiguration config) {
+                config.setCopyVersions(null);
+                config.setCopyOrphanedVersions(betweenHistories);
+            }
+        });
+
+        assertMissingHistories(session,
+                VERSIONABLES_OLD, VERSIONABLES_OLD_ORPHANED, 
VERSIONABLES_YOUNG, VERSIONABLES_YOUNG_ORPHANED);
+    }
+
+    @Test
+    public void dontCopyVersionHistory() throws RepositoryException, 
IOException {
+        Session session = performCopy(new VersionCopySetup() {
+            @Override
+            public void setup(VersionCopyConfiguration config) {
+                config.setCopyVersions(null);
+                config.setCopyOrphanedVersions(null);
+            }
+        });
+
+        assertMissingHistories(session,
+                VERSIONABLES_OLD, VERSIONABLES_OLD_ORPHANED, 
VERSIONABLES_YOUNG, VERSIONABLES_YOUNG_ORPHANED);
+        assertNotNull(session.getNode("/jcr:system/jcr:versionStorage")
+                .getPrimaryNodeType());
+    }
+
+    protected Session performCopy(VersionCopySetup setup) throws 
RepositoryException, IOException {
+        final RepositoryConfig sourceConfig = RepositoryConfig.create(source);
+        final RepositoryContext sourceContext = 
RepositoryContext.create(sourceConfig);
+        final NodeStore targetNodeStore = new SegmentNodeStore();
+        try {
+            final RepositoryUpgrade upgrade = new 
RepositoryUpgrade(sourceContext, targetNodeStore);
+            setup.setup(upgrade.versionCopyConfiguration);
+            upgrade.setEarlyShutdown(true);
+            upgrade.copy(null);
+        } finally {
+            sourceContext.getRepository().shutdown();
+        }
+
+        repository = (RepositoryImpl) new Jcr(new 
Oak(targetNodeStore)).createRepository();
+        Session s = 
repository.login(AbstractRepositoryUpgradeTest.CREDENTIALS);
+        sessions.add(s);
+        return s;
+    }
+
+    @After
+    public void closeRepository() {
+        for (Session s : sessions) {
+            s.logout();
+        }
+        sessions.clear();
+        repository.shutdown();
+    }
+
+    private static String rel(final String path) {
+        if (path.startsWith("/")) {
+            return path.substring(1);
+        }
+        return path;
+    }
+
+    private static VersionHistory getVersionHistoryForPath(Session session, 
String path)
+            throws RepositoryException {
+        final Node root = session.getRootNode();
+        if (root.hasNode(rel(pathToVersionHistory.get(path)))) {
+            return 
(VersionHistory)session.getNode(pathToVersionHistory.get(path));
+        }
+        return null;
+    }
+
+    private static void assertVersionableProperties(final Session session, 
final String... names) throws RepositoryException {
+        VersionManager vMgr = session.getWorkspace().getVersionManager();
+        for (final String mixin : MIXINS) {
+            final String pathPrefix = VERSIONABLES_PATH_PREFIX + mixin + "/";
+            for (final String name : names) {
+                final String path = pathPrefix + name;
+                Node versionable = session.getNode(path);
+
+                String versionHistoryUuid = 
versionable.getProperty(JCR_VERSIONHISTORY).getString();
+                assertEquals(getVersionHistoryForPath(session, 
path).getIdentifier(), versionHistoryUuid);
+
+                final Version baseVersion = vMgr.getBaseVersion(path);
+                assertEquals("1.2", baseVersion.getName());
+                final Value[] predecessors = 
versionable.getProperty(JCR_PREDECESSORS).getValues();
+                assertEquals(1, predecessors.length);
+                assertEquals(baseVersion.getIdentifier(), 
predecessors[0].getString());
+            }
+        }
+    }
+
+    private static void assertExistingHistories(final Session session, final 
String... names)
+            throws RepositoryException {
+        for (final String mixin : MIXINS) {
+            final String pathPrefix = VERSIONABLES_PATH_PREFIX + mixin + "/";
+            for (final String name : names) {
+                final String path = pathPrefix + name;
+                final VersionHistory history = 
getVersionHistoryForPath(session, path);
+                assertNotNull("No history found for " + path, history);
+                VersionCopyTestUtils.assertLabeledVersions(history);
+            }
+        }
+    }
+
+    private static void assertMissingHistories(final Session session, final 
String... names)
+            throws RepositoryException {
+        for (final String mixin : MIXINS) {
+            final String pathPrefix = VERSIONABLES_PATH_PREFIX + mixin + "/";
+            for (final String name : names) {
+                final String path = pathPrefix + name;
+                final VersionHistory history = 
getVersionHistoryForPath(session, path);
+                assertNull("Should not have found history for " + path, 
history);
+            }
+        }
+    }
+
+    private static void assertVersionablePaths(final Session session, final 
String... names)
+            throws RepositoryException {
+        for (final String mixin : MIXINS) {
+            final String pathPrefix = VERSIONABLES_PATH_PREFIX + mixin + "/";
+            for (final String name : names) {
+                final String path = pathPrefix + name;
+                final Node node = session.getNode(path);
+                assertTrue("Node " + path + " should have mix:versionable 
mixin", node.isNodeType(MIX_VERSIONABLE));
+                final VersionHistory history = 
getVersionHistoryForPath(session, path);
+                assertVersionablePath(history, path);
+            }
+        }
+    }
+
+    private static void assertVersionablePath(final VersionHistory history, 
final String versionablePath)
+            throws RepositoryException {
+        final String workspaceName = 
history.getSession().getWorkspace().getName();
+        assertTrue(history.isNodeType(MIX_REP_VERSIONABLE_PATHS));
+        assertTrue(history.hasProperty(workspaceName));
+        final Property pathProperty = history.getProperty(workspaceName);
+        assertEquals(PropertyType.PATH, pathProperty.getType());
+        assertEquals(versionablePath, pathProperty.getString());
+    }
+
+    private static void assertVersionsCanBeRestored(final Session session, 
final String... names) throws RepositoryException {
+        VersionManager vMgr = session.getWorkspace().getVersionManager();
+        for (final String mixin : MIXINS) {
+            final String pathPrefix = VERSIONABLES_PATH_PREFIX + mixin + "/";
+            for (final String name : names) {
+                final String path = pathPrefix + name;
+                VersionHistory history = vMgr.getVersionHistory(path);
+                assertEquals("1.2", 
session.getNode(path).getProperty("version").getString());
+                vMgr.restore(history.getVersion("1.0"), false);
+
+                Node versionable = session.getNode(path);
+                assertEquals("1.0", 
versionable.getProperty("version").getString());
+
+                // restored node should have correct properties
+                String versionHistoryUuid = 
versionable.getProperty(JCR_VERSIONHISTORY).getString();
+                assertEquals(history.getIdentifier(), versionHistoryUuid);
+
+                final Version baseVersion = vMgr.getBaseVersion(path);
+                assertEquals("1.0", baseVersion.getName());
+                final Value[] predecessors = 
versionable.getProperty(JCR_PREDECESSORS).getValues();
+                assertEquals(0, predecessors.length);
+                assertFalse(vMgr.isCheckedOut(path));
+            }
+        }
+        // after restoring, the paths should be still versionable
+        assertVersionablePaths(session, names);
+    }
+}

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/IgnoreMissingBinariesTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/IgnoreMissingBinariesTest.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/IgnoreMissingBinariesTest.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/IgnoreMissingBinariesTest.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,95 @@
+/*
+ * 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.FileDataStoreContainer;
+import org.apache.jackrabbit.oak.upgrade.cli.container.NodeStoreContainer;
+import 
org.apache.jackrabbit.oak.upgrade.cli.container.SegmentNodeStoreContainer;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import java.io.File;
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class IgnoreMissingBinariesTest extends AbstractOak2OakTest {
+
+    private static final Logger log = 
LoggerFactory.getLogger(IgnoreMissingBinariesTest.class);
+
+    private final FileDataStoreContainer blob;
+
+    private final NodeStoreContainer source;
+
+    private final NodeStoreContainer destination;
+
+    @Before
+    public void prepare() throws Exception {
+        NodeStore source = getSourceContainer().open();
+        try {
+            initContent(source);
+        } finally {
+            getSourceContainer().close();
+        }
+
+        assertTrue(new File(blob.getDirectory(), 
"c2/b2/b5/c2b2b532305bf4b7c73ea4f2747a788bb668cedc").delete());
+
+        String[] args = getArgs();
+        log.info("oak2oak {}", Joiner.on(' ').join(args));
+        OakUpgrade.main(args);
+
+        createSession();
+    }
+
+    public IgnoreMissingBinariesTest() throws IOException {
+        blob = new FileDataStoreContainer();
+        source = new SegmentNodeStoreContainer(blob);
+        destination = new SegmentNodeStoreContainer(blob);
+    }
+
+    @Override
+    protected NodeStoreContainer getSourceContainer() {
+        return source;
+    }
+
+    @Override
+    protected NodeStoreContainer getDestinationContainer() {
+        return destination;
+    }
+
+    @Override
+    protected String[] getArgs() {
+        return new String[]{"--ignore-missing-binaries", "--src-datastore", 
blob.getDescription(), source.getDescription(), destination.getDescription()};
+    }
+
+    @Test
+    public void validateMigration() throws RepositoryException, IOException {
+        verifyContent(session);
+        verifyBlob(session);
+        assertEquals(0, 
session.getNode("/libs/sling/xss/config.xml/jcr:content").getProperty("jcr:data").getLength());
+    }
+}
\ No newline at end of file

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/IncludeExcludeSidegradeTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/IncludeExcludeSidegradeTest.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/IncludeExcludeSidegradeTest.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/IncludeExcludeSidegradeTest.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,80 @@
+/*
+ * 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 java.io.File;
+import java.io.IOException;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+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.plugins.segment.file.FileStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.junit.Before;
+
+public class IncludeExcludeSidegradeTest extends IncludeExcludeUpgradeTest {
+
+    @Before
+    public synchronized void upgradeRepository() throws Exception {
+        if (targetNodeStore == null) {
+            File directory = getTestDirectory();
+            File source = new File(directory, "source");
+            source.mkdirs();
+            FileStore fileStore = FileStore.newFileStore(source).create();
+            SegmentNodeStore segmentNodeStore = new 
SegmentNodeStore(fileStore);
+            RepositoryImpl repository = (RepositoryImpl) new Jcr(new 
Oak(segmentNodeStore).with("oak.sling")).createRepository();
+            Session session = repository.login(CREDENTIALS);
+            try {
+                createSourceContent(session);
+            } finally {
+                session.save();
+                session.logout();
+                repository.shutdown();
+                fileStore.close();
+            }
+            final NodeStore target = getTargetNodeStore();
+            doUpgradeRepository(source, target);
+            targetNodeStore = target;
+        }
+    }
+
+    @Override
+    protected void doUpgradeRepository(File source, NodeStore target) throws 
RepositoryException, IOException {
+        FileStore fileStore = FileStore.newFileStore(source).create();
+        SegmentNodeStore segmentNodeStore = new SegmentNodeStore(fileStore);
+        try {
+            final RepositorySidegrade sidegrade = new 
RepositorySidegrade(segmentNodeStore, target);
+            sidegrade.setIncludes(
+                    "/content/foo/en",
+                    "/content/assets/foo"
+            );
+            sidegrade.setExcludes(
+                    "/content/assets/foo/2013",
+                    "/content/assets/foo/2012",
+                    "/content/assets/foo/2011",
+                    "/content/assets/foo/2010"
+            );
+            sidegrade.copy();
+        } finally {
+            fileStore.close();
+        }
+    }
+}


Reply via email to