You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by to...@apache.org on 2017/05/22 11:39:44 UTC

svn commit: r1795794 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/ oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/ oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/ ...

Author: tomekr
Date: Mon May 22 11:39:43 2017
New Revision: 1795794

URL: http://svn.apache.org/viewvc?rev=1795794&view=rev
Log:
OAK-6240: Sidegrade support for DocumentNodeStore to Secondary NodeStore

Added:
    jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/MetadataExposingNodeState.java
    jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/JdbcToSegmentWithMetadataTest.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java
    jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegrade.java
    jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/MigrationFactory.java
    jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/MigrationOptions.java
    jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/AbstractDecoratedNodeState.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java?rev=1795794&r1=1795793&r2=1795794&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java Mon May 22 11:39:43 2017
@@ -48,8 +48,8 @@ import static org.apache.jackrabbit.oak.
  */
 public class DelegatingDocumentNodeState extends AbstractDocumentNodeState {
     //Hidden props holding DocumentNodeState meta properties
-    static final String PROP_REVISION = ":doc-rev";
-    static final String PROP_LAST_REV = ":doc-lastRev";
+    public static final String PROP_REVISION = ":doc-rev";
+    public static final String PROP_LAST_REV = ":doc-lastRev";
 
     private static final Predicate<PropertyState> NOT_META_PROPS = new Predicate<PropertyState>() {
         @Override

Modified: jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegrade.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegrade.java?rev=1795794&r1=1795793&r2=1795794&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegrade.java (original)
+++ jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegrade.java Mon May 22 11:39:43 2017
@@ -46,6 +46,7 @@ import org.apache.jackrabbit.oak.upgrade
 import org.apache.jackrabbit.oak.upgrade.checkpoint.CheckpointRetriever;
 import org.apache.jackrabbit.oak.upgrade.cli.node.TarNodeStore;
 import org.apache.jackrabbit.oak.upgrade.nodestate.FilteringNodeState;
+import org.apache.jackrabbit.oak.upgrade.nodestate.MetadataExposingNodeState;
 import org.apache.jackrabbit.oak.upgrade.nodestate.NameFilteringNodeState;
 import org.apache.jackrabbit.oak.upgrade.nodestate.report.LoggingReporter;
 import org.apache.jackrabbit.oak.upgrade.nodestate.report.ReportingNodeState;
@@ -128,6 +129,8 @@ public class RepositorySidegrade {
 
     private boolean onlyVerify = false;
 
+    private boolean migrateDocumentMetadata = false;
+
     private List<CommitHook> customCommitHooks = null;
 
     VersionCopyConfiguration versionCopyConfiguration = new VersionCopyConfiguration();
@@ -273,6 +276,10 @@ public class RepositorySidegrade {
         this.forceCheckpoints = forceCheckpoints;
     }
 
+    public void setMigrateDocumentMetadata(boolean migrateDocumentMetadata) {
+        this.migrateDocumentMetadata = migrateDocumentMetadata;
+    }
+
     /**
      * Copies the full content from the source to the target repository.
      * <p>
@@ -310,11 +317,6 @@ public class RepositorySidegrade {
     }
 
     private void copyState() throws CommitFailedException, RepositoryException {
-        final List<CommitHook> hooks = new ArrayList<>();
-        if (customCommitHooks != null) {
-            hooks.addAll(customCommitHooks);
-        }
-
         boolean migrateCheckpoints = true;
         if (!isCompleteMigration() && !forceCheckpoints) {
             LOG.info("Checkpoints won't be migrated because of the specified paths");
@@ -336,30 +338,7 @@ public class RepositorySidegrade {
             migrateCheckpoints = migrateWithCheckpoints();
         }
         if (!migrateCheckpoints) {
-            NodeState sourceRoot = wrapSource(source.getRoot());
-            NodeBuilder targetRoot = target.getRoot().builder();
-            copyWorkspace(sourceRoot, targetRoot);
-            removeCheckpointReferences(targetRoot);
-            if (includeIndex) {
-                IndexCopier.copy(sourceRoot, targetRoot, includePaths);
-            }
-            if (!versionCopyConfiguration.isCopyAll()) {
-                NodeBuilder versionStorage = VersionHistoryUtil.getVersionStorage(targetRoot);
-                if (!versionStorage.exists()) { // it's possible that this is a new repository and the version storage
-                                                // hasn't been created/copied yet
-                    versionStorage = VersionHistoryUtil.createVersionStorage(targetRoot);
-                }
-                if (!versionCopyConfiguration.skipOrphanedVersionsCopy()) {
-                    copyVersionStorage(targetRoot, getVersionStorage(sourceRoot), versionStorage, versionCopyConfiguration);
-                }
-                hooks.add(new EditorHook(new VersionableEditor.Provider(sourceRoot, getWorkspaceName(), versionCopyConfiguration)));
-            }
-            // type validation, reference and indexing hooks
-            hooks.add(new EditorHook(new CompositeEditorProvider(
-                    createTypeEditorProvider(),
-                    createIndexEditorProvider()
-            )));
-            target.merge(targetRoot, new LoggingCompositeHook(hooks, null, false), CommitInfo.EMPTY);
+            migrateWithoutCheckpoints();
         }
     }
 
@@ -375,18 +354,18 @@ public class RepositorySidegrade {
         NodeBuilder targetRoot = target.getRoot().builder();
         for (CheckpointRetriever.Checkpoint checkpoint : checkpoints) {
             NodeState checkpointRoot = source.retrieve(checkpoint.getName());
-            if (!isCompleteMigration()) {
-                checkpointRoot = FilteringNodeState.wrap("/", checkpointRoot, includePaths, excludePaths, fragmentPaths, excludeFragments);
-            }
+            boolean tracePaths;
             if (previousRoot == EmptyNodeState.EMPTY_NODE) {
                 LOG.info("Migrating first checkpoint: {}", checkpoint.getName());
-                wrapSource(checkpointRoot).compareAgainstBaseState(previousRoot, new ApplyDiff(targetRoot));
+                tracePaths = true;
             } else {
                 LOG.info("Applying diff to {}", checkpoint.getName());
-                checkpointRoot.compareAgainstBaseState(previousRoot, new ApplyDiff(targetRoot));
+                tracePaths = false;
             }
+            NodeState currentRoot = wrapSource(checkpointRoot, tracePaths, true);
+            currentRoot.compareAgainstBaseState(previousRoot, new ApplyDiff(targetRoot));
             target.merge(targetRoot, EmptyHook.INSTANCE, CommitInfo.EMPTY);
-            previousRoot = checkpointRoot;
+            previousRoot = currentRoot;
 
             Map<String, String> checkpointInfo = source.checkpointInfo(checkpoint.getName());
             String newCheckpointName = target.checkpoint(checkpoint.getExpiryTime() - System.currentTimeMillis(), checkpointInfo);
@@ -397,16 +376,16 @@ public class RepositorySidegrade {
         }
 
         NodeState sourceRoot = source.getRoot();
-        if (!isCompleteMigration()) {
-            sourceRoot = FilteringNodeState.wrap("/", sourceRoot, includePaths, excludePaths, fragmentPaths, excludeFragments);
-        }
+        boolean tracePaths;
         if (previousRoot == EmptyNodeState.EMPTY_NODE) {
             LOG.info("No checkpoints found; migrating head");
-            wrapSource(sourceRoot).compareAgainstBaseState(previousRoot, new ApplyDiff(targetRoot));
+            tracePaths = true;
         } else {
             LOG.info("Applying diff to head");
-            sourceRoot.compareAgainstBaseState(previousRoot, new ApplyDiff(targetRoot));
+            tracePaths = false;
         }
+        NodeState currentRoot = wrapSource(sourceRoot, tracePaths, true);
+        currentRoot.compareAgainstBaseState(previousRoot, new ApplyDiff(targetRoot));
 
         LOG.info("Rewriting checkpoint names in /:async {}", nameToRevision);
         NodeBuilder async = targetRoot.getChildNode(":async");
@@ -430,6 +409,38 @@ public class RepositorySidegrade {
         return true;
     }
 
+    private void migrateWithoutCheckpoints() throws CommitFailedException, RepositoryException {
+        final List<CommitHook> hooks = new ArrayList<>();
+        if (customCommitHooks != null) {
+            hooks.addAll(customCommitHooks);
+        }
+
+        NodeState sourceRoot = wrapSource(source.getRoot(), true, false);
+        NodeBuilder targetRoot = target.getRoot().builder();
+        copyWorkspace(sourceRoot, targetRoot);
+        removeCheckpointReferences(targetRoot);
+        if (includeIndex) {
+            IndexCopier.copy(sourceRoot, targetRoot, includePaths);
+        }
+        if (!versionCopyConfiguration.isCopyAll()) {
+            NodeBuilder versionStorage = VersionHistoryUtil.getVersionStorage(targetRoot);
+            if (!versionStorage.exists()) { // it's possible that this is a new repository and the version storage
+                // hasn't been created/copied yet
+                versionStorage = VersionHistoryUtil.createVersionStorage(targetRoot);
+            }
+            if (!versionCopyConfiguration.skipOrphanedVersionsCopy()) {
+                copyVersionStorage(targetRoot, getVersionStorage(sourceRoot), versionStorage, versionCopyConfiguration);
+            }
+            hooks.add(new EditorHook(new VersionableEditor.Provider(sourceRoot, getWorkspaceName(), versionCopyConfiguration)));
+        }
+        // type validation, reference and indexing hooks
+        hooks.add(new EditorHook(new CompositeEditorProvider(
+                createTypeEditorProvider(),
+                createIndexEditorProvider()
+        )));
+        target.merge(targetRoot, new LoggingCompositeHook(hooks, null, false), CommitInfo.EMPTY);
+    }
+
     private boolean isCompleteMigration() {
         return includePaths.equals(DEFAULT_INCLUDE_PATHS) && excludePaths.equals(DEFAULT_EXCLUDE_PATHS) && excludeFragments.equals(DEFAULT_EXCLUDE_FRAGMENTS) && mergePaths.equals(DEFAULT_MERGE_PATHS) && fragmentPaths.equals(DEFAULT_FRAGMENT_PATHS);
     }
@@ -514,15 +525,21 @@ public class RepositorySidegrade {
         }
     }
 
-    private NodeState wrapSource(NodeState source) {
-        final NodeState reportingSourceRoot = ReportingNodeState.wrap(source, new LoggingReporter(LOG, "Copying", LOG_NODE_COPY, -1));
-        final NodeState sourceRoot;
+    private NodeState wrapSource(NodeState source, boolean tracePaths, boolean filterPaths) {
+        NodeState wrapped = source;
+        if (migrateDocumentMetadata) {
+            wrapped = MetadataExposingNodeState.wrap(wrapped);
+        }
+        if (!isCompleteMigration() && filterPaths) {
+            wrapped = FilteringNodeState.wrap("/", wrapped, includePaths, excludePaths, fragmentPaths, excludeFragments);
+        }
+        if (tracePaths) {
+            wrapped = ReportingNodeState.wrap(wrapped, new LoggingReporter(LOG, "Copying", LOG_NODE_COPY, -1));
+        }
         if (filterLongNames) {
-            sourceRoot = NameFilteringNodeState.wrap(reportingSourceRoot);
-        } else {
-            sourceRoot = reportingSourceRoot;
+            wrapped = NameFilteringNodeState.wrap(wrapped);
         }
-        return sourceRoot;
+        return wrapped;
     }
 
     private boolean targetExists() {

Modified: jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/MigrationFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/MigrationFactory.java?rev=1795794&r1=1795793&r2=1795794&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/MigrationFactory.java (original)
+++ jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/MigrationFactory.java Mon May 22 11:39:43 2017
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.upgrad
 
 import java.io.IOException;
 import java.util.Iterator;
+import java.util.List;
 import java.util.ServiceLoader;
 
 import javax.jcr.RepositoryException;
@@ -99,10 +100,7 @@ public class MigrationFactory {
         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());
+        upgrade.setCustomCommitHooks(loacCommitHooks());
         return upgrade;
     }
 
@@ -131,7 +129,16 @@ public class MigrationFactory {
         sidegrade.setOnlyVerify(options.isOnlyVerify());
         sidegrade.setSkipCheckpoints(options.isSkipCheckpoints());
         sidegrade.setForceCheckpoints(options.isForceCheckpoints());
+        sidegrade.setMigrateDocumentMetadata(options.isAddSecondaryMetadata());
+        sidegrade.setCustomCommitHooks(loacCommitHooks());
         return sidegrade;
     }
 
+    private List<CommitHook> loacCommitHooks() {
+        ServiceLoader<CommitHook> loader = ServiceLoader.load(CommitHook.class);
+        Iterator<CommitHook> iterator = loader.iterator();
+        ImmutableList.Builder<CommitHook> builder = ImmutableList.<CommitHook> builder().addAll(iterator);
+        return builder.build();
+    }
+
 }

Modified: jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/MigrationOptions.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/MigrationOptions.java?rev=1795794&r1=1795793&r2=1795794&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/MigrationOptions.java (original)
+++ jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/MigrationOptions.java Mon May 22 11:39:43 2017
@@ -32,6 +32,8 @@ public class MigrationOptions {
 
     private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
 
+    private static final boolean ADD_SECONDARY_METADATA = Boolean.getBoolean("oak.upgrade.addSecondaryMetadata");
+
     private final boolean copyBinaries;
 
     private final boolean disableMmap;
@@ -238,6 +240,8 @@ public class MigrationOptions {
         return forceCheckpoints;
     }
 
+    public boolean isAddSecondaryMetadata() { return ADD_SECONDARY_METADATA; }
+
     public String getSrcUser() {
         return srcUser;
     }
@@ -387,6 +391,10 @@ public class MigrationOptions {
             log.info("Checkpoints will be migrated even with the custom paths specified");
         }
 
+        if (ADD_SECONDARY_METADATA) {
+            log.info("Secondary metadata will be added");
+        }
+
         log.info("Cache size: {} MB", cacheSizeInMB);
 
     }

Modified: jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/AbstractDecoratedNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/AbstractDecoratedNodeState.java?rev=1795794&r1=1795793&r2=1795794&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/AbstractDecoratedNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/AbstractDecoratedNodeState.java Mon May 22 11:39:43 2017
@@ -36,6 +36,7 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 
 import static com.google.common.base.Predicates.notNull;
 import static org.apache.jackrabbit.oak.plugins.tree.impl.TreeConstants.OAK_CHILD_ORDER;
@@ -64,6 +65,11 @@ public abstract class AbstractDecoratedN
         return false;
     }
 
+    @Nonnull
+    protected Iterable<PropertyState> getNewPropertyStates() {
+        return Collections.emptyList();
+    }
+
     @CheckForNull
     protected abstract PropertyState decorateProperty(@Nonnull final PropertyState delegatePropertyState);
 
@@ -149,7 +155,16 @@ public abstract class AbstractDecoratedN
     @Override
     @CheckForNull
     public PropertyState getProperty(@Nonnull String name) {
-        return decorate(delegate.getProperty(name));
+        if (delegate.hasProperty(name)) {
+            return decorate(delegate.getProperty(name));
+        } else {
+            for (PropertyState p : getNewPropertyStates()) {
+                if (name.equals(p.getName())) {
+                    return p;
+                }
+            }
+            return null;
+        }
     }
 
     @Override
@@ -165,7 +180,7 @@ public abstract class AbstractDecoratedN
                     }
                 }
         );
-        return Iterables.filter(propertyStates, notNull());
+        return Iterables.filter(Iterables.concat(propertyStates, getNewPropertyStates()), notNull());
     }
 
     /**

Added: jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/MetadataExposingNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/MetadataExposingNodeState.java?rev=1795794&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/MetadataExposingNodeState.java (added)
+++ jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/MetadataExposingNodeState.java Mon May 22 11:39:43 2017
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.upgrade.nodestate;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import javax.annotation.Nonnull;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.jackrabbit.oak.plugins.document.secondary.DelegatingDocumentNodeState.PROP_LAST_REV;
+import static org.apache.jackrabbit.oak.plugins.document.secondary.DelegatingDocumentNodeState.PROP_REVISION;
+import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
+
+public class MetadataExposingNodeState extends AbstractDecoratedNodeState {
+
+    private final List<PropertyState> metadataProperties;
+
+    public MetadataExposingNodeState(AbstractDocumentNodeState documentNodeState) {
+        super(documentNodeState);
+
+        metadataProperties = new ArrayList<>(2);
+        if (PathUtils.denotesRoot(documentNodeState.getPath())) {
+            metadataProperties.add(createProperty(PROP_REVISION, documentNodeState.getRootRevision().asString()));
+        }
+        metadataProperties.add(createProperty(PROP_LAST_REV, documentNodeState.getLastRevision().asString()));
+    }
+
+    @Nonnull
+    @Override
+    protected Iterable<PropertyState> getNewPropertyStates() {
+        return metadataProperties;
+    }
+
+    @Nonnull
+    @Override
+    protected NodeState decorateChild(@Nonnull String name, @Nonnull NodeState delegateChild) {
+        return wrap(delegateChild);
+    }
+
+    @Override
+    protected PropertyState decorateProperty(@Nonnull PropertyState delegatePropertyState) {
+        return delegatePropertyState;
+    }
+
+    public static NodeState wrap(NodeState wrapped) {
+        if (wrapped instanceof AbstractDocumentNodeState) {
+            return new MetadataExposingNodeState((AbstractDocumentNodeState) wrapped);
+        } else if (wrapped instanceof MetadataExposingNodeState) {
+            return wrapped;
+        } else if (wrapped instanceof AbstractDecoratedNodeState) {
+            NodeState unwrapped = wrapped;
+            for (int i = 0; i < 10; i++) {
+                if (unwrapped instanceof AbstractDecoratedNodeState) {
+                    unwrapped = ((AbstractDecoratedNodeState) unwrapped).delegate;
+                } else {
+                    break;
+                }
+            }
+            if (unwrapped instanceof AbstractDocumentNodeState) {
+                return unwrapped;
+            }
+        }
+        throw new IllegalArgumentException();
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/JdbcToSegmentWithMetadataTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/JdbcToSegmentWithMetadataTest.java?rev=1795794&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/JdbcToSegmentWithMetadataTest.java (added)
+++ jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/JdbcToSegmentWithMetadataTest.java Mon May 22 11:39:43 2017
@@ -0,0 +1,62 @@
+/*
+ * 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 org.apache.jackrabbit.oak.upgrade.cli.container.JdbcNodeStoreContainer;
+import org.apache.jackrabbit.oak.upgrade.cli.container.NodeStoreContainer;
+import org.apache.jackrabbit.oak.upgrade.cli.container.SegmentNodeStoreContainer;
+import org.junit.BeforeClass;
+
+import java.io.IOException;
+
+public class JdbcToSegmentWithMetadataTest extends AbstractOak2OakTest {
+
+    private final NodeStoreContainer source;
+
+    private final NodeStoreContainer destination;
+
+    @BeforeClass
+    public static void setMetadataProperty() throws IOException {
+        System.setProperty("oak.upgrade.addSecondaryMetadata", "true");
+    }
+
+    public JdbcToSegmentWithMetadataTest() throws IOException {
+        source = new JdbcNodeStoreContainer();
+        destination = new SegmentNodeStoreContainer();
+    }
+
+    @Override
+    protected NodeStoreContainer getSourceContainer() {
+        return source;
+    }
+
+    @Override
+    protected NodeStoreContainer getDestinationContainer() {
+        return destination;
+    }
+
+    @Override
+    protected String[] getArgs() {
+        return new String[] { "--src-user", "sa", "--src-password", "sa", source.getDescription(),
+                destination.getDescription() };
+    }
+
+    @Override
+    protected boolean supportsCheckpointMigration() {
+        return true;
+    }
+}