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 ba...@apache.org on 2015/07/27 17:25:56 UTC

svn commit: r1692897 - in /jackrabbit/oak/branches/1.2: ./ oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/ oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/ oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/s...

Author: baedke
Date: Mon Jul 27 15:25:55 2015
New Revision: 1692897

URL: http://svn.apache.org/r1692897
Log:
OAK-2619: Repeated upgrades

Merged c1691280.

Added:
    jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/
      - copied from r1691280, jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/
    jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepeatedRepositoryUpgradeTest.java   (contents, props changed)
      - copied, changed from r1691280, jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepeatedRepositoryUpgradeTest.java
    jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/nodestate/
      - copied from r1691280, jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/nodestate/
    jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/util/
      - copied from r1691280, jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/util/
Modified:
    jackrabbit/oak/branches/1.2/   (props changed)
    jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java
    jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/NodeStateCopier.java   (contents, props changed)
    jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/GroupEditor.java
    jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java
    jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/nodestate/NodeStateCopierTest.java   (contents, props changed)
    jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/util/NodeStateTestUtils.java   (contents, props changed)

Propchange: jackrabbit/oak/branches/1.2/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Jul 27 15:25:55 2015
@@ -1,3 +1,3 @@
 /jackrabbit/oak/branches/1.0:1665962
-/jackrabbit/oak/trunk:1672350,1672468,1672537,1672603,1672642,1672644,1672834-1672835,1673351,1673410,1673414-1673415,1673436,1673644,1673662-1673664,1673669,1673695,1673738,1673787,1673791,1674046,1674065,1674075,1674107,1674228,1674780,1674880,1675054-1675055,1675319,1675332,1675354,1675357,1675382,1675555,1675566,1675593,1676198,1676237,1676407,1676458,1676539,1676670,1676693,1676703,1676725,1677579,1677581,1677609,1677611,1677774,1677788,1677797,1677804,1677806,1677939,1677991,1678023,1678095-1678096,1678124,1678171,1678173,1678211,1678323,1678758,1678938,1678954,1679144,1679165,1679191,1679232,1679235,1679503,1679958,1679961,1680170,1680172,1680182,1680222,1680232,1680236,1680461,1680633,1680643,1680747,1680805-1680806,1680903,1681282,1681767,1681918,1682042,1682218,1682235,1682437,1682494,1682555,1682855,1682904,1683059,1683089,1683213,1683249,1683259,1683278,1683323,1683687,1683700,1684174-1684175,1684186,1684376,1684442,1684561,1684570,1684601,1684618,1684820,1684868,1685023
 ,1685075,1685370,1685552,1685589-1685590,1685840,1685964,1685977,1685989,1685999,1686023,1686032,1686097,1686162,1686229,1686234,1686253,1686414,1686780,1686854,1686857,1686971,1687053-1687055,1687175,1687196,1687198,1687220,1687239-1687240,1687301,1687441,1687553,1688089-1688090,1688172,1688179,1688349,1688421,1688436,1688453,1688616,1688622,1688634,1688636,1688817,1689003-1689004,1689008,1689577,1689581,1689623,1689810,1689828,1689831,1689833,1689903,1690017,1690043,1690047,1690057,1690247,1690249,1690634-1690637,1690650,1690669,1690674,1690885,1690941,1691139,1691151,1691159,1691167,1691183,1691188,1691210,1691307,1691331-1691333,1691345,1691384-1691385,1691401,1691509,1692133-1692134,1692274,1692363,1692478
+/jackrabbit/oak/trunk:1672350,1672468,1672537,1672603,1672642,1672644,1672834-1672835,1673351,1673410,1673414-1673415,1673436,1673644,1673662-1673664,1673669,1673695,1673738,1673787,1673791,1674046,1674065,1674075,1674107,1674228,1674780,1674880,1675054-1675055,1675319,1675332,1675354,1675357,1675382,1675555,1675566,1675593,1676198,1676237,1676407,1676458,1676539,1676670,1676693,1676703,1676725,1677579,1677581,1677609,1677611,1677774,1677788,1677797,1677804,1677806,1677939,1677991,1678023,1678095-1678096,1678124,1678171,1678173,1678211,1678323,1678758,1678938,1678954,1679144,1679165,1679191,1679232,1679235,1679503,1679958,1679961,1680170,1680172,1680182,1680222,1680232,1680236,1680461,1680633,1680643,1680747,1680805-1680806,1680903,1681282,1681767,1681918,1682042,1682218,1682235,1682437,1682494,1682555,1682855,1682904,1683059,1683089,1683213,1683249,1683259,1683278,1683323,1683687,1683700,1684174-1684175,1684186,1684376,1684442,1684561,1684570,1684601,1684618,1684820,1684868,1685023
 ,1685075,1685370,1685552,1685589-1685590,1685840,1685964,1685977,1685989,1685999,1686023,1686032,1686097,1686162,1686229,1686234,1686253,1686414,1686780,1686854,1686857,1686971,1687053-1687055,1687175,1687196,1687198,1687220,1687239-1687240,1687301,1687441,1687553,1688089-1688090,1688172,1688179,1688349,1688421,1688436,1688453,1688616,1688622,1688634,1688636,1688817,1689003-1689004,1689008,1689577,1689581,1689623,1689810,1689828,1689831,1689833,1689903,1690017,1690043,1690047,1690057,1690247,1690249,1690634-1690637,1690650,1690669,1690674,1690885,1690941,1691139,1691151,1691159,1691167,1691183,1691188,1691210,1691280,1691307,1691331-1691333,1691345,1691384-1691385,1691401,1691509,1692133-1692134,1692274,1692363,1692478
 /jackrabbit/trunk:1345480

Modified: jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java?rev=1692897&r1=1692896&r2=1692897&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java (original)
+++ jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java Mon Jul 27 15:25:55 2015
@@ -17,11 +17,11 @@
 package org.apache.jackrabbit.oak.upgrade;
 
 import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.ImmutableSet.of;
 import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Lists.newArrayListWithCapacity;
 import static com.google.common.collect.Maps.newHashMap;
 import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
-import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONSTORAGE;
 import static org.apache.jackrabbit.core.RepositoryImpl.ACTIVITIES_NODE_ID;
 import static org.apache.jackrabbit.core.RepositoryImpl.ROOT_NODE_ID;
 import static org.apache.jackrabbit.core.RepositoryImpl.VERSION_STORAGE_NODE_ID;
@@ -33,6 +33,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -73,6 +74,7 @@ import org.apache.jackrabbit.oak.api.Com
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.index.CompositeIndexEditorProvider;
 import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider;
@@ -85,7 +87,6 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.nodetype.TypeEditorProvider;
 import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent;
 import org.apache.jackrabbit.oak.plugins.nodetype.write.ReadWriteNodeTypeManager;
-import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeBuilder;
 import org.apache.jackrabbit.oak.plugins.value.ValueFactoryImpl;
 import org.apache.jackrabbit.oak.security.SecurityProviderImpl;
 import org.apache.jackrabbit.oak.spi.commit.CommitHook;
@@ -107,6 +108,7 @@ import org.apache.jackrabbit.oak.spi.sta
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.upgrade.nodestate.NodeStateCopier;
 import org.apache.jackrabbit.oak.upgrade.security.GroupEditorProvider;
 import org.apache.jackrabbit.oak.upgrade.security.RestrictionEditorProvider;
 import org.apache.jackrabbit.spi.Name;
