Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/StoreType.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/StoreType.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/StoreType.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/StoreType.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,154 @@
+/*
+ * 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 static 
org.apache.jackrabbit.oak.upgrade.cli.node.Jackrabbit2Factory.isJcr2Repository;
+import static 
org.apache.jackrabbit.oak.upgrade.cli.node.Jackrabbit2Factory.isRepositoryXml;
+
+import org.apache.jackrabbit.oak.upgrade.cli.node.Jackrabbit2Factory;
+import org.apache.jackrabbit.oak.upgrade.cli.node.JdbcFactory;
+import org.apache.jackrabbit.oak.upgrade.cli.node.MongoFactory;
+import org.apache.jackrabbit.oak.upgrade.cli.node.SegmentFactory;
+import org.apache.jackrabbit.oak.upgrade.cli.node.StoreFactory;
+import 
org.apache.jackrabbit.oak.upgrade.cli.parser.StoreArguments.MigrationDirection;
+
+public enum StoreType {
+    JCR2_XML {
+        @Override
+        public boolean matches(String argument) {
+            return isRepositoryXml(argument);
+        }
+
+        @Override
+        public StoreFactory createFactory(String[] paths, MigrationDirection 
direction, MigrationOptions migrationOptions) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isSupportLongNames() {
+            return true;
+        }
+    },
+    JCR2_DIR {
+        @Override
+        public boolean matches(String argument) {
+            return isJcr2Repository(argument);
+        }
+
+        @Override
+        public StoreFactory createFactory(String[] paths, MigrationDirection 
direction, MigrationOptions migrationOptions) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isSupportLongNames() {
+            return true;
+        }
+    },
+    JCR2_DIR_XML {
+        @Override
+        public boolean matches(String argument) {
+            return false;
+        }
+
+        @Override
+        public StoreFactory createFactory(String[] paths, MigrationDirection 
direction, MigrationOptions migrationOptions) {
+            return new StoreFactory(new Jackrabbit2Factory(paths[0], 
paths[1]));
+        }
+
+        @Override
+        public boolean isSupportLongNames() {
+            return true;
+        }
+    },
+    JDBC {
+        @Override
+        public boolean matches(String argument) {
+            return argument.startsWith("jdbc:");
+        }
+
+        @Override
+        public StoreFactory createFactory(String[] paths, MigrationDirection 
direction, MigrationOptions migrationOptions) {
+            String username, password;
+            if (direction == MigrationDirection.SRC) {
+                username = migrationOptions.getSrcUser();
+                password = migrationOptions.getSrcPassword();
+            } else {
+                username = migrationOptions.getDstUser();
+                password = migrationOptions.getDstPassword();
+            }
+            return new StoreFactory(
+                    new JdbcFactory(paths[0], 
migrationOptions.getCacheSizeInMB(), username, password, direction == 
MigrationDirection.SRC));
+        }
+
+        @Override
+        public boolean isSupportLongNames() {
+            return false;
+        }
+    },
+    MONGO {
+        @Override
+        public boolean matches(String argument) {
+            return argument.startsWith("mongodb://");
+        }
+
+        @Override
+        public StoreFactory createFactory(String[] paths, MigrationDirection 
direction, MigrationOptions migrationOptions) {
+            return new StoreFactory(new MongoFactory(paths[0], 
migrationOptions.getCacheSizeInMB(), direction == MigrationDirection.SRC));
+        }
+
+        @Override
+        public boolean isSupportLongNames() {
+            return false;
+        }
+    },
+    SEGMENT {
+        @Override
+        public boolean matches(String argument) {
+            return true;
+        }
+
+        @Override
+        public StoreFactory createFactory(String[] paths, MigrationDirection 
direction, MigrationOptions migrationOptions) {
+            return new StoreFactory(new SegmentFactory(paths[0], 
migrationOptions.isDisableMmap(), direction == MigrationDirection.SRC));
+        }
+
+        @Override
+        public boolean isSupportLongNames() {
+            return true;
+        }
+    };
+
+    public static StoreType getMatchingType(String argument) {
+        for (StoreType t : values()) {
+            if (t.matches(argument)) {
+                return t;
+            }
+        }
+        return SEGMENT;
+    }
+
+    public abstract boolean matches(String argument);
+
+    public abstract StoreFactory createFactory(String[] paths, 
MigrationDirection direction, MigrationOptions migrationOptions);
+
+    public abstract boolean isSupportLongNames();
+
+    public boolean isSegment() {
+        return this == SEGMENT;
+    }
+}
\ No newline at end of file

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/AbstractDecoratedNodeState.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/AbstractDecoratedNodeState.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/AbstractDecoratedNodeState.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/AbstractDecoratedNodeState.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,240 @@
+/*
+ * 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 com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+import org.apache.jackrabbit.oak.spi.state.ReadOnlyBuilder;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import static com.google.common.base.Predicates.notNull;
+import static 
org.apache.jackrabbit.oak.plugins.tree.impl.TreeConstants.OAK_CHILD_ORDER;
+
+public abstract class AbstractDecoratedNodeState extends AbstractNodeState {
+
+    protected final NodeState delegate;
+
+    protected AbstractDecoratedNodeState(@Nonnull final NodeState delegate) {
+        this.delegate = delegate;
+    }
+
+    protected boolean hideChild(@Nonnull final String name, @Nonnull final 
NodeState delegateChild) {
+        return false;
+    }
+
+    @Nonnull
+    protected abstract NodeState decorateChild(@Nonnull final String name, 
@Nonnull final NodeState delegateChild);
+
+    @Nonnull
+    private NodeState decorate(@Nonnull final String name, @Nonnull final 
NodeState child) {
+        return hideChild(name, child) ? EmptyNodeState.MISSING_NODE : 
decorateChild(name, child);
+    }
+
+    protected boolean hideProperty(@Nonnull final String name) {
+        return false;
+    }
+
+    @CheckForNull
+    protected abstract PropertyState decorateProperty(@Nonnull final 
PropertyState delegatePropertyState);
+
+    @CheckForNull
+    private PropertyState decorate(@Nullable final PropertyState property) {
+        return property == null || hideProperty(property.getName()) ? null : 
decorateProperty(property);
+    }
+
+    /**
+     * Convenience method to help implementations that hide nodes set the
+     * :childOrder (OAK_CHILD_ORDER) property to its correct value.
+     * <br>
+     * Intended to be used to implement {@link 
#decorateProperty(PropertyState)}.
+     *
+     * @param nodeState The current node state.
+     * @param propertyState The property that chould be checked.
+     * @return The original propertyState, unless the property is called 
{@code :childOrder}.
+     */
+    protected static PropertyState fixChildOrderPropertyState(NodeState 
nodeState, PropertyState propertyState) {
+        if (propertyState != null && 
OAK_CHILD_ORDER.equals(propertyState.getName())) {
+            final Collection<String> childNodeNames = new ArrayList<String>();
+            Iterables.addAll(childNodeNames, nodeState.getChildNodeNames());
+            final Iterable<String> values = Iterables.filter(
+                    propertyState.getValue(Type.NAMES), 
Predicates.in(childNodeNames));
+            return PropertyStates.createProperty(OAK_CHILD_ORDER, values, 
Type.NAMES);
+        }
+        return propertyState;
+    }
+
+    /**
+     * The AbstractDecoratedNodeState implementation returns a 
ReadOnlyBuilder, which
+     * will fail for any mutable operation.
+     *
+     * This method can be overridden to return a different NodeBuilder 
implementation.
+     *
+     * @return a NodeBuilder instance corresponding to this NodeState.
+     */
+    @Override
+    @Nonnull
+    public NodeBuilder builder() {
+        return new ReadOnlyBuilder(this);
+    }
+
+    @Override
+    public boolean exists() {
+        return delegate.exists();
+    }
+
+    @Override
+    public boolean hasChildNode(@Nonnull final String name) {
+        return getChildNode(name).exists();
+    }
+
+    @Override
+    @Nonnull
+    public NodeState getChildNode(@Nonnull final String name) throws 
IllegalArgumentException {
+        return decorate(name, delegate.getChildNode(name));
+    }
+
+    @Override
+    @Nonnull
+    public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
+        final Iterable<ChildNodeEntry> transformed = Iterables.transform(
+                delegate.getChildNodeEntries(),
+                new Function<ChildNodeEntry, ChildNodeEntry>() {
+                    @Nullable
+                    @Override
+                    public ChildNodeEntry apply(@Nullable final ChildNodeEntry 
childNodeEntry) {
+                        if (childNodeEntry != null) {
+                            final String name = childNodeEntry.getName();
+                            final NodeState nodeState = decorate(name, 
childNodeEntry.getNodeState());
+                            if (nodeState.exists()) {
+                                return new MemoryChildNodeEntry(name, 
nodeState);
+                            }
+                        }
+                        return null;
+                    }
+                }
+        );
+        return Iterables.filter(transformed, notNull());
+    }
+
+    @Override
+    @CheckForNull
+    public PropertyState getProperty(@Nonnull String name) {
+        return decorate(delegate.getProperty(name));
+    }
+
+    @Override
+    @Nonnull
+    public Iterable<? extends PropertyState> getProperties() {
+        final Iterable<PropertyState> propertyStates = Iterables.transform(
+                delegate.getProperties(),
+                new Function<PropertyState, PropertyState>() {
+                    @Override
+                    @CheckForNull
+                    public PropertyState apply(@Nullable final PropertyState 
propertyState) {
+                        return decorate(propertyState);
+                    }
+                }
+        );
+        return Iterables.filter(propertyStates, notNull());
+    }
+
+    /**
+     * Note that any implementation-specific optimizations of wrapped 
NodeStates
+     * will not work if a AbstractDecoratedNodeState is passed into their 
{@code #equals()}
+     * method. This implementation will compare the wrapped NodeState, 
however. So
+     * optimizations work when calling {@code #equals()} on a 
ReportingNodeState.
+     *
+     * @param other Object to compare with this NodeState.
+     * @return true if the given object is equal to this NodeState, false 
otherwise.
+     */
+    @Override
+    public boolean equals(final Object other) {
+        if (other == null) {
+            return false;
+        }
+
+        if (this.getClass() == other.getClass()) {
+            final AbstractDecoratedNodeState o = (AbstractDecoratedNodeState) 
other;
+            return delegate.equals(o.delegate);
+        }
+
+        return delegate.equals(other);
+    }
+
+    @Override
+    public boolean compareAgainstBaseState(final NodeState base, final 
NodeStateDiff diff) {
+        return AbstractNodeState.compareAgainstBaseState(this, base, new 
DecoratingDiff(diff, this));
+    }
+
+    private static class DecoratingDiff implements NodeStateDiff {
+
+        private final NodeStateDiff diff;
+
+        private AbstractDecoratedNodeState nodeState;
+
+        private DecoratingDiff(final NodeStateDiff diff, final 
AbstractDecoratedNodeState nodeState) {
+            this.diff = diff;
+            this.nodeState = nodeState;
+        }
+
+        @Override
+        public boolean childNodeAdded(final String name, final NodeState 
after) {
+            return diff.childNodeAdded(name, nodeState.decorate(name, after));
+        }
+
+        @Override
+        public boolean childNodeChanged(final String name, final NodeState 
before, final NodeState after) {
+            return diff.childNodeChanged(name, before, 
nodeState.decorate(name, after));
+        }
+
+        @Override
+        public boolean childNodeDeleted(final String name, final NodeState 
before) {
+            return diff.childNodeDeleted(name, before);
+        }
+
+        @Override
+        public boolean propertyAdded(final PropertyState after) {
+            return diff.propertyAdded(nodeState.decorate(after));
+        }
+
+        @Override
+        public boolean propertyChanged(final PropertyState before, final 
PropertyState after) {
+            return diff.propertyChanged(nodeState.decorate(before), 
nodeState.decorate(after));
+        }
+
+        @Override
+        public boolean propertyDeleted(final PropertyState before) {
+            return diff.propertyDeleted(nodeState.decorate(before));
+        }
+    }
+}

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/FilteringNodeState.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/FilteringNodeState.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/FilteringNodeState.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/FilteringNodeState.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,245 @@
+/*
+ * 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 com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Set;
+
+/**
+ * NodeState implementation that decorates another node-state instance
+ * in order to hide subtrees or partial subtrees from the consumer of
+ * the API.
+ * <br>
+ * The set of visible subtrees is defined by two parameters: include paths
+ * and exclude paths, both of which are sets of absolute paths.
+ * <br>
+ * Any paths that are equal or are descendants of one of the
+ * <b>excluded paths</b> are hidden by this implementation.
+ * <br>
+ * For all <b>included paths</b>, the direct ancestors, the node-state at
+ * the path itself and all descendants are visible. Any siblings of the
+ * defined path or its ancestors are implicitly hidden (unless made visible
+ * by another include path).
+ * <br>
+ * The implementation delegates to the decorated node-state instance and
+ * filters out hidden node-states in the following methods:
+ * <ul>
+ *     <li>{@link #exists()}</li>
+ *     <li>{@link #hasChildNode(String)}</li>
+ *     <li>{@link #getChildNodeEntries()}</li>
+ * </ul>
+ * Additionally, hidden node-state names are removed from the property
+ * {@code :childOrder} in the following two methods:
+ * <ul>
+ *     <li>{@link #getProperties()}</li>
+ *     <li>{@link #getProperty(String)}</li>
+ * </ul>
+ */
+public class FilteringNodeState extends AbstractDecoratedNodeState {
+
+    public static final Set<String> ALL = ImmutableSet.of("/");
+
+    public static final Set<String> NONE = ImmutableSet.of();
+
+    private final String path;
+
+    private final Set<String> includedPaths;
+
+    private final Set<String> excludedPaths;
+
+    /**
+     * Factory method that conditionally decorates the given node-state
+     * iff the node-state is (a) hidden itself or (b) has hidden descendants.
+     *
+     * @param path The path where the node-state should be assumed to be 
located.
+     * @param delegate The node-state to decorate.
+     * @param includePaths A Set of paths that should be visible. Defaults to 
["/"] if {@code null}.
+     * @param excludePaths A Set of paths that should be hidden. Empty if 
{@code null}.
+     * @return The decorated node-state if required, the original node-state 
if decoration is unnecessary.
+     */
+    @Nonnull
+    public static NodeState wrap(
+            @Nonnull final String path,
+            @Nonnull final NodeState delegate,
+            @Nullable final Set<String> includePaths,
+            @Nullable final Set<String> excludePaths
+    ) {
+        final Set<String> includes = defaultIfEmpty(includePaths, ALL);
+        final Set<String> excludes = defaultIfEmpty(excludePaths, NONE);
+        if (hasHiddenDescendants(path, includes, excludes)) {
+            return new FilteringNodeState(path, delegate, includes, excludes);
+        }
+        return delegate;
+    }
+
+    private FilteringNodeState(
+            @Nonnull final String path,
+            @Nonnull final NodeState delegate,
+            @Nonnull final Set<String> includedPaths,
+            @Nonnull final Set<String> excludedPaths
+    ) {
+        super(delegate);
+        this.path = path;
+        this.includedPaths = includedPaths;
+        this.excludedPaths = excludedPaths;
+    }
+
+    @Nonnull
+    @Override
+    protected NodeState decorateChild(@Nonnull final String name, @Nonnull 
final NodeState child) {
+        final String childPath = PathUtils.concat(path, name);
+        return wrap(childPath, child, includedPaths, excludedPaths);
+    }
+
+    @Override
+    protected boolean hideChild(@Nonnull final String name, @Nonnull final 
NodeState delegateChild) {
+        return isHidden(PathUtils.concat(path, name), includedPaths, 
excludedPaths);
+    }
+
+    @Override
+    protected PropertyState decorateProperty(@Nonnull final PropertyState 
propertyState) {
+        return fixChildOrderPropertyState(this, propertyState);
+    }
+
+    /**
+     * Utility method to determine whether a given path should is hidden given 
the
+     * include paths and exclude paths.
+     *
+     * @param path Path to be checked
+     * @param includes Include paths
+     * @param excludes Exclude paths
+     * @return Whether the {@code path} is hidden or not.
+     */
+    private static boolean isHidden(
+            @Nonnull final String path,
+            @Nonnull final Set<String> includes,
+            @Nonnull final Set<String> excludes
+    ) {
+        return isExcluded(path, excludes) || !isIncluded(path, includes);
+    }
+
+    /**
+     * Utility method to determine whether the path itself or any of its 
descendants should
+     * be hidden given the include paths and exclude paths.
+     *
+     * @param path Path to be checked
+     * @param includePaths Include paths
+     * @param excludePaths Exclude paths
+     * @return Whether the {@code path} or any of its descendants are hidden 
or not.
+     */
+    private static boolean hasHiddenDescendants(
+            @Nonnull final String path,
+            @Nonnull final Set<String> includePaths,
+            @Nonnull final Set<String> excludePaths
+    ) {
+        return isHidden(path, includePaths, excludePaths)
+                || isAncestorOfAnyPath(path, excludePaths)
+                || isAncestorOfAnyPath(path, includePaths);
+    }
+
+    /**
+     * Utility method to check whether a given set of include paths cover the 
given
+     * {@code path}. I.e. whether the path is visible or implicitly hidden due 
to the
+     * lack of a matching include path.
+     * <br>
+     * Note: the ancestors of every include path are considered visible.
+     *
+     * @param path Path to be checked
+     * @param includePaths Include paths
+     * @return Whether the path is covered by the include paths or not.
+     */
+    private static boolean isIncluded(@Nonnull final String path, @Nonnull 
final Set<String> includePaths) {
+        return isAncestorOfAnyPath(path, includePaths)
+                || includePaths.contains(path)
+                || isDescendantOfAnyPath(path, includePaths);
+    }
+
+    /**
+     * Utility method to check whether a given set of exclude paths cover the 
given
+     * {@code path}. I.e. whether the path is hidden due to the presence of a
+     * matching exclude path.
+     *
+     * @param path Path to be checked
+     * @param excludePaths Exclude paths
+     * @return Whether the path is covered by the excldue paths or not.
+     */
+    private static boolean isExcluded(@Nonnull final String path, @Nonnull 
final Set<String> excludePaths) {
+        return excludePaths.contains(path) || isDescendantOfAnyPath(path, 
excludePaths);
+    }
+
+    /**
+     * Utility method to check whether any of the provided {@code paths} is a 
descendant
+     * of the given ancestor path.
+     *
+     * @param ancestor Ancestor path
+     * @param paths Paths that may be descendants of {@code ancestor}.
+     * @return true if {@code paths} contains a descendant of {@code 
ancestor}, false otherwise.
+     */
+    private static boolean isAncestorOfAnyPath(@Nonnull final String ancestor, 
@Nonnull final Set<String> paths) {
+        for (final String p : paths) {
+            if (PathUtils.isAncestor(ancestor, p)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Utility method to check whether any of the provided {@code paths} is an 
ancestor
+     * of the given descendant path.
+     *
+     * @param descendant Descendant path
+     * @param paths Paths that may be ancestors of {@code descendant}.
+     * @return true if {@code paths} contains an ancestor of {@code 
descendant}, false otherwise.
+     */
+    private static boolean isDescendantOfAnyPath(@Nonnull final String 
descendant, @Nonnull final Set<String> paths) {
+        for (final String p : paths) {
+            if (PathUtils.isAncestor(p, descendant)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Utility method to return the given {@code Set} if it is not empty and a 
default Set otherwise.
+     *
+     * @param value Value to check for emptiness
+     * @param defaultValue Default value
+     * @return return the given {@code Set} if it is not empty and a default 
Set otherwise
+     */
+    @Nonnull
+    private static <T> Set<T> defaultIfEmpty(@Nullable Set<T> value, @Nonnull 
Set<T> defaultValue) {
+        return !isEmpty(value) ? value : defaultValue;
+    }
+
+    /**
+     * Utility method to check whether a Set is empty, i.e. null or of size 0.
+     *
+     * @param set The Set to check.
+     * @return true if empty, false otherwise
+     */
+    private static <T> boolean isEmpty(@Nullable final Set<T> set) {
+        return set == null || set.isEmpty();
+    }
+}

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/NameFilteringNodeState.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/NameFilteringNodeState.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/NameFilteringNodeState.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/NameFilteringNodeState.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.upgrade.nodestate;
+
+import com.google.common.base.Charsets;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.plugins.document.util.Utils;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nonnull;
+
+public class NameFilteringNodeState extends AbstractDecoratedNodeState {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(NameFilteringNodeState.class);
+
+    private static final int NODE_NAME_LIMIT = 150;
+    /**
+     * Max character size in bytes in UTF8 = 4. Therefore if the number of 
characters is smaller
+     * than NODE_NAME_LIMIT / 4 we don't need to count bytes.
+     */
+    private static final int SAFE_NODE_NAME_LENGTH = NODE_NAME_LIMIT / 4;
+
+    public static NodeState wrap(final NodeState delegate) {
+        return new NameFilteringNodeState(delegate);
+    }
+
+    private NameFilteringNodeState(final NodeState delegate) {
+        super(delegate);
+    }
+
+    @Override
+    protected boolean hideChild(@Nonnull final String name, @Nonnull final 
NodeState delegateChild) {
+        if (isNameTooLong(name)) {
+            LOG.warn("Node name '{}' too long. Skipping child of {}", name, 
this);
+            return true;
+        }
+        return super.hideChild(name, delegateChild);
+    }
+
+    @Override
+    @Nonnull
+    protected NodeState decorateChild(@Nonnull final String name, @Nonnull 
final NodeState delegateChild) {
+        return wrap(delegateChild);
+    }
+
+    @Override
+    protected PropertyState decorateProperty(@Nonnull final PropertyState 
delegatePropertyState) {
+        return fixChildOrderPropertyState(this, delegatePropertyState);
+    }
+
+    /**
+     * This method checks whether the name is no longer than the maximum node
+     * name length supported by the DocumentNodeStore.
+     *
+     * @param name
+     *            to check
+     * @return true if the name is longer than {@link Utils#NODE_NAME_LIMIT}
+     */
+    public static boolean isNameTooLong(@Nonnull String name) {
+        // OAK-1589: maximum supported length of name for DocumentNodeStore
+        // is 150 bytes. Skip the sub tree if the the name is too long
+        return name.length() > SAFE_NODE_NAME_LENGTH && 
name.getBytes(Charsets.UTF_8).length > NODE_NAME_LIMIT;
+    }
+}

Modified: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/NodeStateCopier.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/NodeStateCopier.java?rev=1792995&r1=1792994&r2=1792995&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/NodeStateCopier.java
 (original)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/NodeStateCopier.java
 Fri Apr 28 07:18:26 2017
@@ -20,20 +20,24 @@ import org.apache.jackrabbit.oak.api.Com
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.annotation.Nonnull;
-import java.util.Collections;
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.ImmutableSet.copyOf;
+import static com.google.common.collect.ImmutableSet.of;
+import static java.util.Collections.emptySet;
 
 /**
  * The NodeStateCopier and NodeStateCopier.Builder classes allow
@@ -49,14 +53,46 @@ import static com.google.common.base.Pre
  * The work for a traversal without any differences between
  * {@code source} and {@code target} is equivalent to the single
  * execution of a naive equals implementation.
+ * <br>
+ * <b>Usage:</b> For most use-cases the Builder API should be
+ * preferred. It allows setting {@code includePaths},
+ * {@code excludePaths} and {@code mergePaths}.
+ * <br>
+ * <b>Include paths:</b> if include paths are set, only these paths
+ * and their sub-trees are copied. Any nodes that are not within the
+ * scope of an include path are <i>implicitly excluded</i>.
+ * <br>
+ * <b>Exclude paths:</b> if exclude paths are set, any nodes matching
+ * or below the excluded path are not copied. If an excluded node does
+ * exist in the target, it is removed (see also merge paths).
+ * <b>Merge paths:</b> if merge paths are set, any nodes matching or
+ * below the merged path will not be deleted from target, even if they
+ * are missing in (or excluded from) the source.
  */
 public class NodeStateCopier {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(NodeStateCopier.class);
 
+    private final Set<String> includePaths;
+
+    private final Set<String> excludePaths;
+
+    private final Set<String> mergePaths;
 
-    private NodeStateCopier() {
-        // no instances
+    private NodeStateCopier(Set<String> includePaths, Set<String> 
excludePaths, Set<String> mergePaths) {
+        this.includePaths = includePaths;
+        this.excludePaths = excludePaths;
+        this.mergePaths = mergePaths;
+    }
+
+    /**
+     * Create a NodeStateCopier.Builder.
+     *
+     * @return a NodeStateCopier.Builder
+     * @see org.apache.jackrabbit.oak.upgrade.nodestate.NodeStateCopier.Builder
+     */
+    public static Builder builder() {
+        return new Builder();
     }
 
     /**
@@ -65,16 +101,13 @@ public class NodeStateCopier {
      *
      * @param source NodeStore to copy from.
      * @param target NodeStore to copy to.
-     * @throws CommitFailedException
+     * @return true if the target has been modified
+     * @throws CommitFailedException if the operation fails
+     * @see 
org.apache.jackrabbit.oak.upgrade.nodestate.NodeStateCopier.Builder#copy(NodeStore,
 NodeStore)
      */
     public static boolean copyNodeStore(@Nonnull final NodeStore source, 
@Nonnull final NodeStore target)
             throws CommitFailedException {
-        final NodeBuilder builder = checkNotNull(target).getRoot().builder();
-        final boolean hasChanges = 
copyNodeState(checkNotNull(source).getRoot(), builder, "/", 
Collections.<String>emptySet());
-        if (hasChanges) {
-            source.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
-        }
-        return hasChanges;
+        return builder().copy(checkNotNull(source), checkNotNull(target));
     }
 
     /**
@@ -107,6 +140,20 @@ public class NodeStateCopier {
         return hasChanges;
     }
 
+    private boolean copyNodeState(@Nonnull final NodeState sourceRoot, 
@Nonnull final NodeBuilder targetRoot) {
+        final NodeState wrappedSource = FilteringNodeState.wrap("/", 
sourceRoot, this.includePaths, this.excludePaths);
+        boolean hasChanges = false;
+        for (String includePath : this.includePaths) {
+            hasChanges = copyMissingAncestors(sourceRoot, targetRoot, 
includePath) || hasChanges;
+            final NodeState sourceState = 
NodeStateUtils.getNode(wrappedSource, includePath);
+            if (sourceState.exists()) {
+                final NodeBuilder targetBuilder = 
getChildNodeBuilder(targetRoot, includePath);
+                hasChanges = copyNodeState(sourceState, targetBuilder, 
includePath, this.mergePaths) || hasChanges;
+            }
+        }
+        return hasChanges;
+    }
+
     /**
      * Recursively copies the source NodeState to the target NodeBuilder.
      * <br>
@@ -124,8 +171,8 @@ public class NodeStateCopier {
      *                   preserved, even if the do not exist in the source.
      * @return An indication of whether there were changes or not.
      */
-    public static boolean copyNodeState(@Nonnull final NodeState source, 
@Nonnull final NodeBuilder target,
-                                        @Nonnull final String currentPath, 
@Nonnull final Set<String> mergePaths) {
+    private static boolean copyNodeState(@Nonnull final NodeState source, 
@Nonnull final NodeBuilder target,
+                                         @Nonnull final String currentPath, 
@Nonnull final Set<String> mergePaths) {
 
 
         boolean hasChanges = false;
@@ -170,4 +217,209 @@ public class NodeStateCopier {
         }
         return false;
     }
+
+    /**
+     * Ensure that all ancestors of {@code path} are present in {@code 
targetRoot}. Copies any
+     * missing ancestors from {@code sourceRoot}.
+     *
+     * @param sourceRoot NodeState to copy from
+     * @param targetRoot NodeBuilder to copy to
+     * @param path The path along which ancestors should be copied.
+     */
+    private static boolean copyMissingAncestors(
+            final NodeState sourceRoot, final NodeBuilder targetRoot, final 
String path) {
+        NodeState current = sourceRoot;
+        NodeBuilder currentBuilder = targetRoot;
+        boolean hasChanges = false;
+        for (String name : PathUtils.elements(path)) {
+            if (current.hasChildNode(name)) {
+                final boolean targetHasChild = 
currentBuilder.hasChildNode(name);
+                current = current.getChildNode(name);
+                currentBuilder = currentBuilder.child(name);
+                if (!targetHasChild) {
+                    hasChanges = copyProperties(current, currentBuilder) || 
hasChanges;
+                }
+            }
+        }
+        return hasChanges;
+    }
+
+    /**
+     * Allows retrieving a NodeBuilder by path relative to the given root 
NodeBuilder.
+     *
+     * All NodeBuilders are created via {@link NodeBuilder#child(String)} and 
are thus
+     * implicitly created.
+     *
+     * @param root The NodeBuilder to consider the root node.
+     * @param path An absolute or relative path, which is evaluated as a 
relative path under the root NodeBuilder.
+     * @return a NodeBuilder instance, never null
+     */
+    @Nonnull
+    private static NodeBuilder getChildNodeBuilder(@Nonnull final NodeBuilder 
root, @Nonnull final String path) {
+        NodeBuilder child = root;
+        for (String name : PathUtils.elements(path)) {
+            child = child.child(name);
+        }
+        return child;
+    }
+
+    /**
+     * The NodeStateCopier.Builder allows configuring a NodeState copy 
operation with
+     * {@code includePaths}, {@code excludePaths} and {@code mergePaths}.
+     * <br>
+     * <b>Include paths</b> can define which paths should be copied from the 
source to the
+     * target.
+     * <br>
+     * <b>Exclude paths</b> allow restricting which paths should be copied. 
This is
+     * especially useful when there are individual nodes in an included path 
that
+     * should not be copied.
+     * <br>
+     * By default copying will remove items that already exist in the target 
but do
+     * not exist in the source. If this behaviour is undesired that is where 
merge
+     * paths come in.
+     * <br>
+     * <b>Merge paths</b> dictate in which parts of the tree the copy 
operation should
+     * be <i>additive</i>, i.e. the content from source is merged with the 
content
+     * in the target. Nodes that are present in the target but not in the 
source are
+     * then not deleted. However, in the case where nodes are present in both 
the source
+     * and the target, the node from the source is copied with its properties 
and any
+     * properties previously present on the target's node are lost.
+     * <br>
+     * Finally, using one of the {@code copy} methods, NodeStores or 
NodeStates can
+     * be copied.
+     */
+    public static class Builder {
+
+        private Set<String> includePaths = of("/");
+
+        private Set<String> excludePaths = emptySet();
+
+        private Set<String> mergePaths = emptySet();
+
+        private Builder() {}
+
+
+        /**
+         * Set include paths.
+         *
+         * @param paths include paths
+         * @return this Builder instance
+         * @see NodeStateCopier#NodeStateCopier(Set, Set, Set)
+         */
+        @Nonnull
+        public Builder include(@Nonnull Set<String> paths) {
+            if (!checkNotNull(paths).isEmpty()) {
+                this.includePaths = copyOf(paths);
+            }
+            return this;
+        }
+
+        /**
+         * Convenience wrapper for {@link #include(Set)}.
+         *
+         * @param paths include paths
+         * @return this Builder instance
+         * @see NodeStateCopier#NodeStateCopier(Set, Set, Set)
+         */
+        @Nonnull
+        public Builder include(@Nonnull String... paths) {
+            return include(copyOf(checkNotNull(paths)));
+        }
+
+        /**
+         * Set exclude paths.
+         *
+         * @param paths exclude paths
+         * @return this Builder instance
+         * @see NodeStateCopier#NodeStateCopier(Set, Set, Set)
+         */
+        @Nonnull
+        public Builder exclude(@Nonnull Set<String> paths) {
+            if (!checkNotNull(paths).isEmpty()) {
+                this.excludePaths = copyOf(paths);
+            }
+            return this;
+        }
+
+        /**
+         * Convenience wrapper for {@link #exclude(Set)}.
+         *
+         * @param paths exclude paths
+         * @return this Builder instance
+         * @see NodeStateCopier#NodeStateCopier(Set, Set, Set)
+         */
+        @Nonnull
+        public Builder exclude(@Nonnull String... paths) {
+            return exclude(copyOf(checkNotNull(paths)));
+        }
+
+        /**
+         * Set merge paths.
+         *
+         * @param paths merge paths
+         * @return this Builder instance
+         * @see NodeStateCopier#NodeStateCopier(Set, Set, Set)
+         */
+        @Nonnull
+        public Builder merge(@Nonnull Set<String> paths) {
+            if (!checkNotNull(paths).isEmpty()) {
+                this.mergePaths = copyOf(paths);
+            }
+            return this;
+        }
+
+        /**
+         * Convenience wrapper for {@link #merge(Set)}.
+         *
+         * @param paths merge paths
+         * @return this Builder instance
+         * @see NodeStateCopier#NodeStateCopier(Set, Set, Set)
+         */
+        @Nonnull
+        public Builder merge(@Nonnull String... paths) {
+            return merge(copyOf(checkNotNull(paths)));
+        }
+
+        /**
+         * Creates a NodeStateCopier to copy the {@code sourceRoot} NodeState 
to the
+         * {@code targetRoot} NodeBuilder, using any include, exclude and 
merge paths
+         * set on this NodeStateCopier.Builder.
+         * <br>
+         * It is the responsibility of the caller to persist any changes using 
e.g.
+         * {@link NodeStore#merge(NodeBuilder, CommitHook, CommitInfo)}.
+         *
+         * @param sourceRoot NodeState to copy from
+         * @param targetRoot NodeBuilder to copy to
+         * @return true if there were any changes, false if sourceRoot and 
targetRoot represent
+         *         the same content
+         */
+        public boolean copy(@Nonnull final NodeState sourceRoot, @Nonnull 
final NodeBuilder targetRoot) {
+            final NodeStateCopier copier = new NodeStateCopier(includePaths, 
excludePaths, mergePaths);
+            return copier.copyNodeState(checkNotNull(sourceRoot), 
checkNotNull(targetRoot));
+        }
+
+        /**
+         * Creates a NodeStateCopier to copy the {@code source} NodeStore to 
the
+         * {@code target} NodeStore, using any include, exclude and merge paths
+         * set on this NodeStateCopier.Builder.
+         * <br>
+         * Changes are automatically persisted with empty CommitHooks and 
CommitInfo
+         * via {@link NodeStore#merge(NodeBuilder, CommitHook, CommitInfo)}.
+         *
+         * @param source NodeStore to copy from
+         * @param target NodeStore to copy to
+         * @return true if there were any changes, false if source and target 
represent
+         *         the same content
+         * @throws CommitFailedException if the copy operation fails
+         */
+        public boolean copy(@Nonnull final NodeStore source, @Nonnull final 
NodeStore target)
+                throws CommitFailedException {
+            final NodeBuilder targetRoot = 
checkNotNull(target).getRoot().builder();
+            if (copy(checkNotNull(source).getRoot(), targetRoot)) {
+                target.merge(targetRoot, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+                return true;
+            }
+            return false;
+        }
+    }
 }

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/LoggingReporter.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/LoggingReporter.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/LoggingReporter.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/LoggingReporter.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.upgrade.nodestate.report;
+
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.slf4j.Logger;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A Reporter implementation that logs every nth node
+ * and/or any nth property to the given logger on {@code info}
+ * level.
+ */
+public class LoggingReporter extends PeriodicReporter {
+
+    private final Logger logger;
+
+    private final String verb;
+
+    /**
+     * Constructor that allows setting the intervals to log node and property
+     * accesses to a given logger.
+     *
+     * @param logger              The logger to log the progress to.
+     * @param nodeLogInterval     Every how many nodes a log message should be 
written.
+     * @param propertyLogInterval Every how many properties a log message 
should be written.
+     */
+    public LoggingReporter(final Logger logger, final int nodeLogInterval, 
final int propertyLogInterval) {
+        this(logger, "Loading", nodeLogInterval, propertyLogInterval);
+    }
+
+    /**
+     * Like {@link #LoggingReporter(Logger, int, int)}, however this 
constructor allow
+     * to customize the verb of the log message.
+     * <br>
+     * The messages are of the format: "{verb} node #100: /path/to/the/node
+     *
+     * @param logger              The logger to log the progress to.
+     * @param verb                The verb to use for logging.
+     * @param nodeLogInterval     Every how many nodes a log message should be 
written.
+     * @param propertyLogInterval Every how many properties a log message 
should be written.
+     */
+    public LoggingReporter(final Logger logger, final String verb, final int 
nodeLogInterval, final int propertyLogInterval) {
+        super(nodeLogInterval, propertyLogInterval);
+        this.logger = logger;
+        this.verb = verb;
+    }
+
+    @Override
+    protected void reportPeriodicNode(final long count, @Nonnull final 
ReportingNodeState nodeState) {
+        logger.info("{} node #{}: {}", verb, count, nodeState.getPath());
+    }
+
+    @Override
+    protected void reportPeriodicProperty(final long count, @Nonnull final 
ReportingNodeState parent, @Nonnull final String propertyName) {
+        logger.info("{} properties #{}: {}", verb, count, 
PathUtils.concat(parent.getPath(), propertyName));
+    }
+}

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/PeriodicReporter.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/PeriodicReporter.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/PeriodicReporter.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/PeriodicReporter.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.upgrade.nodestate.report;
+
+import javax.annotation.Nonnull;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Abstract class that simplifies development of a Reporter
+ * that should only report every nth event (node or property seen).
+ */
+public abstract class PeriodicReporter implements Reporter {
+
+    private final int nodeLogInterval;
+
+    private final int propertyLogInterval;
+
+    private AtomicLong nodes = new AtomicLong(0);
+
+    private AtomicLong properties = new AtomicLong(0);
+
+    protected PeriodicReporter(final int nodeLogInterval, final int 
propertyLogInterval) {
+        this.nodeLogInterval = nodeLogInterval;
+        this.propertyLogInterval = propertyLogInterval;
+    }
+
+    /**
+     * Reset the node and property counts to 0. Inheriting implementations
+     * may reset their own internal state.
+     */
+    protected void reset() {
+        nodes.set(0);
+        properties.set(0);
+    }
+
+    /**
+     * Callback called every nth time a node is accessed.
+     *
+     * @param count The count of reported nodes.
+     * @param nodeState The node that was reported.
+     */
+    protected abstract void reportPeriodicNode(
+            final long count, @Nonnull final ReportingNodeState nodeState);
+
+    /**
+     * Callback called every nth time a property is accessed.
+     *
+     * @param count The count of reported properties.
+     * @param parent The parent node of the reported property.
+     * @param propertyName The name of the reported property.
+     */
+    protected abstract void reportPeriodicProperty(
+            final long count, @Nonnull final ReportingNodeState parent, 
@Nonnull final String propertyName);
+
+    @Override
+    public final void reportNode(@Nonnull final ReportingNodeState nodeState) {
+        if (nodeLogInterval == -1) {
+            return;
+        }
+
+        final long count = nodes.incrementAndGet();
+        if (count % nodeLogInterval == 0) {
+            reportPeriodicNode(count, nodeState);
+        }
+    }
+
+    @Override
+    public final void reportProperty(@Nonnull final ReportingNodeState parent, 
@Nonnull final String propertyName) {
+        if (propertyLogInterval == -1) {
+            return;
+        }
+
+        final long count = properties.incrementAndGet();
+        if (count % propertyLogInterval == 0) {
+            reportPeriodicProperty(count, parent, propertyName);
+        }
+    }
+}

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/Reporter.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/Reporter.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/Reporter.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/Reporter.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.upgrade.nodestate.report;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A {@code Reporter} receives callbacks for every NodeState
+ * and PropertyState that was accessed via a {ReportingNodeState}
+ * instance.
+ */
+public interface Reporter {
+
+    /**
+     * Callback reporting that the given {@code nodeState} was accessed.
+     *
+     * @param nodeState The accessed {@code ReportingNodeState} instance.
+     */
+    void reportNode(@Nonnull final ReportingNodeState nodeState);
+
+    /**
+     * Callback reporting that the property named {@code propertyName}
+     * was accessed on the {@code parent} node.
+     *
+     * @param parent The parent node state of the reported property.
+     * @param propertyName The name of the reported property.
+     */
+    void reportProperty(@Nonnull final ReportingNodeState parent, @Nonnull 
final String propertyName);
+}

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/ReportingNodeState.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/ReportingNodeState.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/ReportingNodeState.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/ReportingNodeState.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.upgrade.nodestate.report;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.upgrade.nodestate.AbstractDecoratedNodeState;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * A decoration layer for NodeState instances that intercepts
+ * all accesses to NodeStates and PropertyStates (getters) and
+ * informs a {@link Reporter} via its callbacks that the respective
+ * NodeStates or PropertyStates have been accessed.
+ * <br>
+ * The decoration is deep, i.e. any child NodeStates will be
+ * decorated as well and will report to the same {@code Reporter}
+ * instance.
+ * <br>
+ * For convenience, a {@link PeriodicReporter} abstract class exists.
+ * This simplifies reporting every nth node/property only.
+ * <br>
+ * Note: Multiple accesses to the same node or property are each
+ * reported. Therefore if exactly counting unique accesses is a
+ * requirement, the reporter needs to take care of de-duplication.
+ *
+ * @see Reporter
+ * @see PeriodicReporter
+ * @see LoggingReporter
+ */
+public class ReportingNodeState extends AbstractDecoratedNodeState {
+
+    private final ReportingNodeState parent;
+    private final String name;
+    private final Reporter reporter;
+
+    /**
+     * Allows wrapping a NodeState as a ReportingNodeState. The wrapped
+     * NodeState is treated as the root of a tree (i.e. path is "/").
+     * <br>
+     * Any children accessed via this NodeState are also wrapped. Each
+     * wrapped NodeState is also reported to the provided Reporter.
+     *
+     * @param nodeState The NodeState to be wrapped.
+     * @param reporter The reporter to report to.
+     * @return the wrapped NodeState.
+     */
+    public static NodeState wrap(NodeState nodeState, Reporter reporter) {
+        return wrapAndReport(null, "/", nodeState, reporter);
+    }
+
+    private static NodeState wrapAndReport(@Nullable ReportingNodeState 
parent, @Nonnull String name,
+                                           @Nonnull NodeState delegate, 
@Nonnull Reporter reporter) {
+        final ReportingNodeState nodeState = new ReportingNodeState(parent, 
name, delegate, reporter);
+        reporter.reportNode(nodeState);
+        return nodeState;
+    }
+
+    private ReportingNodeState(ReportingNodeState parent, String name, 
NodeState delegate, Reporter reporter) {
+        super(delegate);
+        this.parent = parent;
+        this.name = name;
+        this.reporter = reporter;
+    }
+
+    /**
+     * ReportingNodeState instances provide access to their path via their
+     * parent hierarchy. Note that calculating the path on every access may
+     * incur a significant performance penalty.
+     *
+     * @return The path of the ReportingNodeState instance, assuming that
+     *         the first wrapped instance is the root node.
+     */
+    public String getPath() {
+        if (parent == null) {
+            return name;
+        }
+        return PathUtils.concat(this.parent.getPath(), name);
+    }
+
+    @Nonnull
+    @Override
+    protected NodeState decorateChild(@Nonnull final String name, @Nonnull 
final NodeState delegateChild) {
+        return wrapAndReport(this, name, delegateChild, reporter);
+    }
+
+    @Override
+    @CheckForNull
+    protected PropertyState decorateProperty(@Nonnull final PropertyState 
delegatePropertyState) {
+        reporter.reportProperty(this, delegatePropertyState.getName());
+        return delegatePropertyState;
+    }
+
+    @Override
+    public String toString() {
+        return "ReportingNodeState{" + getPath() + ", " + delegate.toString() 
+ "}";
+    }
+}

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.upgrade.security;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static 
org.apache.jackrabbit.oak.spi.security.user.UserConstants.NT_REP_AUTHORIZABLE_FOLDER;
+
+/**
+ * There are occasions where in old JR2 repositories not all ancestors on
+ * the users path are of type {@code rep:AuthorizableFolder}, thus leading
+ * to exceptions after repository upgrade.
+ * <br>
+ * In order to avoid such situations, this hook verifies that all nodes on
+ * the users and groups paths are of type {@code rep:AuthorizableFolder} and
+ * fixes the node-type if it is incorrect.
+ */
+public class AuthorizableFolderEditor extends DefaultEditor {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(AuthorizableFolderEditor.class);
+
+    private final NodeBuilder currentBuilder;
+
+    private final String groupsPath;
+
+    private final String usersPath;
+
+    private final String path;
+
+    public AuthorizableFolderEditor(final NodeBuilder builder, final String 
path, final String groupsPath, final String usersPath) {
+        this.currentBuilder = builder;
+        this.groupsPath = groupsPath;
+        this.usersPath = usersPath;
+        this.path = path;
+    }
+
+    public static EditorProvider provider(final String groupsPath, final 
String usersPath) {
+        return new EditorProvider() {
+            @Override
+            public Editor getRootEditor(final NodeState before,
+                                        final NodeState after,
+                                        final NodeBuilder builder,
+                                        final CommitInfo info) throws 
CommitFailedException {
+                return new AuthorizableFolderEditor(builder, "/", groupsPath, 
usersPath);
+            }
+        };
+    }
+
+    @Override
+    public void propertyAdded(final PropertyState after) throws 
CommitFailedException {
+         propertyChanged(null, after);
+    }
+
+    @Override
+    public void propertyChanged(final PropertyState before, final 
PropertyState after) throws CommitFailedException {
+        if (JCR_PRIMARYTYPE.equals(after.getName())) {
+            String nodeType = after.getValue(Type.STRING);
+            LOG.debug("Found {}/jcr:primaryType = {}", path, nodeType);
+            if (!nodeType.equals(NT_REP_AUTHORIZABLE_FOLDER) && 
expectAuthorizableFolder(path)) {
+                LOG.info("Changed {}/jcr:primaryType from {} to {}", path, 
nodeType, NT_REP_AUTHORIZABLE_FOLDER);
+                currentBuilder.setProperty(JCR_PRIMARYTYPE, 
NT_REP_AUTHORIZABLE_FOLDER, Type.NAME);
+            }
+        }
+    }
+
+    private boolean expectAuthorizableFolder(final String path) {
+        return !PathUtils.denotesRoot(path)
+                && (PathUtils.isAncestor(path, groupsPath) || 
path.equals(groupsPath)
+                || PathUtils.isAncestor(path, usersPath) || 
path.equals(usersPath));
+    }
+
+    @Override
+    public Editor childNodeAdded(final String name, final NodeState after) 
throws CommitFailedException {
+        return childNodeChanged(name, null, after);
+    }
+
+    @Override
+    public Editor childNodeChanged(final String name, final NodeState before, 
final NodeState after) throws CommitFailedException {
+        final String childPath = PathUtils.concat(path, name);
+        if (expectAuthorizableFolder(childPath)) {
+            LOG.debug("Found {} - descending", childPath);
+            return new AuthorizableFolderEditor(currentBuilder.child(name), 
childPath, groupsPath, usersPath);
+        } else {
+            return null;
+        }
+    }
+}

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.upgrade.version;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_CREATED;
+import static org.apache.jackrabbit.JcrConstants.NT_VERSION;
+
+import java.util.Calendar;
+import java.util.Iterator;
+
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.upgrade.DescendantsIterator;
+import org.apache.jackrabbit.oak.upgrade.nodestate.NodeStateCopier;
+import org.apache.jackrabbit.util.ISO8601;
+
+import static 
org.apache.jackrabbit.oak.plugins.version.VersionConstants.VERSION_STORE_PATH;
+
+import static 
org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getRelativeVersionHistoryPath;
+import static 
org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getVersionHistoryNodeState;
+
+/**
+ * This class allows to copy the version history, optionally filtering it with 
a
+ * given date.
+ */
+public class VersionCopier {
+
+    private final TypePredicate isVersion;
+
+    private final NodeState sourceVersionStorage;
+
+    private final NodeBuilder targetVersionStorage;
+
+    private final NodeBuilder targetRoot;
+
+    public VersionCopier(NodeBuilder targetRoot, NodeState 
sourceVersionStorage, NodeBuilder targetVersionStorage) {
+        this.isVersion = new TypePredicate(targetRoot.getNodeState(), 
NT_VERSION);
+        this.sourceVersionStorage = sourceVersionStorage;
+        this.targetVersionStorage = targetVersionStorage;
+        this.targetRoot = targetRoot;
+    }
+
+    public static void copyVersionStorage(NodeBuilder targetRoot, NodeState 
sourceVersionStorage, NodeBuilder targetVersionStorage, 
VersionCopyConfiguration config) {
+        final Iterator<NodeState> versionStorageIterator = new 
DescendantsIterator(sourceVersionStorage, 3);
+        final VersionCopier versionCopier = new VersionCopier(targetRoot, 
sourceVersionStorage, targetVersionStorage);
+
+        while (versionStorageIterator.hasNext()) {
+            final NodeState versionHistoryBucket = 
versionStorageIterator.next();
+            for (String versionHistory : 
versionHistoryBucket.getChildNodeNames()) {
+                versionCopier.copyVersionHistory(versionHistory, 
config.getOrphanedMinDate());
+            }
+        }
+    }
+
+    /**
+     * Copy history filtering versions using passed date and returns {@code
+     * true} if the history has been copied.
+     * 
+     * @param versionableUuid
+     *            Name of the version history node
+     * @param minDate
+     *            Only versions older than this date will be copied
+     * @return {@code true} if at least one version has been copied
+     */
+    public boolean copyVersionHistory(String versionableUuid, Calendar 
minDate) {
+        final String versionHistoryPath = 
getRelativeVersionHistoryPath(versionableUuid);
+        final NodeState sourceVersionHistory = 
getVersionHistoryNodeState(sourceVersionStorage, versionableUuid);
+        final Calendar lastModified = 
getVersionHistoryLastModified(sourceVersionHistory);
+
+        if (sourceVersionHistory.exists() && (lastModified.after(minDate) || 
minDate.getTimeInMillis() == 0)) {
+            NodeStateCopier.builder()
+                    .include(versionHistoryPath)
+                    .merge(VERSION_STORE_PATH)
+                    .copy(sourceVersionStorage, targetVersionStorage);
+            return true;
+        }
+        return false;
+    }
+
+    private Calendar getVersionHistoryLastModified(final NodeState 
versionHistory) {
+        Calendar youngest = Calendar.getInstance();
+        youngest.setTimeInMillis(0);
+        for (final ChildNodeEntry entry : 
versionHistory.getChildNodeEntries()) {
+            final NodeState version = entry.getNodeState();
+            if (!isVersion.apply(version)) {
+                continue;
+            }
+            if (version.hasProperty(JCR_CREATED)) {
+                final Calendar created = 
ISO8601.parse(version.getProperty(JCR_CREATED).getValue(Type.DATE));
+                if (created.after(youngest)) {
+                    youngest = created;
+                }
+            }
+        }
+        return youngest;
+    }
+}

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.upgrade.version;
+
+import java.util.Calendar;
+
+/**
+ * This class allows to configure the behaviour of the version copier.
+ */
+public class VersionCopyConfiguration {
+
+    private Calendar copyVersions;
+
+    private Calendar copyOrphanedVersions;
+
+    public VersionCopyConfiguration() {
+        final Calendar epoch = Calendar.getInstance();
+        epoch.setTimeInMillis(0);
+        this.copyVersions = epoch;
+        this.copyOrphanedVersions = epoch;
+    }
+
+    public void setCopyVersions(Calendar copyVersions) {
+        this.copyVersions = copyVersions;
+    }
+
+    public void setCopyOrphanedVersions(Calendar copyOrphanedVersions) {
+        this.copyOrphanedVersions = copyOrphanedVersions;
+    }
+
+    public Calendar getVersionsMinDate() {
+        return copyVersions;
+    }
+
+    public Calendar getOrphanedMinDate() {
+        if (copyVersions == null) {
+            return copyVersions;
+        } else if (copyOrphanedVersions != null && 
copyVersions.after(copyOrphanedVersions)) {
+            return copyVersions;
+        } else {
+            return copyOrphanedVersions;
+        }
+    }
+
+    public boolean isCopyVersions() {
+        return copyVersions != null;
+    }
+
+    public boolean skipOrphanedVersionsCopy() {
+        return copyVersions == null || copyOrphanedVersions == null;
+    }
+
+    public boolean isCopyAll() {
+        return copyVersions != null && copyVersions.getTimeInMillis() == 0 && 
copyOrphanedVersions != null && copyOrphanedVersions.getTimeInMillis() == 0;
+    }
+
+}
\ No newline at end of file

Added: 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java?rev=1792995&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java
 (added)
+++ 
jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java
 Fri Apr 28 07:18:26 2017
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.upgrade.version;
+
+import static com.google.common.collect.Iterables.concat;
+import static java.util.Collections.singleton;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONSTORAGE;
+import static 
org.apache.jackrabbit.oak.plugins.version.VersionConstants.REP_VERSIONSTORAGE;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.base.Joiner;
+
+public class VersionHistoryUtil {
+
+    public static String getRelativeVersionHistoryPath(String versionableUuid) 
{
+        return Joiner.on('/').join(concat(
+                singleton(""),
+                getRelativeVersionHistoryPathSegments(versionableUuid),
+                singleton(versionableUuid)));
+    }
+
+    /**
+     * Constructs the version history path based on the versionable's UUID.
+     *
+     * @param root The root NodeState below which to look for the version.
+     * @param versionableUuid The String representation of the versionable's 
UUID.
+     * @return The NodeState corresponding to the version history, or {@code 
null}
+     *         if it does not exist.
+     */
+    static NodeState getVersionHistoryNodeState(NodeState versionStorage, 
String versionableUuid) {
+        NodeState historyParent = versionStorage;
+        for (String segment : 
getRelativeVersionHistoryPathSegments(versionableUuid)) {
+            historyParent = historyParent.getChildNode(segment);
+        }
+        return historyParent.getChildNode(versionableUuid);
+    }
+
+    static NodeBuilder getVersionHistoryBuilder(NodeBuilder versionStorage, 
String versionableUuid) {
+        NodeBuilder history = versionStorage;
+        for (String segment : 
getRelativeVersionHistoryPathSegments(versionableUuid)) {
+            history = history.getChildNode(segment);
+        }
+        return history.getChildNode(versionableUuid);
+    }
+
+    private static List<String> getRelativeVersionHistoryPathSegments(String 
versionableUuid) {
+        final List<String> segments = new ArrayList<String>();
+        for (int i = 0; i < 3; i++) {
+            segments.add(versionableUuid.substring(i * 2, i * 2 + 2));
+        }
+        return segments;
+    }
+
+    public static NodeState getVersionStorage(NodeState root) {
+        return root.getChildNode(JCR_SYSTEM).getChildNode(JCR_VERSIONSTORAGE);
+    }
+
+    public static NodeBuilder getVersionStorage(NodeBuilder root) {
+        return root.getChildNode(JCR_SYSTEM).getChildNode(JCR_VERSIONSTORAGE);
+    }
+
+    public static NodeBuilder createVersionStorage(NodeBuilder root) {
+        NodeBuilder vs = root.child(JCR_SYSTEM).child(JCR_VERSIONSTORAGE);
+        if (!vs.hasProperty(JCR_PRIMARYTYPE)) {
+            vs.setProperty(JCR_PRIMARYTYPE, REP_VERSIONSTORAGE, Type.NAME);
+        }
+        return vs;
+    }
+
+}


Reply via email to