Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/CliUtils.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/CliUtils.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/CliUtils.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/CliUtils.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,57 @@
+/*
+ * 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.cli;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.io.Closer;
+
+public class CliUtils {
+
+    private static final Logger log = 
LoggerFactory.getLogger(OakUpgrade.class);
+
+    public static void displayUsage() throws IOException {
+        System.out.println(getUsage().replace("${command}", "java -jar 
oak-run-*-jr2.jar upgrade"));
+    }
+
+    public static String getUsage() throws IOException {
+        InputStream is = 
CliUtils.class.getClassLoader().getResourceAsStream("upgrade_usage.txt");
+        try {
+            return IOUtils.toString(is);
+        } finally {
+            IOUtils.closeQuietly(is);
+        }
+    }
+
+    public static void handleSigInt(final Closer closer) {
+        Runtime.getRuntime().addShutdownHook(new Thread() {
+            @Override
+            public void run() {
+                try {
+                    closer.close();
+                } catch (IOException e) {
+                    log.error("Can't close", e);
+                }
+            }
+        });
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/MigrationFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/MigrationFactory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/MigrationFactory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/MigrationFactory.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,123 @@
+/*
+ * 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.cli;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.core.RepositoryContext;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreBlobStore;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.upgrade.RepositorySidegrade;
+import org.apache.jackrabbit.oak.upgrade.RepositoryUpgrade;
+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.MigrationOptions;
+import org.apache.jackrabbit.oak.upgrade.cli.parser.StoreArguments;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.Closer;
+
+public class MigrationFactory {
+
+    protected final MigrationOptions options;
+
+    protected final StoreArguments stores;
+
+    protected final DatastoreArguments datastores;
+
+    protected final Closer closer;
+
+    public MigrationFactory(MigrationOptions options, StoreArguments stores, 
DatastoreArguments datastores, Closer closer) {
+        this.options = options;
+        this.stores = stores;
+        this.datastores = datastores;
+        this.closer = closer;
+    }
+
+    public RepositoryUpgrade createUpgrade() throws IOException, 
RepositoryException, CliArgumentException {
+        RepositoryContext src = stores.getSrcStore().create(closer);
+        BlobStore srcBlobStore = new DataStoreBlobStore(src.getDataStore());
+        NodeStore dstStore = createTarget(closer, srcBlobStore);
+        return createUpgrade(src, dstStore);
+    }
+
+    public RepositorySidegrade createSidegrade() throws IOException, 
CliArgumentException {
+        BlobStore srcBlobStore = datastores.getSrcBlobStore().create(closer);
+        NodeStore srcStore = stores.getSrcStore().create(srcBlobStore, closer);
+        NodeStore dstStore = createTarget(closer, srcBlobStore);
+        return createSidegrade(srcStore, dstStore);
+    }
+
+    protected NodeStore createTarget(Closer closer, BlobStore srcBlobStore) 
throws IOException {
+        BlobStore dstBlobStore = 
datastores.getDstBlobStore(srcBlobStore).create(closer);
+        NodeStore dstStore = stores.getDstStore().create(dstBlobStore, closer);
+        return dstStore;
+    }
+
+    protected RepositoryUpgrade createUpgrade(RepositoryContext source, 
NodeStore dstStore) {
+        RepositoryUpgrade upgrade = new RepositoryUpgrade(source, dstStore);
+        upgrade.setCopyBinariesByReference(datastores.getBlobMigrationCase() 
== DatastoreArguments.BlobMigrationCase.COPY_REFERENCES);
+        upgrade.setCopyVersions(options.getCopyVersions());
+        upgrade.setCopyOrphanedVersions(options.getCopyOrphanedVersions());
+        if (options.getIncludePaths() != null) {
+            upgrade.setIncludes(options.getIncludePaths());
+        }
+        if (options.getExcludePaths() != null) {
+            upgrade.setExcludes(options.getExcludePaths());
+        }
+        if (options.getMergePaths() != null) {
+            upgrade.setMerges(options.getMergePaths());
+        }
+        upgrade.setFilterLongNames(!stores.getDstType().isSupportLongNames());
+        upgrade.setCheckLongNames(!options.isSkipNameCheck() && 
!stores.getDstType().isSupportLongNames());
+        upgrade.setSkipOnError(!options.isFailOnError());
+        upgrade.setEarlyShutdown(options.isEarlyShutdown());
+        upgrade.setSkipInitialization(options.isSkipInitialization());
+        ServiceLoader<CommitHook> loader = 
ServiceLoader.load(CommitHook.class);
+        Iterator<CommitHook> iterator = loader.iterator();
+        ImmutableList.Builder<CommitHook> builder = ImmutableList.<CommitHook> 
builder().addAll(iterator);
+        upgrade.setCustomCommitHooks(builder.build());
+        return upgrade;
+    }
+
+    private RepositorySidegrade createSidegrade(NodeStore srcStore, NodeStore 
dstStore) {
+        RepositorySidegrade sidegrade = new RepositorySidegrade(srcStore, 
dstStore);
+        sidegrade.setCopyVersions(options.getCopyVersions());
+        sidegrade.setCopyOrphanedVersions(options.getCopyOrphanedVersions());
+        if (options.getIncludePaths() != null) {
+            sidegrade.setIncludes(options.getIncludePaths());
+        }
+        if (options.getExcludePaths() != null) {
+            sidegrade.setExcludes(options.getExcludePaths());
+        }
+        if (options.getMergePaths() != null) {
+            sidegrade.setMerges(options.getMergePaths());
+        }
+        sidegrade.setFilterLongNames(stores.getSrcType().isSupportLongNames() 
&& !stores.getDstType().isSupportLongNames());
+        sidegrade.setVerify(options.isVerify());
+        sidegrade.setOnlyVerify(options.isOnlyVerify());
+        sidegrade.setSkipCheckpoints(options.isSkipCheckpoints());
+        return sidegrade;
+    }
+
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/OakUpgrade.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/OakUpgrade.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/OakUpgrade.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/OakUpgrade.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,101 @@
+/*
+ * 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.cli;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.ServiceLoader;
+
+import javax.jcr.RepositoryException;
+
+import com.google.common.collect.Lists;
+import com.google.common.io.Closer;
+
+import joptsimple.OptionSet;
+import org.apache.jackrabbit.oak.spi.lifecycle.CompositeInitializer;
+import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer;
+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;
+
+public class OakUpgrade {
+
+    public static void main(String... args) throws IOException {
+        OptionSet options = OptionParserFactory.create().parse(args);
+        try {
+            MigrationCliArguments cliArguments = new 
MigrationCliArguments(options);
+            if (cliArguments.hasOption(OptionParserFactory.HELP) || 
cliArguments.getArguments().isEmpty()) {
+                CliUtils.displayUsage();
+                return;
+            }
+            migrate(cliArguments);
+        } catch(CliArgumentException e) {
+            if (e.getMessage() != null) {
+                System.err.println(e.getMessage());
+            }
+            System.exit(e.getExitCode());
+        }
+    }
+
+    public static void migrate(MigrationCliArguments argumentParser) throws 
IOException, CliArgumentException {
+        MigrationOptions options = new MigrationOptions(argumentParser);
+        options.logOptions();
+
+        StoreArguments stores = new StoreArguments(options, 
argumentParser.getArguments());
+        stores.logOptions();
+
+        boolean srcEmbedded = stores.srcUsesEmbeddedDatastore();
+        DatastoreArguments datastores = new DatastoreArguments(options, 
stores, srcEmbedded);
+
+        migrate(options, stores, datastores);
+    }
+
+    public static void migrate(MigrationOptions options, StoreArguments 
stores, DatastoreArguments datastores) throws IOException, CliArgumentException 
{
+        Closer closer = Closer.create();
+        CliUtils.handleSigInt(closer);
+        MigrationFactory factory = new MigrationFactory(options, stores, 
datastores, closer);
+        try {
+            if (stores.getSrcStore().isJcr2()) {
+                upgrade(factory);
+            } else {
+                sidegrade(factory);
+            }
+        } catch (Throwable t) {
+            throw closer.rethrow(t);
+        } finally {
+            closer.close();
+        }
+    }
+
+    private static void upgrade(MigrationFactory migrationFactory) throws 
IOException, RepositoryException, CliArgumentException {
+        migrationFactory.createUpgrade().copy(createCompositeInitializer());
+    }
+
+    private static void sidegrade(MigrationFactory migrationFactory) throws 
IOException, RepositoryException, CliArgumentException {
+        migrationFactory.createSidegrade().copy();
+    }
+
+    private static RepositoryInitializer createCompositeInitializer() {
+        ServiceLoader<RepositoryInitializer> loader = 
ServiceLoader.load(RepositoryInitializer.class);
+        List<RepositoryInitializer> initializers = 
Lists.newArrayList(loader.iterator());
+        return new CompositeInitializer(initializers);
+    }
+
+}
\ No newline at end of file

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/BlobStoreFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/BlobStoreFactory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/BlobStoreFactory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/BlobStoreFactory.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,27 @@
+/*
+ * 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.cli.blob;
+
+import java.io.IOException;
+
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+
+import com.google.common.io.Closer;
+
+public interface BlobStoreFactory {
+    BlobStore create(Closer closer) throws IOException;
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/ConstantBlobStoreFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/ConstantBlobStoreFactory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/ConstantBlobStoreFactory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/ConstantBlobStoreFactory.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,36 @@
+/*
+ * 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.cli.blob;
+
+import com.google.common.io.Closer;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+
+import java.io.IOException;
+
+public class ConstantBlobStoreFactory implements BlobStoreFactory {
+
+    private final BlobStore blobStore;
+
+    public ConstantBlobStoreFactory(BlobStore blobStore) {
+        this.blobStore = blobStore;
+    }
+
+    @Override
+    public BlobStore create(Closer closer) throws IOException {
+        return blobStore;
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/DummyBlobStoreFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/DummyBlobStoreFactory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/DummyBlobStoreFactory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/DummyBlobStoreFactory.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,34 @@
+/*
+ * 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.cli.blob;
+
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+
+import com.google.common.io.Closer;
+
+public class DummyBlobStoreFactory implements BlobStoreFactory {
+
+    @Override
+    public BlobStore create(Closer closer) {
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return "DummyBlobStore";
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FileBlobStoreFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FileBlobStoreFactory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FileBlobStoreFactory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FileBlobStoreFactory.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,41 @@
+/*
+ * 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.cli.blob;
+
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.blob.FileBlobStore;
+
+import com.google.common.io.Closer;
+
+public class FileBlobStoreFactory implements BlobStoreFactory {
+
+    private final String directory;
+
+    public FileBlobStoreFactory(String directory) {
+        this.directory = directory;
+    }
+
+    @Override
+    public BlobStore create(Closer closer) {
+        return new FileBlobStore(directory);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("FileBlobStore[%s]", directory);
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FileDataStoreFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FileDataStoreFactory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FileDataStoreFactory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FileDataStoreFactory.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,67 @@
+/*
+ * 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.cli.blob;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+import org.apache.jackrabbit.core.data.FileDataStore;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreBlobStore;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.OakFileDataStore;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+
+import com.google.common.io.Closer;
+
+public class FileDataStoreFactory implements BlobStoreFactory {
+
+    private final String directory;
+
+    private final boolean ignoreMissingBlobs;
+
+    public FileDataStoreFactory(String directory, boolean ignoreMissingBlobs) {
+        this.directory = directory;
+        this.ignoreMissingBlobs = ignoreMissingBlobs;
+    }
+
+    @Override
+    public BlobStore create(Closer closer) {
+        OakFileDataStore delegate = new OakFileDataStore();
+        delegate.setPath(directory);
+        delegate.init(null);
+        closer.register(asCloseable(delegate));
+
+        if (ignoreMissingBlobs) {
+            return new SafeDataStoreBlobStore(delegate);
+        } else {
+            return new DataStoreBlobStore(delegate);
+        }
+    }
+
+    private static Closeable asCloseable(final FileDataStore store) {
+        return new Closeable() {
+            @Override
+            public void close() throws IOException {
+                store.close();
+            }
+        };
+    }
+
+    @Override
+    public String toString() {
+        return String.format("FileDataStore[%s]", directory);
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/MissingBlobStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/MissingBlobStore.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/MissingBlobStore.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/MissingBlobStore.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,68 @@
+/*
+ * 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.cli.blob;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+
+/**
+ * Utility BlobStore implementation to be used in tooling that can work with a
+ * FileStore without the need of the DataStore being present locally
+ */
+public class MissingBlobStore implements BlobStore {
+
+    @Override
+    public String writeBlob(InputStream in) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int readBlob(String blobId, long pos, byte[] buff, int off,
+            int length) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public long getBlobLength(String blobId) throws IOException {
+        // best effort length extraction
+        int indexOfSep = blobId.lastIndexOf("#");
+        if (indexOfSep != -1) {
+            return Long.valueOf(blobId.substring(indexOfSep + 1));
+        }
+        return -1;
+    }
+
+    @Override
+    public InputStream getInputStream(String blobId) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getBlobId(String reference) {
+        return reference;
+    }
+
+    @Override
+    public String getReference(String blobId) {
+        return blobId;
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/MissingBlobStoreFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/MissingBlobStoreFactory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/MissingBlobStoreFactory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/MissingBlobStoreFactory.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,34 @@
+/*
+ * 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.cli.blob;
+
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+
+import com.google.common.io.Closer;
+
+public class MissingBlobStoreFactory implements BlobStoreFactory {
+
+    @Override
+    public BlobStore create(Closer closer) {
+        return new MissingBlobStore();
+    }
+
+    @Override
+    public String toString() {
+        return "MissingBlobStore";
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/S3DataStoreFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/S3DataStoreFactory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/S3DataStoreFactory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/S3DataStoreFactory.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.cli.blob;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.aws.ext.ds.S3DataStore;
+import org.apache.jackrabbit.core.data.CachingDataStore;
+import org.apache.jackrabbit.core.data.DataStoreException;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreBlobStore;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.io.Closer;
+import com.google.common.io.Files;
+
+public class S3DataStoreFactory implements BlobStoreFactory {
+
+    private static final Logger log = 
LoggerFactory.getLogger(S3DataStoreFactory.class);
+
+    private static final Pattern STRIP_VALUE_PATTERN = 
Pattern.compile("^[TILFDXSCB]?\"(.*)\"\\W*$");
+
+    private final Properties props;
+
+    private final String directory;
+
+    private final File tempHomeDir;
+
+    private final boolean ignoreMissingBlobs;
+
+    public S3DataStoreFactory(String configuration, String directory, boolean 
ignoreMissingBlobs) throws IOException {
+        this.props = new Properties();
+        FileReader reader = new FileReader(new File(configuration));
+        try {
+            props.load(reader);
+        } finally {
+            IOUtils.closeQuietly(reader);
+        }
+
+        for (Object key : new HashSet<Object>(props.keySet())) {
+            String value = props.getProperty((String) key);
+            props.put(key, stripValue(value));
+        }
+
+        this.directory = directory;
+        this.tempHomeDir = Files.createTempDir();
+        this.ignoreMissingBlobs = ignoreMissingBlobs;
+    }
+
+    @Override
+    public BlobStore create(Closer closer) throws IOException {
+        S3DataStore delegate = new S3DataStore();
+        delegate.setProperties(props);
+        delegate.setPath(directory);
+        try {
+            delegate.init(tempHomeDir.getPath());
+        } catch (RepositoryException e) {
+            throw new IOException(e);
+        }
+        closer.register(asCloseable(delegate, tempHomeDir));
+        if (ignoreMissingBlobs) {
+            return new SafeDataStoreBlobStore(delegate);
+        } else {
+            return new DataStoreBlobStore(delegate);
+        }
+    }
+
+    private static Closeable asCloseable(final CachingDataStore store, final 
File tempHomeDir) {
+        return new Closeable() {
+            @Override
+            public void close() throws IOException {
+                try {
+                    while (!store.getPendingUploads().isEmpty()) {
+                        log.info("Waiting for following uploads to finish: " + 
store.getPendingUploads());
+                        Thread.sleep(1000);
+                    }
+                    store.close();
+                    FileUtils.deleteDirectory(tempHomeDir);
+                } catch (DataStoreException e) {
+                    throw new IOException(e);
+                } catch (InterruptedException e) {
+                    throw new IOException(e);
+                }
+            }
+        };
+    }
+
+    static String stripValue(String value) {
+        Matcher matcher = STRIP_VALUE_PATTERN.matcher(value);
+        if (matcher.matches()) {
+            return matcher.group(1);
+        } else {
+            return value;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return String.format("S3DataStore[%s]", directory);
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/SafeDataStoreBlobStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/SafeDataStoreBlobStore.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/SafeDataStoreBlobStore.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/blob/SafeDataStoreBlobStore.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,101 @@
+/*
+ * 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.cli.blob;
+
+import org.apache.jackrabbit.core.data.DataIdentifier;
+import org.apache.jackrabbit.core.data.DataRecord;
+import org.apache.jackrabbit.core.data.DataStore;
+import org.apache.jackrabbit.core.data.DataStoreException;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreBlobStore;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.InMemoryDataRecord;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nonnull;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * This implementation of the DataStoreBlobStore won't throw an exception if
+ * it can't find blob with given id. The WARN message will be emitted instead
+ * and the empty InputStream will be returned.
+ */
+public class SafeDataStoreBlobStore extends DataStoreBlobStore {
+
+    private static final Logger log = 
LoggerFactory.getLogger(SafeDataStoreBlobStore.class);
+
+    public SafeDataStoreBlobStore(DataStore delegate) {
+        super(delegate);
+    }
+
+    @Override
+    public String getReference(@Nonnull String encodedBlobId) {
+        checkNotNull(encodedBlobId);
+        String blobId = extractBlobId(encodedBlobId);
+        //Reference are not created for in memory record
+        if (InMemoryDataRecord.isInstance(blobId)) {
+            return null;
+        }
+
+        DataRecord record;
+        try {
+            record = delegate.getRecordIfStored(new DataIdentifier(blobId));
+            if (record != null) {
+                return record.getReference();
+            } else {
+                log.debug("No blob found for id [{}]", blobId);
+            }
+        } catch (DataStoreException e) {
+            log.warn("Unable to access the blobId for  [{}]", blobId, e);
+        }
+        return  null;
+    }
+
+
+    @Override
+    protected InputStream getStream(String blobId) throws IOException {
+        try {
+            DataRecord record = getDataRecord(blobId);
+            if (record == null) {
+                log.warn("No blob found for id [{}]", blobId);
+                return new ByteArrayInputStream(new byte[0]);
+            }
+            InputStream in = getDataRecord(blobId).getStream();
+            if (!(in instanceof BufferedInputStream)){
+                in = new BufferedInputStream(in);
+            }
+            return in;
+        } catch (DataStoreException e) {
+            throw new IOException(e);
+        }
+    }
+
+    @Override
+    protected DataRecord getDataRecord(String blobId) throws 
DataStoreException {
+        DataRecord id;
+        if (InMemoryDataRecord.isInstance(blobId)) {
+            id = InMemoryDataRecord.getInstance(blobId);
+        } else {
+            id = delegate.getRecordIfStored(new DataIdentifier(blobId));
+        }
+        return id;
+    }
+}
\ No newline at end of file

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/Jackrabbit2Factory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/Jackrabbit2Factory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/Jackrabbit2Factory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/Jackrabbit2Factory.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,107 @@
+/*
+ * 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.cli.node;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.jackrabbit.core.RepositoryContext;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+
+import com.google.common.io.Closer;
+
+public class Jackrabbit2Factory {
+
+    private final File repositoryDir;
+
+    private final File repositoryFile;
+
+    public Jackrabbit2Factory(String repositoryDir, String repositoryFile) {
+        if (!isJcr2Repository(repositoryDir)) {
+            throw new IllegalArgumentException("Repository directory not 
found: " + repositoryDir);
+        }
+        this.repositoryDir = new File(repositoryDir);
+        this.repositoryFile = new File(repositoryFile);
+        if (!isRepositoryXml(repositoryFile)) {
+            throw new IllegalArgumentException("Repository configuration not 
found: " + repositoryFile);
+        }
+    }
+
+    public RepositoryContext create(Closer closer) throws RepositoryException {
+        RepositoryContext source = 
RepositoryContext.create(RepositoryConfig.create(repositoryFile, 
repositoryDir));
+        closer.register(asCloseable(source));
+        return source;
+    }
+
+    public File getRepositoryDir() {
+        return repositoryDir;
+    }
+
+    private static Closeable asCloseable(final RepositoryContext context) {
+        return new Closeable() {
+            @Override
+            public void close() throws IOException {
+                context.getRepository().shutdown();
+            }
+        };
+    }
+
+    public static boolean isRepositoryXml(String path) {
+        File file = new File(path);
+        if (file.isFile()) {
+            BufferedReader reader = null;
+            try {
+                reader = new BufferedReader(new FileReader(file));
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    if (StringUtils.containsIgnoreCase(line, "<Repository>")) {
+                        return true;
+                    }
+                }
+            } catch (FileNotFoundException e) {
+                return false;
+            } catch (IOException e) {
+                return false;
+            } finally {
+                IOUtils.closeQuietly(reader);
+            }
+        }
+        return false;
+    }
+
+    public static boolean isJcr2Repository(String directory) {
+        File dir = new File(directory);
+        if (!dir.isDirectory()) {
+            return false;
+        }
+        File workspaces = new File(dir, "workspaces");
+        return workspaces.isDirectory();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("JCR2[%s, %s]", repositoryDir, repositoryFile);
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/JdbcFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/JdbcFactory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/JdbcFactory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/JdbcFactory.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,102 @@
+/*
+ * 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.cli.node;
+
+import org.apache.jackrabbit.oak.plugins.document.DocumentMK;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
+import org.apache.jackrabbit.oak.plugins.document.rdb.RDBBlobStore;
+import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDataSourceFactory;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.io.Closer;
+
+import javax.sql.DataSource;
+import java.io.Closeable;
+import java.io.IOException;
+
+public class JdbcFactory implements NodeStoreFactory {
+
+    private static final Logger log = 
LoggerFactory.getLogger(JdbcFactory.class);
+
+    private final String jdbcUri;
+
+    private final int cacheSize;
+
+    private final String user;
+
+    private final String password;
+
+    private final boolean readOnly;
+
+    public JdbcFactory(String jdbcUri, int cacheSize, String user, String 
password, boolean readOnly) {
+        this.jdbcUri = jdbcUri;
+        this.cacheSize = cacheSize;
+        if (user == null || password == null) {
+            throw new IllegalArgumentException("RBD requires username and 
password parameters.");
+        }
+        this.user = user;
+        this.password = password;
+        this.readOnly = readOnly;
+    }
+
+    @Override
+    public NodeStore create(BlobStore blobStore, Closer closer) {
+        DocumentMK.Builder builder = MongoFactory.getBuilder(cacheSize);
+        if (blobStore != null) {
+            builder.setBlobStore(blobStore);
+        }
+        builder.setRDBConnection(getDataSource(closer));
+        if (readOnly) {
+            log.warn("Read-only mode for the DocumentMK is not available in 
1.4");
+        }
+        log.info("Initialized DocumentNodeStore on RDB with Cache size : {} 
MB, Fast migration : {}", cacheSize,
+                builder.isDisableBranches());
+        DocumentNodeStore documentNodeStore = builder.getNodeStore();
+        closer.register(MongoFactory.asCloseable(documentNodeStore));
+        return documentNodeStore;
+    }
+
+    private DataSource getDataSource(Closer closer) {
+        DataSource ds = RDBDataSourceFactory.forJdbcUrl(jdbcUri, user, 
password);
+        if (ds instanceof Closeable) {
+            closer.register((Closeable)ds);
+        }
+        return ds;
+    }
+
+    @Override
+    public boolean hasExternalBlobReferences() throws IOException {
+        Closer closer = Closer.create();
+        try {
+            DataSource ds = getDataSource(closer);
+            RDBBlobStore blobStore = new RDBBlobStore(ds);
+            return !blobStore.getAllChunkIds(0).hasNext();
+        } catch(Throwable e) {
+            throw closer.rethrow(e);
+        } finally {
+            closer.close();
+        }
+    }
+
+    @Override
+    public String toString() {
+        return String.format("DocumentNodeStore[%s]", jdbcUri);
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/MongoFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/MongoFactory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/MongoFactory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/MongoFactory.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,126 @@
+/*
+ * 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.cli.node;
+
+import com.mongodb.DB;
+import org.apache.jackrabbit.oak.plugins.document.DocumentMK;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
+import org.apache.jackrabbit.oak.plugins.document.mongo.MongoBlobStore;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+import com.google.common.io.Closer;
+import com.mongodb.MongoClient;
+import com.mongodb.MongoClientURI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.UnknownHostException;
+
+public class MongoFactory implements NodeStoreFactory {
+
+    private static final Logger log = 
LoggerFactory.getLogger(MongoFactory.class);
+
+    private static final long MB = 1024 * 1024;
+
+    private final MongoClientURI uri;
+
+    private final int cacheSize;
+
+    private final boolean readOnly;
+
+    public MongoFactory(String repoDesc, int cacheSize, boolean readOnly) {
+        this.uri = new MongoClientURI(repoDesc);
+        this.cacheSize = cacheSize;
+        this.readOnly = readOnly;
+    }
+
+    @Override
+    public NodeStore create(BlobStore blobStore, Closer closer) throws 
UnknownHostException {
+        DocumentMK.Builder builder = getBuilder(cacheSize);
+        builder.setMongoDB(getDB(closer));
+        if (blobStore != null) {
+            builder.setBlobStore(blobStore);
+        }
+        if (readOnly) {
+            log.warn("Read-only mode for the DocumentMK is not available in 
1.4");
+        }
+        DocumentNodeStore documentNodeStore = builder.getNodeStore();
+        closer.register(asCloseable(documentNodeStore));
+        return documentNodeStore;
+    }
+
+    private DB getDB(Closer closer) throws UnknownHostException {
+        String db;
+        if (uri.getDatabase() == null) {
+            db = "aem-author"; // assume an author instance
+        } else {
+            db = uri.getDatabase();
+        }
+        MongoClient client = new MongoClient(uri);
+        closer.register(asCloseable(client));
+        return client.getDB(db);
+    }
+
+    @Override
+    public boolean hasExternalBlobReferences() throws IOException {
+        Closer closer = Closer.create();
+        try {
+            MongoBlobStore mongoBlobStore = new MongoBlobStore(getDB(closer));
+            return !mongoBlobStore.getAllChunkIds(0).hasNext();
+        } catch(Throwable e) {
+            throw closer.rethrow(e);
+        } finally {
+            closer.close();
+        }
+    }
+
+    static Closeable asCloseable(final DocumentNodeStore documentNodeStore) {
+        return new Closeable() {
+            @Override
+            public void close() throws IOException {
+                documentNodeStore.dispose();
+            }
+        };
+    }
+
+    private static Closeable asCloseable(final MongoClient client) {
+        return new Closeable() {
+            @Override
+            public void close() throws IOException {
+                client.close();
+            }
+        };
+    }
+
+    static DocumentMK.Builder getBuilder(int cacheSize) {
+        boolean fastMigration = 
!Boolean.getBoolean("mongomk.disableFastMigration");
+        DocumentMK.Builder builder = new DocumentMK.Builder();
+        builder.memoryCacheSize(cacheSize * MB);
+        if (fastMigration) {
+            builder.disableBranches();
+        }
+        return builder;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("DocumentNodeStore[%s]", uri.toString());
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/NodeStoreFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/NodeStoreFactory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/NodeStoreFactory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/NodeStoreFactory.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,31 @@
+/*
+ * 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.cli.node;
+
+import java.io.IOException;
+
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+import com.google.common.io.Closer;
+
+public interface NodeStoreFactory {
+
+    NodeStore create(BlobStore blobStore, Closer closer) throws IOException;
+
+    boolean hasExternalBlobReferences() throws IOException;
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/SegmentFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/SegmentFactory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/SegmentFactory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/SegmentFactory.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,129 @@
+/*
+ * 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.cli.node;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.jackrabbit.oak.plugins.blob.ReferenceCollector;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore.Builder;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+import com.google.common.io.Closer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+
+public class SegmentFactory implements NodeStoreFactory {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(SegmentFactory.class);
+
+    private final File dir;
+
+    private final boolean disableMmap;
+
+    public SegmentFactory(String directory, boolean disableMmap, boolean 
readOnly) {
+        this.dir = new File(directory);
+        this.disableMmap = disableMmap;
+        createDirectoryIfMissing(dir);
+        if (readOnly) {
+            LOG.info("Read-only segment node store is not supported in 1.0");
+        }
+        if (!dir.isDirectory()) {
+            throw new IllegalArgumentException("Not a directory: " + 
dir.getPath());
+        }
+    }
+
+    private void createDirectoryIfMissing(File directory) {
+        if (!directory.exists()) {
+            directory.mkdirs();
+        }
+    }
+
+    @Override
+    public NodeStore create(BlobStore blobStore, Closer closer) throws 
IOException {
+        File directory = new File(dir, "segmentstore");
+        boolean mmapEnabled = !disableMmap && 
"64".equals(System.getProperty("sun.arch.data.model", "32"));
+
+        final FileStore fs;
+        Builder builder;
+        builder = FileStore.newFileStore(directory);
+
+        if (blobStore != null) {
+            builder.withBlobStore(blobStore);
+        }
+        builder.withMaxFileSize(256);
+        builder.withMemoryMapping(mmapEnabled);
+        fs = builder.create();
+
+        closer.register(asCloseable(fs));
+
+        return new TarNodeStore(new SegmentNodeStore(fs), new 
TarNodeStore.SuperRootProvider() {
+            @Override
+            public NodeState getSuperRoot() {
+                return fs.getHead();
+            }
+        });
+    }
+
+    @Override
+    public boolean hasExternalBlobReferences() throws IOException {
+        FileStore fs = FileStore.newFileStore(new File(dir, 
"segmentstore")).create();
+        try {
+            fs.getTracker().collectBlobReferences(new ReferenceCollector() {
+                @Override
+                public void addReference(String reference) {
+                    // FIXME the collector should allow to stop processing
+                    // see java.nio.file.FileVisitor
+                    throw new ExternalBlobFound();
+                }
+            });
+            return false;
+        } catch (ExternalBlobFound e) {
+            return true;
+        } finally {
+            fs.close();
+        }
+    }
+
+    public File getRepositoryDir() {
+        return dir;
+    }
+
+    private static Closeable asCloseable(final FileStore fs) {
+        return new Closeable() {
+            @Override
+            public void close() throws IOException {
+                fs.close();
+            }
+        };
+    }
+
+    @Override
+    public String toString() {
+        return String.format("SegmentNodeStore[%s]", dir);
+    }
+
+    private static class ExternalBlobFound extends RuntimeException {
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/StoreFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/StoreFactory.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/StoreFactory.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/StoreFactory.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,70 @@
+/*
+ * 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.cli.node;
+
+import java.io.IOException;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.core.RepositoryContext;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+import com.google.common.io.Closer;
+
+public class StoreFactory {
+
+    private final Jackrabbit2Factory jcr2Factory;
+
+    private final NodeStoreFactory nodeStoreFactory;
+
+    public StoreFactory(Jackrabbit2Factory crx2Factory) {
+        this.jcr2Factory = crx2Factory;
+        this.nodeStoreFactory = null;
+    }
+
+    public StoreFactory(NodeStoreFactory nodeStoreFactory) {
+        this.jcr2Factory = null;
+        this.nodeStoreFactory = nodeStoreFactory;
+    }
+
+    public RepositoryContext create(Closer closer) throws IOException, 
RepositoryException {
+        if (jcr2Factory == null) {
+            throw new UnsupportedOperationException();
+        }
+        return jcr2Factory.create(closer);
+    }
+
+    public NodeStore create(BlobStore blobStore, Closer closer) throws 
IOException {
+        if (nodeStoreFactory == null) {
+            throw new UnsupportedOperationException();
+        }
+        return nodeStoreFactory.create(blobStore, closer);
+    }
+
+    public boolean isJcr2() {
+        return jcr2Factory != null;
+    }
+
+    public boolean hasExternalBlobReferences() throws IOException {
+        if (isJcr2()) {
+            return true;
+        } else {
+            return nodeStoreFactory.hasExternalBlobReferences();
+        }
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/TarNodeStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/TarNodeStore.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/TarNodeStore.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/node/TarNodeStore.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,48 @@
+/*
+ * 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.cli.node;
+
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.state.ProxyNodeStore;
+
+public class TarNodeStore extends ProxyNodeStore {
+
+    private final NodeStore ns;
+
+    private final SuperRootProvider superRootProvider;
+
+    public TarNodeStore(NodeStore ns, SuperRootProvider superRootProvider) {
+        this.ns = ns;
+        this.superRootProvider = superRootProvider;
+    }
+
+    public NodeState getSuperRoot() {
+        return superRootProvider.getSuperRoot();
+    }
+
+    @Override
+    protected NodeStore getNodeStore() {
+        return ns;
+    }
+
+    public interface SuperRootProvider {
+
+        NodeState getSuperRoot();
+
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/CliArgumentException.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/CliArgumentException.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/CliArgumentException.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/CliArgumentException.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,38 @@
+/*
+ * 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.cli.parser;
+
+public class CliArgumentException extends Exception {
+
+    private static final long serialVersionUID = -7579563789244874904L;
+
+    private final int exitCode;
+
+    public CliArgumentException(int exitCode) {
+        super();
+        this.exitCode = exitCode;
+    }
+
+    public CliArgumentException(String message, int exitCode) {
+        super(message);
+        this.exitCode = exitCode;
+    }
+
+    public int getExitCode() {
+        return exitCode;
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/DatastoreArguments.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/DatastoreArguments.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/DatastoreArguments.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/DatastoreArguments.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,238 @@
+/*
+ * 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.cli.parser;
+
+import org.apache.commons.lang.text.StrSubstitutor;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.upgrade.cli.blob.BlobStoreFactory;
+import org.apache.jackrabbit.oak.upgrade.cli.blob.ConstantBlobStoreFactory;
+import org.apache.jackrabbit.oak.upgrade.cli.blob.DummyBlobStoreFactory;
+import org.apache.jackrabbit.oak.upgrade.cli.blob.FileBlobStoreFactory;
+import org.apache.jackrabbit.oak.upgrade.cli.blob.FileDataStoreFactory;
+import org.apache.jackrabbit.oak.upgrade.cli.blob.MissingBlobStoreFactory;
+import org.apache.jackrabbit.oak.upgrade.cli.blob.S3DataStoreFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Map;
+
+import static com.google.common.collect.Maps.newHashMap;
+import static 
org.apache.jackrabbit.oak.upgrade.cli.parser.StoreType.JCR2_DIR_XML;
+
+/**
+ * This class parses the input provided by the user and analyses the given 
node stores
+ * in order to find out which datastore combination should be used for the 
migration.
+ *
+ * The desired outcome for the combinations of user input can be found in the 
table below.
+ * The table is a kind of heuristics that tries to match the user intentions.
+ * <pre>
+ * For sidegrade:
+ || src blobstore defined || src blobs embedded || dst blobstore defined || 
--copy-binaries || outcome src blobstore || outcome action
+ |   -                    |   -                 |  -                     |  -  
             |  missing               |  copy references¹
+ |   -                    |   -                 |  -                     |  +  
             |  missing               |  (x) not supported
+ |   -                    |   -                 |  +                     |  *  
             |  missing               |  (x) not supported
+ |   -                    |   +                 |  -                     |  *  
             |  embedded              |  copy to embedded
+ |   -                    |   +                 |  +                     |  *  
             |  embedded              |  copy to defined blobstore
+ |   +                    |   *                 |  -                     |  -  
             |  as in src             |  copy references
+ |   +                    |   *                 |  -                     |  +  
             |  as in src             |  copy to embedded
+ |   +                    |   *                 |  +                     |  *  
             |  as in src             |  copy to defined blobstore
+
+ ¹ - (x) not supported for SegmentMK -&gt; MongoMK migration
+
+ For upgrade:
+
+ || dst blobstore defined || --copy-binaries || outcome src blobstore || 
outcome action
+ |  -                     |  -               |  defined by JCR2       |  copy 
references
+ |  -                     |  +               |  defined by JCR2       |  copy 
to embedded
+ |  +                     |  *               |  defined by JCR2       |  copy 
to defined blobstore
+ * </pre>
+ */
+public class DatastoreArguments {
+
+    private static final Logger log = 
LoggerFactory.getLogger(DatastoreArguments.class);
+
+    private final BlobStoreFactory definedSrcBlob;
+
+    private final BlobStoreFactory definedDstBlob;
+
+    private final StoreArguments storeArguments;
+
+    private final BlobMigrationCase blobMigrationCase;
+
+    private final MigrationOptions options;
+
+    private final boolean srcEmbedded;
+
+    public DatastoreArguments(MigrationOptions options, StoreArguments 
storeArguments, boolean srcEmbedded) throws CliArgumentException {
+        this.storeArguments = storeArguments;
+        this.options = options;
+        this.srcEmbedded = srcEmbedded;
+
+        try {
+            blobMigrationCase = discoverBlobMigrationCase();
+        } catch (IOException e) {
+            log.error("Can't figure out the right blob migration path", e);
+            throw new CliArgumentException(1);
+        }
+
+        if (blobMigrationCase == BlobMigrationCase.UNSUPPORTED) {
+            throw new CliArgumentException("This combination of data- and 
node-stores is not supported", 1);
+        }
+
+        try {
+            definedSrcBlob = options.isSrcBlobStoreDefined() ? 
getDefinedSrcBlobStore() : null;
+            definedDstBlob = options.isDstBlobStoreDefined() ? 
getDefinedDstBlobStore() : null;
+        } catch(IOException e) {
+            log.error("Can't read the blob configuration", e);
+            throw new CliArgumentException(1);
+        }
+
+        log.info(blobMigrationCase.getDescription(this));
+    }
+
+    public BlobStoreFactory getSrcBlobStore() throws IOException {
+        BlobStoreFactory result;
+        if (options.isSrcBlobStoreDefined()) {
+            result = definedSrcBlob;
+        } else if (blobMigrationCase == BlobMigrationCase.COPY_REFERENCES) {
+            result = new MissingBlobStoreFactory();
+        } else {
+            result = new DummyBlobStoreFactory(); // embedded
+        }
+        log.info("Source blob store: {}", result);
+        return result;
+    }
+
+    public BlobStoreFactory getDstBlobStore(BlobStore srcBlobStore) throws 
IOException {
+        BlobStoreFactory result;
+        if (options.isDstBlobStoreDefined()) {
+            result = definedDstBlob;
+        } else if (blobMigrationCase == BlobMigrationCase.COPY_REFERENCES && 
(options.isSrcBlobStoreDefined() || storeArguments.getSrcType() == 
JCR2_DIR_XML)) {
+            result = new ConstantBlobStoreFactory(srcBlobStore);
+        } else if (blobMigrationCase == BlobMigrationCase.COPY_REFERENCES) {
+            result = new MissingBlobStoreFactory();
+        } else {
+            result = new DummyBlobStoreFactory(); // embedded
+        }
+
+        log.info("Destination blob store: {}", result);
+        return result;
+    }
+
+    private BlobStoreFactory getDefinedSrcBlobStore() throws IOException {
+        boolean ignoreMissingBinaries = options.isIgnoreMissingBinaries();
+        if (options.isSrcFbs()) {
+            return new FileBlobStoreFactory(options.getSrcFbs());
+        } else if (options.isSrcS3()) {
+            return new S3DataStoreFactory(options.getSrcS3Config(), 
options.getSrcS3(), ignoreMissingBinaries);
+        } else if (options.isSrcFds()) {
+            return new FileDataStoreFactory(options.getSrcFds(), 
ignoreMissingBinaries);
+        } else {
+            return null;
+        }
+    }
+
+    private BlobStoreFactory getDefinedDstBlobStore() throws IOException {
+        if (options.isDstFbs()) {
+            return new FileBlobStoreFactory(options.getDstFbs());
+        } else if (options.isDstS3()) {
+            return new S3DataStoreFactory(options.getDstS3Config(), 
options.getDstS3(), false);
+        } else if (options.isDstFds()) {
+            return new FileDataStoreFactory(options.getDstFds(), false);
+        } else {
+            return null;
+        }
+    }
+
+    public enum BlobMigrationCase {
+        COPY_REFERENCES("Only blob references will be copied"),
+        EMBEDDED_TO_EMBEDDED("Blobs embedded in ${srcnode} will be embedded in 
${dstnode}"),
+        EMBEDDED_TO_EXTERNAL("Blobs embedded in ${srcnode} will be copied to 
${dstblob}"),
+        EXTERNAL_TO_EMBEDDED("Blobs stored in ${srcblob} will be embedded in 
${dstnode}"),
+        EXTERNAL_TO_EXTERNAL("Blobs stored in ${srcblob} will be copied to 
${dstblob}"),
+        UNSUPPORTED("Unsupported case");
+
+        private final String description;
+
+        BlobMigrationCase(String description) {
+            this.description = description;
+        }
+
+        private String getDescription(DatastoreArguments datastoreArguments) {
+            Map<String, String> map = newHashMap();
+            map.put("srcnode", 
datastoreArguments.storeArguments.getSrcDescriptor());
+            map.put("dstnode", 
datastoreArguments.storeArguments.getDstDescriptor());
+
+            if (datastoreArguments.storeArguments.getSrcType() == 
JCR2_DIR_XML) {
+                map.put("srcblob", "CRX2 datastore");
+            } else {
+                map.put("srcblob", datastoreArguments.definedSrcBlob == null ? 
"?" : datastoreArguments.definedSrcBlob.toString());
+            }
+            map.put("dstblob", datastoreArguments.definedDstBlob == null ? "?" 
: datastoreArguments.definedDstBlob.toString());
+
+            StrSubstitutor subst = new StrSubstitutor(map);
+            return subst.replace(description);
+        }
+
+    }
+
+    public BlobMigrationCase getBlobMigrationCase() {
+        return blobMigrationCase;
+    }
+
+    private BlobMigrationCase discoverBlobMigrationCase() throws IOException {
+        boolean srcDefined = options.isSrcBlobStoreDefined() || 
storeArguments.getSrcType() == JCR2_DIR_XML;
+        boolean dstDefined = options.isDstBlobStoreDefined();
+        boolean copyBinaries = options.isCopyBinaries();
+
+        boolean srcSegment = storeArguments.getSrcType().isSegment();
+        boolean dstSegment = storeArguments.getDstType().isSegment();
+
+        // default case, no datastore-related arguments given, but blobs are 
stored externally
+        if (!srcDefined && !dstDefined && !srcEmbedded && !copyBinaries) {
+            if (srcSegment && !dstSegment) { // segment -> document is not 
supported for this case
+                return BlobMigrationCase.UNSUPPORTED;
+            } else { // we try to copy references using MissingBlobStore
+                return BlobMigrationCase.COPY_REFERENCES;
+            }
+            // can't copy binaries if they are stored externally and we don't 
know where
+        } else if (!srcDefined && !dstDefined && !srcEmbedded && copyBinaries) 
{
+            return BlobMigrationCase.UNSUPPORTED;
+            // can't copy binaries if they are stored externally and we don't 
know where
+            // (even if the destination datastore is defined)
+        } else if (!srcDefined && !srcEmbedded && dstDefined) {
+            return BlobMigrationCase.UNSUPPORTED;
+            // source is embedded and no destination given
+        } else if (!srcDefined && srcEmbedded && !dstDefined) {
+            return BlobMigrationCase.EMBEDDED_TO_EMBEDDED;
+            // source is embedded and the destination is given
+        } else if (!srcDefined && srcEmbedded && dstDefined) {
+            return BlobMigrationCase.EMBEDDED_TO_EXTERNAL;
+            // source is given, no destination, but also no --copy-binaries -> 
copy references
+        } else if (srcDefined && !dstDefined && !copyBinaries) {
+            return BlobMigrationCase.COPY_REFERENCES;
+            // source is given, no destination, but --copy-binaries -> copy to 
embedded
+        } else if (srcDefined && !dstDefined && copyBinaries) {
+            return BlobMigrationCase.EXTERNAL_TO_EMBEDDED;
+            // source and destination is given
+        } else if (srcDefined && dstDefined) {
+            return BlobMigrationCase.EXTERNAL_TO_EXTERNAL;
+        }
+        return BlobMigrationCase.UNSUPPORTED;
+    }
+}

Added: 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/MigrationCliArguments.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/MigrationCliArguments.java?rev=1792993&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/MigrationCliArguments.java
 (added)
+++ 
jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/MigrationCliArguments.java
 Fri Apr 28 07:16:13 2017
@@ -0,0 +1,67 @@
+/*
+ * 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.cli.parser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import joptsimple.OptionSet;
+
+public final class MigrationCliArguments {
+
+    private final OptionSet options;
+
+    private final List<String> arguments;
+
+    public MigrationCliArguments(OptionSet options) throws 
CliArgumentException {
+        this.options = options;
+        this.arguments = getNonOptionArguments();
+    }
+
+    private List<String> getNonOptionArguments() {
+        List<String> args = new ArrayList<String>();
+        for (Object o : options.nonOptionArguments()) {
+            args.add(o.toString());
+        }
+        return args;
+    }
+
+    public boolean hasOption(String optionName) {
+        return options.has(optionName);
+    }
+
+    public String getOption(String optionName) {
+        return (String) options.valueOf(optionName);
+    }
+
+    public int getIntOption(String optionName) {
+        return (Integer) options.valueOf(optionName);
+    }
+
+    public String[] getOptionList(String optionName) {
+        String option = getOption(optionName);
+        if (option == null) {
+            return null;
+        } else {
+            return option.split(",");
+        }
+    }
+
+    public List<String> getArguments() {
+        return arguments;
+    }
+}


Reply via email to