@@ -506,6 +508,12 @@ public class RepositoryUpgrade {
         logger.debug("Registering custom non-aggregated privileges");
         for (Privilege privilege : registry.getRegisteredPrivileges()) {
             String privilegeName = privilege.getName();
+
+            if (hasPrivilege(pMgr, privilegeName)) {
+                logger.debug("Privilege {} already exists", privilegeName);
+                continue;
+            }
+
             if (PrivilegeBits.BUILT_IN.containsKey(privilegeName) || JCR_ALL.equals(privilegeName)) {
                 // Ignore built in privileges as those have been installed by the PrivilegesInitializer already
                 logger.debug("Built-in privilege -> ignore.");
@@ -557,6 +565,16 @@ public class RepositoryUpgrade {
         }
     }
 
+    private boolean hasPrivilege(PrivilegeManager pMgr, String privilegeName) throws RepositoryException {
+        final Privilege[] registeredPrivileges = pMgr.getRegisteredPrivileges();
+        for (Privilege registeredPrivilege : registeredPrivileges) {
+            if (registeredPrivilege.getName().equals(privilegeName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private static boolean allAggregatesRegistered(PrivilegeManager privilegeManager, List<String> aggrNames) {
         for (String name : aggrNames) {
             try {
@@ -694,16 +712,18 @@ public class RepositoryUpgrade {
         NodeBuilder system = builder.child(JCR_SYSTEM);
 
         logger.info("Copying version histories");
-        copyState(system, JCR_VERSIONSTORAGE, new JackrabbitNodeState(
+        copyState(system, "/jcr:system/jcr:versionStorage", new JackrabbitNodeState(
                 pm, root, uriToPrefix, VERSION_STORAGE_NODE_ID,
                 "/jcr:system/jcr:versionStorage",
-                workspaceName, versionablePaths, copyBinariesByReference, skipOnError));
+                workspaceName, versionablePaths, copyBinariesByReference, skipOnError),
+                true);
 
         logger.info("Copying activities");
-        copyState(system, "jcr:activities", new JackrabbitNodeState(
+        copyState(system, "/jcr:system/jcr:activities", new JackrabbitNodeState(
                 pm, root, uriToPrefix, ACTIVITIES_NODE_ID,
                 "/jcr:system/jcr:activities",
-                workspaceName, versionablePaths, copyBinariesByReference, skipOnError));
+                workspaceName, versionablePaths, copyBinariesByReference, skipOnError),
+                true);
     }
 
     private String copyWorkspace(
@@ -725,40 +745,30 @@ public class RepositoryUpgrade {
         for (ChildNodeEntry child : state.getChildNodeEntries()) {
             String childName = child.getName();
             if (!JCR_SYSTEM.equals(childName)) {
-                logger.info("Copying subtree /{}", childName);
-                copyState(builder, childName, child.getNodeState());
+                final String path = PathUtils.concat("/", childName);
+                logger.info("Copying subtree {}", path);
+                copyState(builder, path, child.getNodeState(), false);
             }
         }
 
         return workspaceName;
     }
 
-    private void copyState(NodeBuilder parent, String name, NodeState state) {
-        if (parent instanceof SegmentNodeBuilder) {
-            parent.setChildNode(name, state);
-        } else {
-            setChildNode(parent, name, state);
-        }
-    }
-
-    /**
-     * NodeState are copied by value by recursing down the complete tree
-     * This is a temporary approach for OAK-1760 for 1.0 branch.
-     */
-    private void setChildNode(NodeBuilder parent, String name, NodeState state) {
+    private void copyState(NodeBuilder targetParent, String path, NodeState source, boolean merge) {
+        final String name = PathUtils.getName(path);
         // OAK-1589: maximum supported length of name for DocumentNodeStore
         // is 150 bytes. Skip the sub tree if the the name is too long
         if (name.length() > 37 && name.getBytes(Charsets.UTF_8).length > 150) {
-            logger.warn("Node name too long. Skipping {}", state);
+            logger.warn("Node name too long. Skipping {}", source);
             return;
         }
-        NodeBuilder builder = parent.setChildNode(name);
-        for (PropertyState property : state.getProperties()) {
-            builder.setProperty(property);
-        }
-        for (ChildNodeEntry child : state.getChildNodeEntries()) {
-            setChildNode(builder, child.getName(), child.getNodeState());
-        }
+        NodeBuilder target = targetParent.child(name);
+        NodeStateCopier.copyNodeState(
+                source,
+                target,
+                path,
+                merge ? of(path) : Collections.<String>emptySet()
+        );
     }
 
     private static class LoggingCompositeHook implements CommitHook {

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=1692897&r1=1691280&r2=1692897&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 Mon Jul 27 15:25:55 2015
@@ -1,173 +1,173 @@
-/*
- * 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.CommitFailedException;
-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.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.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;
-
-/**
- * The NodeStateCopier and NodeStateCopier.Builder classes allow
- * recursively copying a NodeState to a NodeBuilder.
- * <br>
- * The copy algorithm is optimized for copying nodes between two
- * different NodeStore instances, i.e. where comparing NodeStates
- * is imprecise and/or expensive.
- * <br>
- * The algorithm does a post-order traversal. I.e. it copies
- * changed leaf-nodes first.
- * <br>
- * 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.
- */
-public class NodeStateCopier {
-
-    private static final Logger LOG = LoggerFactory.getLogger(NodeStateCopier.class);
-
-
-    private NodeStateCopier() {
-        // no instances
-    }
-
-    /**
-     * Shorthand method to copy one NodeStore to another. The changes in the
-     * target NodeStore are automatically persisted.
-     *
-     * @param source NodeStore to copy from.
-     * @param target NodeStore to copy to.
-     * @throws CommitFailedException
-     */
-    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;
-    }
-
-    /**
-     * Copies all changed properties from the source NodeState to the target
-     * NodeBuilder instance.
-     *
-     * @param source The NodeState to copy from.
-     * @param target The NodeBuilder to copy to.
-     * @return Whether changes were made or not.
-     */
-    public static boolean copyProperties(NodeState source, NodeBuilder target) {
-        boolean hasChanges = false;
-
-        // remove removed properties
-        for (final PropertyState property : target.getProperties()) {
-            final String name = property.getName();
-            if (!source.hasProperty(name)) {
-                target.removeProperty(name);
-                hasChanges = true;
-            }
-        }
-
-        // add new properties and change changed properties
-        for (PropertyState property : source.getProperties()) {
-            if (!property.equals(target.getProperty(property.getName()))) {
-                target.setProperty(property);
-                hasChanges = true;
-            }
-        }
-        return hasChanges;
-    }
-
-    /**
-     * Recursively copies the source NodeState to the target NodeBuilder.
-     * <br>
-     * Nodes that exist in the {@code target} but not in the {@code source}
-     * are removed, unless they are descendants of one of the {@code mergePaths}.
-     * This is determined by checking if the {@code currentPath} is a descendant
-     * of any of the {@code mergePaths}.
-     * <br>
-     * <b>Note:</b> changes are not persisted.
-     *
-     * @param source NodeState to copy from
-     * @param target NodeBuilder to copy to
-     * @param currentPath The path of both the source and target arguments.
-     * @param mergePaths A Set of paths under which existing nodes should be
-     *                   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) {
-
-
-        boolean hasChanges = false;
-
-        // delete deleted children
-        for (final String childName : target.getChildNodeNames()) {
-            if (!source.hasChildNode(childName) && !isMerge(PathUtils.concat(currentPath, childName), mergePaths)) {
-                target.setChildNode(childName, EmptyNodeState.MISSING_NODE);
-                hasChanges = true;
-            }
-        }
-
-        for (ChildNodeEntry child : source.getChildNodeEntries()) {
-            final String childName = child.getName();
-            final NodeState childSource = child.getNodeState();
-            if (!target.hasChildNode(childName)) {
-                // add new children
-                target.setChildNode(childName, childSource);
-                hasChanges = true;
-            } else {
-                // recurse into existing children
-                final NodeBuilder childTarget = target.getChildNode(childName);
-                final String childPath = PathUtils.concat(currentPath, childName);
-                hasChanges = copyNodeState(childSource, childTarget, childPath, mergePaths) || hasChanges;
-            }
-        }
-
-        hasChanges = copyProperties(source, target) || hasChanges;
-
-        if (hasChanges) {
-            LOG.trace("Node {} has changes", target);
-        }
-
-        return hasChanges;
-    }
-
-    private static boolean isMerge(String path, Set<String> mergePaths) {
-        for (String mergePath : mergePaths) {
-            if (PathUtils.isAncestor(mergePath, path) || mergePath.equals(path)) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
+/*
+ * 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.CommitFailedException;
+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.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.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;
+
+/**
+ * The NodeStateCopier and NodeStateCopier.Builder classes allow
+ * recursively copying a NodeState to a NodeBuilder.
+ * <br>
+ * The copy algorithm is optimized for copying nodes between two
+ * different NodeStore instances, i.e. where comparing NodeStates
+ * is imprecise and/or expensive.
+ * <br>
+ * The algorithm does a post-order traversal. I.e. it copies
+ * changed leaf-nodes first.
+ * <br>
+ * 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.
+ */
+public class NodeStateCopier {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NodeStateCopier.class);
+
+
+    private NodeStateCopier() {
+        // no instances
+    }
+
+    /**
+     * Shorthand method to copy one NodeStore to another. The changes in the
+     * target NodeStore are automatically persisted.
+     *
+     * @param source NodeStore to copy from.
+     * @param target NodeStore to copy to.
+     * @throws CommitFailedException
+     */
+    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;
+    }
+
+    /**
+     * Copies all changed properties from the source NodeState to the target
+     * NodeBuilder instance.
+     *
+     * @param source The NodeState to copy from.
+     * @param target The NodeBuilder to copy to.
+     * @return Whether changes were made or not.
+     */
+    public static boolean copyProperties(NodeState source, NodeBuilder target) {
+        boolean hasChanges = false;
+
+        // remove removed properties
+        for (final PropertyState property : target.getProperties()) {
+            final String name = property.getName();
+            if (!source.hasProperty(name)) {
+                target.removeProperty(name);
+                hasChanges = true;
+            }
+        }
+
+        // add new properties and change changed properties
+        for (PropertyState property : source.getProperties()) {
+            if (!property.equals(target.getProperty(property.getName()))) {
+                target.setProperty(property);
+                hasChanges = true;
+            }
+        }
+        return hasChanges;
+    }
+
+    /**
+     * Recursively copies the source NodeState to the target NodeBuilder.
+     * <br>
+     * Nodes that exist in the {@code target} but not in the {@code source}
+     * are removed, unless they are descendants of one of the {@code mergePaths}.
+     * This is determined by checking if the {@code currentPath} is a descendant
+     * of any of the {@code mergePaths}.
+     * <br>
+     * <b>Note:</b> changes are not persisted.
+     *
+     * @param source NodeState to copy from
+     * @param target NodeBuilder to copy to
+     * @param currentPath The path of both the source and target arguments.
+     * @param mergePaths A Set of paths under which existing nodes should be
+     *                   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) {
+
+
+        boolean hasChanges = false;
+
+        // delete deleted children
+        for (final String childName : target.getChildNodeNames()) {
+            if (!source.hasChildNode(childName) && !isMerge(PathUtils.concat(currentPath, childName), mergePaths)) {
+                target.setChildNode(childName, EmptyNodeState.MISSING_NODE);
+                hasChanges = true;
+            }
+        }
+
+        for (ChildNodeEntry child : source.getChildNodeEntries()) {
+            final String childName = child.getName();
+            final NodeState childSource = child.getNodeState();
+            if (!target.hasChildNode(childName)) {
+                // add new children
+                target.setChildNode(childName, childSource);
+                hasChanges = true;
+            } else {
+                // recurse into existing children
+                final NodeBuilder childTarget = target.getChildNode(childName);
+                final String childPath = PathUtils.concat(currentPath, childName);
+                hasChanges = copyNodeState(childSource, childTarget, childPath, mergePaths) || hasChanges;
+            }
+        }
+
+        hasChanges = copyProperties(source, target) || hasChanges;
+
+        if (hasChanges) {
+            LOG.trace("Node {} has changes", target);
+        }
+
+        return hasChanges;
+    }
+
+    private static boolean isMerge(String path, Set<String> mergePaths) {
+        for (String mergePath : mergePaths) {
+            if (PathUtils.isAncestor(mergePath, path) || mergePath.equals(path)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

Propchange: jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/NodeStateCopier.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/GroupEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/GroupEditor.java?rev=1692897&r1=1692896&r2=1692897&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/GroupEditor.java (original)
+++ jackrabbit/oak/branches/1.2/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/GroupEditor.java Mon Jul 27 15:25:55 2015
@@ -108,7 +108,7 @@ class GroupEditor extends DefaultEditor
 
     @Override
     public Editor childNodeChanged(String name, NodeState before, NodeState after) {
-        throw new IllegalStateException("changed node during upgrade copy not expected: " + state.path + "/" + name);
+        return null;
     }
 
     @Override

Modified: jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java?rev=1692897&r1=1692896&r2=1692897&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java (original)
+++ jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java Mon Jul 27 15:25:55 2015
@@ -19,12 +19,14 @@
 package org.apache.jackrabbit.oak.upgrade;
 
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
 import javax.jcr.Credentials;
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
+import javax.jcr.Session;
 import javax.jcr.SimpleCredentials;
 
 import org.apache.commons.io.FileUtils;
@@ -40,63 +42,118 @@ import org.apache.jackrabbit.oak.stats.C
 import org.junit.Before;
 import org.junit.BeforeClass;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 public abstract class AbstractRepositoryUpgradeTest {
 
     protected static final Credentials CREDENTIALS = new SimpleCredentials("admin", "admin".toCharArray());
 
-    private static Repository targetRepository;
+    private static NodeStore targetNodeStore;
+
+    private static File testDirectory;
 
     @BeforeClass
-    public static void init() {
+    public static void init() throws InterruptedException {
         // ensure that we create a new repository for the next test
-        targetRepository = null;
+        targetNodeStore = null;
+        testDirectory = createTestDirectory();
+    }
+
+    protected static File createTestDirectory() throws InterruptedException {
+        final File dir = new File("target", "upgrade-" + Clock.SIMPLE.getTimeIncreasing());
+        FileUtils.deleteQuietly(dir);
+        return dir;
+    }
+
+    protected NodeStore createTargetNodeStore() {
+        return new SegmentNodeStore();
     }
 
     @Before
     public synchronized void upgradeRepository() throws Exception {
-        if (targetRepository == null) {
-            File directory = new File(
-                    "target", "upgrade-" + Clock.SIMPLE.getTimeIncreasing());
-            FileUtils.deleteQuietly(directory);
-
+        if (targetNodeStore == null) {
+            File directory = getTestDirectory();
             File source = new File(directory, "source");
             source.mkdirs();
-
-            InputStream repoConfig = getRepositoryConfig();
-            RepositoryConfig config;
-            if (repoConfig == null) {
-                config = RepositoryConfig.install(source);
-            } else {
-                OutputStream out = FileUtils.openOutputStream(new File(source, "repository.xml"));
-                IOUtils.copy(repoConfig, out);
-                out.close();
-                repoConfig.close();
-                config = RepositoryConfig.create(source);
-            }
-            RepositoryImpl repository = RepositoryImpl.create(config);
+            RepositoryImpl repository = createSourceRepository(source);
             try {
                 createSourceContent(repository);
             } finally {
                 repository.shutdown();
             }
-            NodeStore target = new SegmentNodeStore();
-            RepositoryUpgrade.copy(source, target);
-            targetRepository = new Jcr(new Oak(target)).createRepository();
+            final NodeStore target = getTargetNodeStore();
+            doUpgradeRepository(source, target);
+            targetNodeStore = target;
         }
     }
 
-    public InputStream getRepositoryConfig() {
+    protected synchronized NodeStore getTargetNodeStore() {
+        if (targetNodeStore == null) {
+            targetNodeStore = createTargetNodeStore();
+        }
+        return targetNodeStore;
+    }
+
+    protected static File getTestDirectory() {
+        return testDirectory;
+    }
+
+    protected RepositoryImpl createSourceRepository(File repositoryHome) throws IOException, RepositoryException {
+        InputStream repoConfig = getRepositoryConfig();
+        RepositoryConfig config;
+        if (repoConfig == null) {
+            config = RepositoryConfig.install(repositoryHome);
+        } else {
+            OutputStream out = FileUtils.openOutputStream(new File(repositoryHome, "repository.xml"));
+            IOUtils.copy(repoConfig, out);
+            out.close();
+            repoConfig.close();
+            config = RepositoryConfig.create(repositoryHome);
+        }
+        return RepositoryImpl.create(config);
+    }
+
+
+    protected void doUpgradeRepository(File source, NodeStore target)throws RepositoryException{
+        RepositoryUpgrade.copy(source, target);
+    }
+
+    public InputStream getRepositoryConfig(){
         return null;
     }
 
-    public Repository getTargetRepository() {
-        return targetRepository;
+    public Repository getTargetRepository(){
+        return new Jcr(new Oak(targetNodeStore)).createRepository();
     }
 
-    public JackrabbitSession createAdminSession() throws RepositoryException {
-        return (JackrabbitSession) getTargetRepository().login(CREDENTIALS);
+    public JackrabbitSession createAdminSession()throws RepositoryException{
+        return(JackrabbitSession)getTargetRepository().login(CREDENTIALS);
     }
 
     protected abstract void createSourceContent(Repository repository) throws Exception;
 
+    protected void assertExisting(final String... paths) throws RepositoryException {
+        final Session session = createAdminSession();
+        try {
+            for (final String path : paths) {
+                final String relPath = path.substring(1);
+                assertTrue("node " + path + " should exist", session.getRootNode().hasNode(relPath));
+            }
+        } finally {
+            session.logout();
+        }
+    }
+
+    protected void assertMissing(final String... paths) throws RepositoryException {
+        final Session session = createAdminSession();
+        try {
+            for (final String path : paths) {
+                final String relPath = path.substring(1);
+                assertFalse("node " + path + " should not exist", session.getRootNode().hasNode(relPath));
+            }
+        } finally {
+            session.logout();
+        }
+    }
 }

Copied: jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepeatedRepositoryUpgradeTest.java (from r1691280, jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepeatedRepositoryUpgradeTest.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepeatedRepositoryUpgradeTest.java?p2=jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepeatedRepositoryUpgradeTest.java&p1=jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepeatedRepositoryUpgradeTest.java&r1=1691280&r2=1692897&rev=1692897&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepeatedRepositoryUpgradeTest.java (original)
+++ jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepeatedRepositoryUpgradeTest.java Mon Jul 27 15:25:55 2015
@@ -1,221 +1,221 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.jackrabbit.oak.upgrade;
-
-import org.apache.jackrabbit.api.JackrabbitWorkspace;
-import org.apache.jackrabbit.api.security.authorization.PrivilegeManager;
-import org.apache.jackrabbit.commons.JcrUtils;
-import org.apache.jackrabbit.core.RepositoryContext;
-import org.apache.jackrabbit.core.RepositoryImpl;
-import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
-import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
-import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer;
-import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
-import org.apache.jackrabbit.oak.spi.state.NodeStore;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import javax.annotation.Nonnull;
-import javax.jcr.NamespaceRegistry;
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Test case to simulate an incremental upgrade, where a source repository is
- * copied to target initially. Then some modifications are made in the source
- * repository and these are (incrementally) copied to the target repository.
- * <br>
- * The expectation is that in the end the state in the target repository is
- * identical to the state in the source repository, with the exception of any
- * initial content that the upgrade tool created.
- */
-public class RepeatedRepositoryUpgradeTest extends AbstractRepositoryUpgradeTest {
-
-    private static boolean upgradeComplete;
-    private static FileStore fileStore;
-
-    @Override
-    protected NodeStore createTargetNodeStore() {
-        return new SegmentNodeStore(fileStore);
-    }
-
-    @BeforeClass
-    public static void initialize() {
-        final File dir = new File(getTestDirectory(), "segments");
-        dir.mkdirs();
-        try {
-            fileStore = FileStore.newFileStore(dir).withMaxFileSize(128).create();
-            upgradeComplete = false;
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @AfterClass
-    public static void cleanup() {
-        fileStore.close();
-        fileStore = null;
-    }
-
-    @Before
-    public synchronized void upgradeRepository() throws Exception {
-        if (!upgradeComplete) {
-            final File sourceDir = new File(getTestDirectory(), "jackrabbit2");
-
-            sourceDir.mkdirs();
-
-            RepositoryImpl source = createSourceRepository(sourceDir);
-
-            try {
-                createSourceContent(source);
-            } finally {
-                source.shutdown();
-            }
-
-            final NodeStore target = getTargetNodeStore();
-            doUpgradeRepository(sourceDir, target);
-            fileStore.flush();
-
-            // re-create source repo
-            source = createSourceRepository(sourceDir);
-            try {
-                modifySourceContent(source);
-            } finally {
-                source.shutdown();
-            }
-
-            doUpgradeRepository(sourceDir, target);
-            fileStore.flush();
-
-            upgradeComplete = true;
-        }
-    }
-
-    @Override
-    protected void doUpgradeRepository(File source, NodeStore target) throws RepositoryException {
-        final RepositoryConfig config = RepositoryConfig.create(source);
-        final RepositoryContext context = RepositoryContext.create(config);
-        try {
-            final RepositoryUpgrade repositoryUpgrade = new RepositoryUpgrade(context, target);
-            repositoryUpgrade.copy(new RepositoryInitializer() {
-                @Override
-                public void initialize(@Nonnull NodeBuilder builder) {
-                    builder.child("foo").child("bar");
-                }
-            });
-        } finally {
-            context.getRepository().shutdown();
-        }
-    }
-
-    @Override
-    protected void createSourceContent(Repository repository) throws RepositoryException {
-        Session session = null;
-        try {
-            session = repository.login(CREDENTIALS);
-
-            registerCustomPrivileges(session);
-
-            JcrUtils.getOrCreateByPath("/content/child1/grandchild1", "nt:unstructured", session);
-            JcrUtils.getOrCreateByPath("/content/child1/grandchild2", "nt:unstructured", session);
-            JcrUtils.getOrCreateByPath("/content/child1/grandchild3", "nt:unstructured", session);
-            JcrUtils.getOrCreateByPath("/content/child2/grandchild1", "nt:unstructured", session);
-            JcrUtils.getOrCreateByPath("/content/child2/grandchild2", "nt:unstructured", session);
-
-            session.save();
-        } finally {
-            if (session != null && session.isLive()) {
-                session.logout();
-            }
-        }
-    }
-
-    private void modifySourceContent(Repository repository) throws RepositoryException {
-        Session session = null;
-        try {
-            session = repository.login(CREDENTIALS);
-
-            JcrUtils.getOrCreateByPath("/content/child2/grandchild3", "nt:unstructured", session);
-            JcrUtils.getOrCreateByPath("/content/child3", "nt:unstructured", session);
-
-            final Node child1 = JcrUtils.getOrCreateByPath("/content/child1", "nt:unstructured", session);
-            child1.remove();
-
-            session.save();
-        } finally {
-            if (session != null && session.isLive()) {
-                session.logout();
-            }
-        }
-    }
-
-    private void registerCustomPrivileges(Session session) throws RepositoryException {
-        final JackrabbitWorkspace workspace = (JackrabbitWorkspace) session.getWorkspace();
-
-        final NamespaceRegistry registry = workspace.getNamespaceRegistry();
-        registry.registerNamespace("test", "http://www.example.org/");
-
-        final PrivilegeManager privilegeManager = workspace.getPrivilegeManager();
-        privilegeManager.registerPrivilege("test:privilege", false, null);
-        privilegeManager.registerPrivilege(
-                "test:aggregate", false, new String[]{"jcr:read", "test:privilege"});
-    }
-
-    @Test
-    public void shouldReflectSourceAfterModifications() throws Exception {
-
-        assertExisting(
-                "/",
-                "/content",
-                "/content/child2",
-                "/content/child2/grandchild1",
-                "/content/child2/grandchild2",
-                "/content/child2/grandchild3",
-                "/content/child3"
-        );
-
-        assertMissing(
-                "/content/child1"
-        );
-    }
-
-    @Test
-    public void shouldContainCustomInitializerContent() throws Exception {
-        assertExisting(
-                "/foo",
-                "/foo/bar"
-        );
-    }
-
-    @Test
-    public void shouldContainUpgradeInitializedContent() throws Exception {
-        assertExisting(
-                "/rep:security",
-                "/oak:index"
-        );
-    }
-
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.upgrade;
+
+import org.apache.jackrabbit.api.JackrabbitWorkspace;
+import org.apache.jackrabbit.api.security.authorization.PrivilegeManager;
+import org.apache.jackrabbit.commons.JcrUtils;
+import org.apache.jackrabbit.core.RepositoryContext;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
+import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import javax.annotation.Nonnull;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Test case to simulate an incremental upgrade, where a source repository is
+ * copied to target initially. Then some modifications are made in the source
+ * repository and these are (incrementally) copied to the target repository.
+ * <br>
+ * The expectation is that in the end the state in the target repository is
+ * identical to the state in the source repository, with the exception of any
+ * initial content that the upgrade tool created.
+ */
+public class RepeatedRepositoryUpgradeTest extends AbstractRepositoryUpgradeTest {
+
+    private static boolean upgradeComplete;
+    private static FileStore fileStore;
+
+    @Override
+    protected NodeStore createTargetNodeStore() {
+        return new SegmentNodeStore(fileStore);
+    }
+
+    @BeforeClass
+    public static void initialize() {
+        final File dir = new File(getTestDirectory(), "segments");
+        dir.mkdirs();
+        try {
+            fileStore = FileStore.newFileStore(dir).withMaxFileSize(128).create();
+            upgradeComplete = false;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        fileStore.close();
+        fileStore = null;
+    }
+
+    @Before
+    public synchronized void upgradeRepository() throws Exception {
+        if (!upgradeComplete) {
+            final File sourceDir = new File(getTestDirectory(), "jackrabbit2");
+
+            sourceDir.mkdirs();
+
+            RepositoryImpl source = createSourceRepository(sourceDir);
+
+            try {
+                createSourceContent(source);
+            } finally {
+                source.shutdown();
+            }
+
+            final NodeStore target = getTargetNodeStore();
+            doUpgradeRepository(sourceDir, target);
+            fileStore.flush();
+
+            // re-create source repo
+            source = createSourceRepository(sourceDir);
+            try {
+                modifySourceContent(source);
+            } finally {
+                source.shutdown();
+            }
+
+            doUpgradeRepository(sourceDir, target);
+            fileStore.flush();
+
+            upgradeComplete = true;
+        }
+    }
+
+    @Override
+    protected void doUpgradeRepository(File source, NodeStore target) throws RepositoryException {
+        final RepositoryConfig config = RepositoryConfig.create(source);
+        final RepositoryContext context = RepositoryContext.create(config);
+        try {
+            final RepositoryUpgrade repositoryUpgrade = new RepositoryUpgrade(context, target);
+            repositoryUpgrade.copy(new RepositoryInitializer() {
+                @Override
+                public void initialize(@Nonnull NodeBuilder builder) {
+                    builder.child("foo").child("bar");
+                }
+            });
+        } finally {
+            context.getRepository().shutdown();
+        }
+    }
+
+    @Override
+    protected void createSourceContent(Repository repository) throws RepositoryException {
+        Session session = null;
+        try {
+            session = repository.login(CREDENTIALS);
+
+            registerCustomPrivileges(session);
+
+            JcrUtils.getOrCreateByPath("/content/child1/grandchild1", "nt:unstructured", session);
+            JcrUtils.getOrCreateByPath("/content/child1/grandchild2", "nt:unstructured", session);
+            JcrUtils.getOrCreateByPath("/content/child1/grandchild3", "nt:unstructured", session);
+            JcrUtils.getOrCreateByPath("/content/child2/grandchild1", "nt:unstructured", session);
+            JcrUtils.getOrCreateByPath("/content/child2/grandchild2", "nt:unstructured", session);
+
+            session.save();
+        } finally {
+            if (session != null && session.isLive()) {
+                session.logout();
+            }
+        }
+    }
+
+    private void modifySourceContent(Repository repository) throws RepositoryException {
+        Session session = null;
+        try {
+            session = repository.login(CREDENTIALS);
+
+            JcrUtils.getOrCreateByPath("/content/child2/grandchild3", "nt:unstructured", session);
+            JcrUtils.getOrCreateByPath("/content/child3", "nt:unstructured", session);
+
+            final Node child1 = JcrUtils.getOrCreateByPath("/content/child1", "nt:unstructured", session);
+            child1.remove();
+
+            session.save();
+        } finally {
+            if (session != null && session.isLive()) {
+                session.logout();
+            }
+        }
+    }
+
+    private void registerCustomPrivileges(Session session) throws RepositoryException {
+        final JackrabbitWorkspace workspace = (JackrabbitWorkspace) session.getWorkspace();
+
+        final NamespaceRegistry registry = workspace.getNamespaceRegistry();
+        registry.registerNamespace("test", "http://www.example.org/");
+
+        final PrivilegeManager privilegeManager = workspace.getPrivilegeManager();
+        privilegeManager.registerPrivilege("test:privilege", false, null);
+        privilegeManager.registerPrivilege(
+                "test:aggregate", false, new String[]{"jcr:read", "test:privilege"});
+    }
+
+    @Test
+    public void shouldReflectSourceAfterModifications() throws Exception {
+
+        assertExisting(
+                "/",
+                "/content",
+                "/content/child2",
+                "/content/child2/grandchild1",
+                "/content/child2/grandchild2",
+                "/content/child2/grandchild3",
+                "/content/child3"
+        );
+
+        assertMissing(
+                "/content/child1"
+        );
+    }
+
+    @Test
+    public void shouldContainCustomInitializerContent() throws Exception {
+        assertExisting(
+                "/foo",
+                "/foo/bar"
+        );
+    }
+
+    @Test
+    public void shouldContainUpgradeInitializedContent() throws Exception {
+        assertExisting(
+                "/rep:security",
+                "/oak:index"
+        );
+    }
+
 }
\ No newline at end of file

Propchange: jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepeatedRepositoryUpgradeTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/nodestate/NodeStateCopierTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/nodestate/NodeStateCopierTest.java?rev=1692897&r1=1691280&r2=1692897&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/nodestate/NodeStateCopierTest.java (original)
+++ jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/nodestate/NodeStateCopierTest.java Mon Jul 27 15:25:55 2015
@@ -1,183 +1,183 @@
-/*
- * 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.CommitFailedException;
-import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
-import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.apache.jackrabbit.oak.spi.state.NodeStore;
-import org.junit.Test;
-
-import static com.google.common.collect.ImmutableSet.of;
-import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
-import static org.apache.jackrabbit.oak.upgrade.util.NodeStateTestUtils.commit;
-import static org.apache.jackrabbit.oak.upgrade.util.NodeStateTestUtils.create;
-import static org.apache.jackrabbit.oak.upgrade.util.NodeStateTestUtils.createNodeStoreWithContent;
-import static org.apache.jackrabbit.oak.upgrade.util.NodeStateTestUtils.expectDifference;
-
-public class NodeStateCopierTest {
-
-    private final PropertyState primaryType =
-            createProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
-
-    @Test
-    public void shouldMergeIdenticalContent() throws CommitFailedException {
-        final NodeStore source = createPrefilledNodeStore();
-        final NodeStore target = createPrefilledNodeStore();
-
-        final NodeState before = target.getRoot();
-        NodeStateCopier.copyNodeStore(source, target);
-        final NodeState after = target.getRoot();
-
-        expectDifference().strict().verify(before, after);
-        expectDifference().strict().verify(source.getRoot(), after);
-    }
-
-    private NodeStore createPrefilledNodeStore() throws CommitFailedException {
-        final NodeStore store = createNodeStoreWithContent();
-        final NodeBuilder builder = store.getRoot().builder();
-        create(builder, "/excluded");
-        create(builder, "/a", primaryType, createProperty("name", "a"));
-        create(builder, "/a/b", primaryType, createProperty("name", "b"));
-        create(builder, "/a/b/excluded");
-        create(builder, "/a/b/c", primaryType, createProperty("name", "c"));
-        create(builder, "/a/b/c/d", primaryType);
-        create(builder, "/a/b/c/e", primaryType);
-        create(builder, "/a/b/c/f", primaryType);
-        commit(store, builder);
-        return store;
-    }
-
-    @Test
-    public void shouldRespectMergePaths() throws CommitFailedException {
-        final NodeStore source = createNodeStoreWithContent("/content/foo/en", "/content/bar/en");
-        final NodeStore target = createNodeStoreWithContent("/content/foo/de");
-
-        final NodeBuilder builder = target.getRoot().builder();
-        NodeStateCopier.copyNodeState(source.getRoot(), builder, "/", of("/content"));
-        commit(target, builder);
-        final NodeState after = target.getRoot();
-
-        expectDifference()
-                .strict()
-                .childNodeAdded("/content/foo/de")
-                .childNodeChanged("/content", "/content/foo")
-                .verify(source.getRoot(), after);
-    }
-
-
-    @Test
-    public void shouldDeleteExistingNodes() throws CommitFailedException {
-        final NodeStore source = createNodeStoreWithContent("/content/foo");
-        final NodeStore target = createNodeStoreWithContent("/content/bar");
-
-        final NodeState before = target.getRoot();
-        final NodeBuilder builder = before.builder();
-        NodeStateCopier.copyNodeState(source.getRoot(), builder, "/", ImmutableSet.<String>of());
-        commit(target, builder);
-        final NodeState after = target.getRoot();
-
-        expectDifference()
-                .strict()
-                .childNodeAdded("/content/foo")
-                .childNodeChanged("/content")
-                .childNodeDeleted("/content/bar")
-                .verify(before, after);
-    }
-
-    @Test
-    public void shouldDeleteExistingPropertyIfMissingInSource() throws CommitFailedException {
-        final NodeStore source = createNodeStoreWithContent("/a");
-        final NodeStore target = createNodeStoreWithContent();
-        NodeBuilder builder = target.getRoot().builder();
-        create(builder, "/a", primaryType);
-        commit(target, builder);
-
-        final NodeState before = target.getRoot();
-        builder = before.builder();
-        NodeStateCopier.copyNodeState(source.getRoot(), builder, "/", ImmutableSet.<String>of());
-        commit(target, builder);
-        final NodeState after = target.getRoot();
-
-        expectDifference()
-                .strict()
-                .propertyDeleted("/a/jcr:primaryType")
-                .childNodeChanged("/a")
-                .verify(before, after);
-    }
-
-    @Test
-    public void shouldNotDeleteExistingNodesIfMerged() throws CommitFailedException {
-        final NodeStore source = createNodeStoreWithContent("/content/foo");
-        final NodeStore target = createNodeStoreWithContent("/content/bar");
-
-        final NodeState before = target.getRoot();
-        final NodeBuilder builder = before.builder();
-        NodeStateCopier.copyNodeState(source.getRoot(), builder, "/", of("/content/bar"));
-        commit(target, builder);
-        final NodeState after = target.getRoot();
-
-        expectDifference()
-                .strict()
-                .childNodeAdded("/content/foo")
-                .childNodeChanged("/content")
-                .verify(before, after);
-    }
-
-    @Test
-    public void shouldNotDeleteExistingNodesIfDescendantsOfMerged() throws CommitFailedException {
-        final NodeStore source = createNodeStoreWithContent("/content/foo");
-        final NodeStore target = createNodeStoreWithContent("/content/bar");
-
-        final NodeState before = target.getRoot();
-        final NodeBuilder builder = before.builder();
-        NodeStateCopier.copyNodeState(source.getRoot(), builder, "/", of("/content"));
-        commit(target, builder);
-        final NodeState after = target.getRoot();
-
-        expectDifference()
-                .strict()
-                .childNodeAdded("/content/foo")
-                .childNodeChanged("/content")
-                .verify(before, after);
-    }
-
-    @Test
-    public void shouldIgnoreNonMatchingMergePaths() throws CommitFailedException {
-        final NodeStore source = createNodeStoreWithContent("/content/foo");
-        final NodeStore target = createNodeStoreWithContent("/content/bar");
-
-        final NodeState before = target.getRoot();
-        final NodeBuilder builder = before.builder();
-        NodeStateCopier.copyNodeState(source.getRoot(), builder, "/", of("/con"));
-        commit(target, builder);
-        final NodeState after = target.getRoot();
-
-        expectDifference()
-                .strict()
-                .childNodeAdded("/content/foo")
-                .childNodeChanged("/content")
-                .childNodeDeleted("/content/bar")
-                .verify(before, after);
-    }
-
-}
+/*
+ * 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.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.junit.Test;
+
+import static com.google.common.collect.ImmutableSet.of;
+import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
+import static org.apache.jackrabbit.oak.upgrade.util.NodeStateTestUtils.commit;
+import static org.apache.jackrabbit.oak.upgrade.util.NodeStateTestUtils.create;
+import static org.apache.jackrabbit.oak.upgrade.util.NodeStateTestUtils.createNodeStoreWithContent;
+import static org.apache.jackrabbit.oak.upgrade.util.NodeStateTestUtils.expectDifference;
+
+public class NodeStateCopierTest {
+
+    private final PropertyState primaryType =
+            createProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+    @Test
+    public void shouldMergeIdenticalContent() throws CommitFailedException {
+        final NodeStore source = createPrefilledNodeStore();
+        final NodeStore target = createPrefilledNodeStore();
+
+        final NodeState before = target.getRoot();
+        NodeStateCopier.copyNodeStore(source, target);
+        final NodeState after = target.getRoot();
+
+        expectDifference().strict().verify(before, after);
+        expectDifference().strict().verify(source.getRoot(), after);
+    }
+
+    private NodeStore createPrefilledNodeStore() throws CommitFailedException {
+        final NodeStore store = createNodeStoreWithContent();
+        final NodeBuilder builder = store.getRoot().builder();
+        create(builder, "/excluded");
+        create(builder, "/a", primaryType, createProperty("name", "a"));
+        create(builder, "/a/b", primaryType, createProperty("name", "b"));
+        create(builder, "/a/b/excluded");
+        create(builder, "/a/b/c", primaryType, createProperty("name", "c"));
+        create(builder, "/a/b/c/d", primaryType);
+        create(builder, "/a/b/c/e", primaryType);
+        create(builder, "/a/b/c/f", primaryType);
+        commit(store, builder);
+        return store;
+    }
+
+    @Test
+    public void shouldRespectMergePaths() throws CommitFailedException {
+        final NodeStore source = createNodeStoreWithContent("/content/foo/en", "/content/bar/en");
+        final NodeStore target = createNodeStoreWithContent("/content/foo/de");
+
+        final NodeBuilder builder = target.getRoot().builder();
+        NodeStateCopier.copyNodeState(source.getRoot(), builder, "/", of("/content"));
+        commit(target, builder);
+        final NodeState after = target.getRoot();
+
+        expectDifference()
+                .strict()
+                .childNodeAdded("/content/foo/de")
+                .childNodeChanged("/content", "/content/foo")
+                .verify(source.getRoot(), after);
+    }
+
+
+    @Test
+    public void shouldDeleteExistingNodes() throws CommitFailedException {
+        final NodeStore source = createNodeStoreWithContent("/content/foo");
+        final NodeStore target = createNodeStoreWithContent("/content/bar");
+
+        final NodeState before = target.getRoot();
+        final NodeBuilder builder = before.builder();
+        NodeStateCopier.copyNodeState(source.getRoot(), builder, "/", ImmutableSet.<String>of());
+        commit(target, builder);
+        final NodeState after = target.getRoot();
+
+        expectDifference()
+                .strict()
+                .childNodeAdded("/content/foo")
+                .childNodeChanged("/content")
+                .childNodeDeleted("/content/bar")
+                .verify(before, after);
+    }
+
+    @Test
+    public void shouldDeleteExistingPropertyIfMissingInSource() throws CommitFailedException {
+        final NodeStore source = createNodeStoreWithContent("/a");
+        final NodeStore target = createNodeStoreWithContent();
+        NodeBuilder builder = target.getRoot().builder();
+        create(builder, "/a", primaryType);
+        commit(target, builder);
+
+        final NodeState before = target.getRoot();
+        builder = before.builder();
+        NodeStateCopier.copyNodeState(source.getRoot(), builder, "/", ImmutableSet.<String>of());
+        commit(target, builder);
+        final NodeState after = target.getRoot();
+
+        expectDifference()
+                .strict()
+                .propertyDeleted("/a/jcr:primaryType")
+                .childNodeChanged("/a")
+                .verify(before, after);
+    }
+
+    @Test
+    public void shouldNotDeleteExistingNodesIfMerged() throws CommitFailedException {
+        final NodeStore source = createNodeStoreWithContent("/content/foo");
+        final NodeStore target = createNodeStoreWithContent("/content/bar");
+
+        final NodeState before = target.getRoot();
+        final NodeBuilder builder = before.builder();
+        NodeStateCopier.copyNodeState(source.getRoot(), builder, "/", of("/content/bar"));
+        commit(target, builder);
+        final NodeState after = target.getRoot();
+
+        expectDifference()
+                .strict()
+                .childNodeAdded("/content/foo")
+                .childNodeChanged("/content")
+                .verify(before, after);
+    }
+
+    @Test
+    public void shouldNotDeleteExistingNodesIfDescendantsOfMerged() throws CommitFailedException {
+        final NodeStore source = createNodeStoreWithContent("/content/foo");
+        final NodeStore target = createNodeStoreWithContent("/content/bar");
+
+        final NodeState before = target.getRoot();
+        final NodeBuilder builder = before.builder();
+        NodeStateCopier.copyNodeState(source.getRoot(), builder, "/", of("/content"));
+        commit(target, builder);
+        final NodeState after = target.getRoot();
+
+        expectDifference()
+                .strict()
+                .childNodeAdded("/content/foo")
+                .childNodeChanged("/content")
+                .verify(before, after);
+    }
+
+    @Test
+    public void shouldIgnoreNonMatchingMergePaths() throws CommitFailedException {
+        final NodeStore source = createNodeStoreWithContent("/content/foo");
+        final NodeStore target = createNodeStoreWithContent("/content/bar");
+
+        final NodeState before = target.getRoot();
+        final NodeBuilder builder = before.builder();
+        NodeStateCopier.copyNodeState(source.getRoot(), builder, "/", of("/con"));
+        commit(target, builder);
+        final NodeState after = target.getRoot();
+
+        expectDifference()
+                .strict()
+                .childNodeAdded("/content/foo")
+                .childNodeChanged("/content")
+                .childNodeDeleted("/content/bar")
+                .verify(before, after);
+    }
+
+}

Propchange: jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/nodestate/NodeStateCopierTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/util/NodeStateTestUtils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/util/NodeStateTestUtils.java?rev=1692897&r1=1691280&r2=1692897&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/util/NodeStateTestUtils.java (original)
+++ jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/util/NodeStateTestUtils.java Mon Jul 27 15:25:55 2015
@@ -1,205 +1,205 @@
-/*
- * 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.util;
-
-import org.apache.jackrabbit.oak.api.CommitFailedException;
-import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
-import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
-import org.apache.jackrabbit.oak.spi.commit.DefaultValidator;
-import org.apache.jackrabbit.oak.spi.commit.EditorDiff;
-import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
-import org.apache.jackrabbit.oak.spi.commit.Validator;
-import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
-import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.apache.jackrabbit.oak.spi.state.NodeStore;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-import static org.junit.Assert.assertEquals;
-
-public class NodeStateTestUtils {
-
-    private NodeStateTestUtils() {
-        // no instances
-    }
-
-    public static NodeStore createNodeStoreWithContent(String... paths) throws CommitFailedException {
-        final SegmentNodeStore store = new SegmentNodeStore();
-        final NodeBuilder builder = store.getRoot().builder();
-        for (String path : paths) {
-            create(builder, path);
-        }
-        commit(store, builder);
-        return store;
-    }
-
-    public static void create(NodeBuilder rootBuilder, String path, PropertyState... properties) {
-        final NodeBuilder builder = createOrGetBuilder(rootBuilder, path);
-        for (PropertyState property : properties) {
-            builder.setProperty(property);
-        }
-    }
-
-    public static void commit(NodeStore store, NodeBuilder rootBuilder) throws CommitFailedException {
-        store.merge(rootBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
-    }
-
-    public static NodeBuilder createOrGetBuilder(NodeBuilder builder, String path) {
-        NodeBuilder current = builder;
-        for (final String name : PathUtils.elements(path)) {
-            current = current.child(name);
-        }
-        return current;
-    }
-
-    public static ExpectedDifference expectDifference() {
-        return new ExpectedDifference();
-    }
-
-    public static class ExpectedDifference {
-
-        private ExpectedDifference() {
-        }
-
-        private final Map<String, Set<String>> expected = new HashMap<String, Set<String>>();
-
-        public void verify(NodeState before, NodeState after) {
-            final Map<String, Set<String>> actual = TestValidator.compare(before, after);
-            for (String type : expected.keySet()) {
-                if (!actual.containsKey(type)) {
-                    actual.put(type, Collections.<String>emptySet());
-                }
-                assertEquals(type, expected.get(type), actual.get(type));
-            }
-        }
-
-        public ExpectedDifference propertyAdded(String... paths) {
-            return expect("propertyAdded", paths);
-        }
-
-        public ExpectedDifference propertyChanged(String... paths) {
-            return expect("propertyChanged", paths);
-        }
-
-        public ExpectedDifference propertyDeleted(String... paths) {
-            return expect("propertyDeleted", paths);
-        }
-
-        public ExpectedDifference childNodeAdded(String... paths) {
-            return expect("childNodeAdded", paths);
-        }
-
-        public ExpectedDifference childNodeChanged(String... paths) {
-            return expect("childNodeChanged", paths);
-        }
-
-        public ExpectedDifference childNodeDeleted(String... paths) {
-            return expect("childNodeDeleted", paths);
-        }
-
-        public ExpectedDifference strict() {
-            return this.propertyAdded()
-                    .propertyChanged()
-                    .propertyDeleted()
-                    .childNodeAdded()
-                    .childNodeChanged()
-                    .childNodeDeleted();
-        }
-
-        private ExpectedDifference expect(String type, String... paths) {
-            if (!expected.containsKey(type)) {
-                expected.put(type, new TreeSet<String>());
-            }
-            Collections.addAll(expected.get(type), paths);
-            return this;
-        }
-    }
-
-    private static class TestValidator extends DefaultValidator {
-
-        final Map<String, Set<String>> actual = new HashMap<String, Set<String>>();
-
-        String path = "/";
-
-        public static Map<String, Set<String>> compare(NodeState before, NodeState after) {
-            final TestValidator validator = new TestValidator();
-            EditorDiff.process(validator, before, after);
-            return validator.actual;
-        }
-
-        @Override
-        public void leave(NodeState before, NodeState after) throws CommitFailedException {
-            path = PathUtils.getParentPath(path);
-        }
-
-        @Override
-        public void propertyAdded(PropertyState after) throws CommitFailedException {
-            record("propertyAdded", PathUtils.concat(path, after.getName()));
-        }
-
-        @Override
-        public void propertyChanged(PropertyState before, PropertyState after) throws CommitFailedException {
-            record("propertyChanged", PathUtils.concat(path, after.getName()));
-        }
-
-        @Override
-        public void propertyDeleted(PropertyState before) throws CommitFailedException {
-            record("propertyDeleted", PathUtils.concat(path, before.getName()));
-        }
-
-        @Override
-        public Validator childNodeAdded(String name, NodeState after) throws CommitFailedException {
-            path = PathUtils.concat(path, name);
-            record("childNodeAdded", path);
-            return this;
-        }
-
-        @Override
-        public Validator childNodeChanged(String name, NodeState before, NodeState after)
-                throws CommitFailedException {
-            // make sure not to record false positives (inefficient for large trees)
-            if (!before.equals(after)) {
-                path = PathUtils.concat(path, name);
-                record("childNodeChanged", path);
-                return this;
-            }
-            return null;
-        }
-
-        @Override
-        public Validator childNodeDeleted(String name, NodeState before) throws CommitFailedException {
-            path = PathUtils.concat(path, name);
-            record("childNodeDeleted", path);
-            return this;
-        }
-
-        private void record(String type, String path) {
-            if (!actual.containsKey(type)) {
-                actual.put(type, new TreeSet<String>());
-            }
-            actual.get(type).add(path);
-        }
-    }
-}
+/*
+ * 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.util;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.DefaultValidator;
+import org.apache.jackrabbit.oak.spi.commit.EditorDiff;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
+import org.apache.jackrabbit.oak.spi.commit.Validator;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import static org.junit.Assert.assertEquals;
+
+public class NodeStateTestUtils {
+
+    private NodeStateTestUtils() {
+        // no instances
+    }
+
+    public static NodeStore createNodeStoreWithContent(String... paths) throws CommitFailedException {
+        final SegmentNodeStore store = new SegmentNodeStore();
+        final NodeBuilder builder = store.getRoot().builder();
+        for (String path : paths) {
+            create(builder, path);
+        }
+        commit(store, builder);
+        return store;
+    }
+
+    public static void create(NodeBuilder rootBuilder, String path, PropertyState... properties) {
+        final NodeBuilder builder = createOrGetBuilder(rootBuilder, path);
+        for (PropertyState property : properties) {
+            builder.setProperty(property);
+        }
+    }
+
+    public static void commit(NodeStore store, NodeBuilder rootBuilder) throws CommitFailedException {
+        store.merge(rootBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+    }
+
+    public static NodeBuilder createOrGetBuilder(NodeBuilder builder, String path) {
+        NodeBuilder current = builder;
+        for (final String name : PathUtils.elements(path)) {
+            current = current.child(name);
+        }
+        return current;
+    }
+
+    public static ExpectedDifference expectDifference() {
+        return new ExpectedDifference();
+    }
+
+    public static class ExpectedDifference {
+
+        private ExpectedDifference() {
+        }
+
+        private final Map<String, Set<String>> expected = new HashMap<String, Set<String>>();
+
+        public void verify(NodeState before, NodeState after) {
+            final Map<String, Set<String>> actual = TestValidator.compare(before, after);
+            for (String type : expected.keySet()) {
+                if (!actual.containsKey(type)) {
+                    actual.put(type, Collections.<String>emptySet());
+                }
+                assertEquals(type, expected.get(type), actual.get(type));
+            }
+        }
+
+        public ExpectedDifference propertyAdded(String... paths) {
+            return expect("propertyAdded", paths);
+        }
+
+        public ExpectedDifference propertyChanged(String... paths) {
+            return expect("propertyChanged", paths);
+        }
+
+        public ExpectedDifference propertyDeleted(String... paths) {
+            return expect("propertyDeleted", paths);
+        }
+
+        public ExpectedDifference childNodeAdded(String... paths) {
+            return expect("childNodeAdded", paths);
+        }
+
+        public ExpectedDifference childNodeChanged(String... paths) {
+            return expect("childNodeChanged", paths);
+        }
+
+        public ExpectedDifference childNodeDeleted(String... paths) {
+            return expect("childNodeDeleted", paths);
+        }
+
+        public ExpectedDifference strict() {
+            return this.propertyAdded()
+                    .propertyChanged()
+                    .propertyDeleted()
+                    .childNodeAdded()
+                    .childNodeChanged()
+                    .childNodeDeleted();
+        }
+
+        private ExpectedDifference expect(String type, String... paths) {
+            if (!expected.containsKey(type)) {
+                expected.put(type, new TreeSet<String>());
+            }
+            Collections.addAll(expected.get(type), paths);
+            return this;
+        }
+    }
+
+    private static class TestValidator extends DefaultValidator {
+
+        final Map<String, Set<String>> actual = new HashMap<String, Set<String>>();
+
+        String path = "/";
+
+        public static Map<String, Set<String>> compare(NodeState before, NodeState after) {
+            final TestValidator validator = new TestValidator();
+            EditorDiff.process(validator, before, after);
+            return validator.actual;
+        }
+
+        @Override
+        public void leave(NodeState before, NodeState after) throws CommitFailedException {
+            path = PathUtils.getParentPath(path);
+        }
+
+        @Override
+        public void propertyAdded(PropertyState after) throws CommitFailedException {
+            record("propertyAdded", PathUtils.concat(path, after.getName()));
+        }
+
+        @Override
+        public void propertyChanged(PropertyState before, PropertyState after) throws CommitFailedException {
+            record("propertyChanged", PathUtils.concat(path, after.getName()));
+        }
+
+        @Override
+        public void propertyDeleted(PropertyState before) throws CommitFailedException {
+            record("propertyDeleted", PathUtils.concat(path, before.getName()));
+        }
+
+        @Override
+        public Validator childNodeAdded(String name, NodeState after) throws CommitFailedException {
+            path = PathUtils.concat(path, name);
+            record("childNodeAdded", path);
+            return this;
+        }
+
+        @Override
+        public Validator childNodeChanged(String name, NodeState before, NodeState after)
+                throws CommitFailedException {
+            // make sure not to record false positives (inefficient for large trees)
+            if (!before.equals(after)) {
+                path = PathUtils.concat(path, name);
+                record("childNodeChanged", path);
+                return this;
+            }
+            return null;
+        }
+
+        @Override
+        public Validator childNodeDeleted(String name, NodeState before) throws CommitFailedException {
+            path = PathUtils.concat(path, name);
+            record("childNodeDeleted", path);
+            return this;
+        }
+
+        private void record(String type, String path) {
+            if (!actual.containsKey(type)) {
+                actual.put(type, new TreeSet<String>());
+            }
+            actual.get(type).add(path);
+        }
+    }
+}

Propchange: jackrabbit/oak/branches/1.2/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/util/NodeStateTestUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native