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