You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-dev@jackrabbit.apache.org by Julian Sedding <js...@gmail.com> on 2015/08/06 16:41:27 UTC
Re: svn commit: r1694498 - in /jackrabbit/oak/trunk:
oak-run/src/main/java/org/apache/jackrabbit/oak/run/ oak-run/src/test/java/org/apache/jackrabbit/oak/run/
oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/ oak-upgrade/src/main/java/org/apache...
Thanks Manfred for getting this in!
For completeness regarding credits (Manfred couldn't know): I created
the initial patch and developed some doubts over time. Tomek took care
of adapting the code to address those doubts and create a superior
feature (IMHO). I assisted at that stage with code reviews and
suggestions only. So thanks Tomek!
Regards
Julian
On Thu, Aug 6, 2015 at 3:57 PM, <ba...@apache.org> wrote:
> Author: baedke
> Date: Thu Aug 6 13:57:58 2015
> New Revision: 1694498
>
> URL: http://svn.apache.org/r1694498
> Log:
> OAK-2776: Upgrade should allow to skip copying versions
>
> Initial implementation. Full credit goes to Julian Sedding (jsedding@gmail.com) for the patch and to Tomek Rekawek (trekawek@gmail.com) for aligning it with the current trunk and integrating into oak-run.
>
> Added:
> jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/ParseVersionCopyArgumentTest.java
> jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/DescendantsIterator.java
> jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/
> jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java
> jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java
> jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java
> jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java
> jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java
> jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/util/VersionCopyTestUtils.java
> Modified:
> jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java
> jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/JackrabbitNodeState.java
> jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java
> jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java
>
> Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java?rev=1694498&r1=1694497&r2=1694498&view=diff
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java (original)
> +++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java Thu Aug 6 13:57:58 2015
> @@ -28,8 +28,12 @@ import java.io.File;
> import java.io.IOException;
> import java.io.InputStream;
> import java.sql.Timestamp;
> +import java.text.DateFormat;
> +import java.text.ParseException;
> +import java.text.SimpleDateFormat;
> import java.util.ArrayList;
> import java.util.Arrays;
> +import java.util.Calendar;
> import java.util.Collections;
> import java.util.HashMap;
> import java.util.HashSet;
> @@ -60,6 +64,8 @@ import joptsimple.ArgumentAcceptingOptio
> import joptsimple.OptionParser;
> import joptsimple.OptionSet;
> import joptsimple.OptionSpec;
> +
> +import org.apache.commons.lang.time.DateUtils;
> import org.apache.jackrabbit.core.RepositoryContext;
> import org.apache.jackrabbit.core.config.RepositoryConfig;
> import org.apache.jackrabbit.oak.Oak;
> @@ -939,6 +945,8 @@ public final class Main {
> private static void upgrade(String[] args) throws Exception {
> OptionParser parser = new OptionParser();
> parser.accepts("datastore", "keep data store");
> + ArgumentAcceptingOptionSpec<String> copyVersions = parser.accepts("copy-versions", "copy referenced versions. valid arguments: true|false|yyyy-mm-dd").withRequiredArg().defaultsTo("true");
> + ArgumentAcceptingOptionSpec<String> copyOrphanedVersions = parser.accepts("copy-orphaned-versions", "copy all versions. valid arguments: true|false|yyyy-mm-dd").withRequiredArg().defaultsTo("true");
> OptionSpec<String> nonOption = parser.nonOptions();
> OptionSet options = parser.parse(args);
>
> @@ -967,6 +975,7 @@ public final class Main {
> new RepositoryUpgrade(source, target);
> upgrade.setCopyBinariesByReference(
> options.has("datastore"));
> + setCopyVersionOptions(copyVersions.value(options), copyOrphanedVersions.value(options), upgrade);
> upgrade.copy(null);
> } finally {
> target.dispose();
> @@ -996,6 +1005,26 @@ public final class Main {
> }
> }
>
> + private static void setCopyVersionOptions(String copyVersions, String copyOrphanedVersions, RepositoryUpgrade upgrade) throws ParseException {
> + upgrade.setCopyVersions(parseVersionCopyArgument(copyVersions));
> + upgrade.setCopyOrphanedVersions(parseVersionCopyArgument(copyOrphanedVersions));
> + }
> +
> + static Calendar parseVersionCopyArgument(String string) throws ParseException {
> + final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
> + final Calendar calendar;
> +
> + if (Boolean.parseBoolean(string)) {
> + calendar = Calendar.getInstance();
> + calendar.setTimeInMillis(0);
> + } else if (string != null && string.matches("^\\d{4}-\\d{2}-\\d{2}$")) {
> + calendar = DateUtils.toCalendar(df.parse(string));
> + } else {
> + calendar = null;
> + }
> + return calendar;
> + }
> +
> private static void server(String defaultUri, String[] args) throws Exception {
> OptionParser parser = new OptionParser();
>
>
> Added: jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/ParseVersionCopyArgumentTest.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/ParseVersionCopyArgumentTest.java?rev=1694498&view=auto
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/ParseVersionCopyArgumentTest.java (added)
> +++ jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/ParseVersionCopyArgumentTest.java Thu Aug 6 13:57:58 2015
> @@ -0,0 +1,53 @@
> +/*
> + * 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.run;
> +
> +import java.text.ParseException;
> +import java.util.Arrays;
> +import java.util.Calendar;
> +import java.util.GregorianCalendar;
> +
> +import org.junit.Assert;
> +import org.junit.Test;
> +
> +public class ParseVersionCopyArgumentTest {
> +
> + @Test
> + public void parseTrue() throws ParseException {
> + for (String argument : Arrays.asList("true", "TRUE", "TrUe")) {
> + final Calendar result = Main.parseVersionCopyArgument(argument);
> + Assert.assertEquals(0, result.getTimeInMillis());
> + }
> + }
> +
> + @Test
> + public void parseDate() throws ParseException {
> + final Calendar result = Main.parseVersionCopyArgument("2013-01-01");
> + Assert.assertEquals(new GregorianCalendar(2013, 0, 1), result);
> + }
> +
> + @Test
> + public void parseFalse() throws ParseException {
> + for (String argument : Arrays.asList("false", "FaLse", "", "xyz", null)) {
> + final Calendar result = Main.parseVersionCopyArgument(argument);
> + Assert.assertNull(result);
> + }
> + }
> +}
>
> Added: jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/DescendantsIterator.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/DescendantsIterator.java?rev=1694498&view=auto
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/DescendantsIterator.java (added)
> +++ jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/DescendantsIterator.java Thu Aug 6 13:57:58 2015
> @@ -0,0 +1,46 @@
> +package org.apache.jackrabbit.oak.upgrade;
> +
> +import java.util.ArrayDeque;
> +import java.util.Deque;
> +import java.util.Iterator;
> +
> +import org.apache.jackrabbit.commons.iterator.AbstractLazyIterator;
> +import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
> +import org.apache.jackrabbit.oak.spi.state.NodeState;
> +
> +public class DescendantsIterator extends AbstractLazyIterator<NodeState> {
> +
> + private final Deque<Iterator<? extends ChildNodeEntry>> stack = new ArrayDeque<Iterator<? extends ChildNodeEntry>>();
> +
> + private final int maxLevel;
> +
> + public DescendantsIterator(NodeState root, int maxLevel) {
> + this.maxLevel = maxLevel;
> + stack.push(root.getChildNodeEntries().iterator());
> + }
> +
> + @Override
> + protected NodeState getNext() {
> + if (!fillStack()) {
> + return null;
> + }
> + return stack.peekFirst().next().getNodeState();
> + }
> +
> + private boolean fillStack() {
> + while (stack.size() < maxLevel || !stack.peekFirst().hasNext()) {
> + Iterator<? extends ChildNodeEntry> topIterator = stack.peekFirst();
> + if (topIterator.hasNext()) {
> + final NodeState nextNode = topIterator.next().getNodeState();
> + stack.push(nextNode.getChildNodeEntries().iterator());
> + } else {
> + stack.pop();
> + if (stack.isEmpty()) {
> + return false;
> + }
> + }
> + }
> + return true;
> + }
> +
> +}
>
> Modified: jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/JackrabbitNodeState.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/JackrabbitNodeState.java?rev=1694498&r1=1694497&r2=1694498&view=diff
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/JackrabbitNodeState.java (original)
> +++ jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/JackrabbitNodeState.java Thu Aug 6 13:57:58 2015
> @@ -32,7 +32,6 @@ import static org.apache.jackrabbit.JcrC
> import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
> import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
> import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
> -import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONHISTORY;
> import static org.apache.jackrabbit.JcrConstants.MIX_REFERENCEABLE;
> import static org.apache.jackrabbit.JcrConstants.MIX_VERSIONABLE;
> import static org.apache.jackrabbit.JcrConstants.NT_BASE;
> @@ -46,7 +45,6 @@ import static org.apache.jackrabbit.oak.
> import static org.apache.jackrabbit.oak.api.Type.NAMES;
> import static org.apache.jackrabbit.oak.api.Type.STRING;
> import static org.apache.jackrabbit.oak.plugins.tree.impl.TreeConstants.OAK_CHILD_ORDER;
> -import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.MIX_REP_VERSIONABLE_PATHS;
>
> import java.io.ByteArrayInputStream;
> import java.io.IOException;
> @@ -139,8 +137,6 @@ class JackrabbitNodeState extends Abstra
> */
> private final Map<String, String> uriToPrefix;
>
> - private final Map<String, String> versionablePaths;
> -
> private final boolean useBinaryReferences;
>
> private final Map<String, NodeId> nodes;
> @@ -158,7 +154,6 @@ class JackrabbitNodeState extends Abstra
> String workspaceName,
> NodeState root,
> Map<String, String> uriToPrefix,
> - Map<String, String> versionablePaths,
> boolean copyBinariesByReference,
> boolean skipOnError
> ) throws RepositoryException {
> @@ -169,7 +164,6 @@ class JackrabbitNodeState extends Abstra
> versionPM, root, uriToPrefix,
> VERSION_STORAGE_NODE_ID, "/jcr:system/jcr:versionStorage",
> null,
> - versionablePaths,
> emptyMountPoints,
> copyBinariesByReference,
> skipOnError
> @@ -179,7 +173,6 @@ class JackrabbitNodeState extends Abstra
> versionPM, root, uriToPrefix,
> ACTIVITIES_NODE_ID, "/jcr:system/jcr:activities",
> null,
> - versionablePaths,
> emptyMountPoints,
> copyBinariesByReference,
> skipOnError
> @@ -193,7 +186,7 @@ class JackrabbitNodeState extends Abstra
> );
> return new JackrabbitNodeState(
> pm, root, uriToPrefix, ROOT_NODE_ID, "/",
> - workspaceName, versionablePaths, mountPoints, copyBinariesByReference, skipOnError);
> + workspaceName, mountPoints, copyBinariesByReference, skipOnError);
> }
>
> private JackrabbitNodeState(
> @@ -209,7 +202,6 @@ class JackrabbitNodeState extends Abstra
> this.isVersionHistory = parent.isVersionHistory;
> this.isFrozenNode = parent.isFrozenNode;
> this.uriToPrefix = parent.uriToPrefix;
> - this.versionablePaths = parent.versionablePaths;
> this.useBinaryReferences = parent.useBinaryReferences;
> this.properties = createProperties(bundle);
> this.nodes = createNodes(bundle);
> @@ -217,7 +209,6 @@ class JackrabbitNodeState extends Abstra
> this.mountPoints = parent.mountPoints;
> this.nodeStateCache = parent.nodeStateCache;
> setChildOrder();
> - setVersionablePaths();
> fixFrozenUuid();
> logNewNode(this);
> }
> @@ -225,7 +216,7 @@ class JackrabbitNodeState extends Abstra
> JackrabbitNodeState(
> PersistenceManager source, NodeState root,
> Map<String, String> uriToPrefix, NodeId id, String path,
> - String workspaceName, Map<String, String> versionablePaths,
> + String workspaceName,
> Map<NodeId, JackrabbitNodeState> mountPoints,
> boolean useBinaryReferences, boolean skipOnError) {
> this.parent = null;
> @@ -239,7 +230,6 @@ class JackrabbitNodeState extends Abstra
> this.isVersionHistory = new TypePredicate(root, NT_VERSIONHISTORY);
> this.isFrozenNode = new TypePredicate(root, NT_FROZENNODE);
> this.uriToPrefix = uriToPrefix;
> - this.versionablePaths = versionablePaths;
> this.mountPoints = mountPoints;
> final int cacheSize = 50; // cache size 50 results in > 25% cache hits during version copy
> this.nodeStateCache = new LinkedHashMap<NodeId, JackrabbitNodeState>(cacheSize, 0.75f, true) {
> @@ -380,28 +370,6 @@ class JackrabbitNodeState extends Abstra
> }
> }
>
> - private void setVersionablePaths() {
> - if (isVersionable.apply(this)) {
> - String uuid = getString(JCR_VERSIONHISTORY);
> - if (uuid != null) {
> - versionablePaths.put(uuid, getPath());
> - }
> - } else if (isVersionHistory.apply(this)) {
> - String uuid = getString(JCR_UUID);
> - String path = versionablePaths.get(uuid);
> - if (path != null) {
> - properties.put(workspaceName, PropertyStates.createProperty(
> - workspaceName, path, Type.PATH));
> -
> - Set<String> mixins = newLinkedHashSet(getNames(JCR_MIXINTYPES));
> - if (mixins.add(MIX_REP_VERSIONABLE_PATHS)) {
> - properties.put(JCR_MIXINTYPES, PropertyStates.createProperty(
> - JCR_MIXINTYPES, mixins, Type.NAMES));
> - }
> - }
> - }
> - }
> -
> private Map<String, NodeId> createNodes(NodePropBundle bundle) {
> Map<String, NodeId> children = newLinkedHashMap();
> for (ChildNodeEntry entry : bundle.getChildNodeEntries()) {
> @@ -728,4 +696,4 @@ class JackrabbitNodeState extends Abstra
> log.warn(getPath() + ": " + message, cause);
> }
>
> -}
> \ No newline at end of file
> +}
>
> Modified: jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java?rev=1694498&r1=1694497&r2=1694498&view=diff
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java (original)
> +++ jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java Thu Aug 6 13:57:58 2015
> @@ -26,6 +26,7 @@ import static com.google.common.collect.
> import static com.google.common.collect.Sets.newHashSet;
> import static com.google.common.collect.Sets.union;
> import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
> +import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONSTORAGE;
> import static org.apache.jackrabbit.oak.plugins.name.Namespaces.addCustomMapping;
> import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.NODE_TYPES_PATH;
> import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.JCR_ALL;
> @@ -35,6 +36,7 @@ import static org.apache.jackrabbit.oak.
> import java.io.File;
> import java.io.IOException;
> import java.io.InputStream;
> +import java.util.Calendar;
> import java.util.Collection;
> import java.util.Iterator;
> import java.util.List;
> @@ -110,6 +112,9 @@ import org.apache.jackrabbit.oak.spi.sta
> 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.oak.upgrade.version.VersionCopier;
> +import org.apache.jackrabbit.oak.upgrade.version.VersionCopyConfiguration;
> +import org.apache.jackrabbit.oak.upgrade.version.VersionableEditor;
> import org.apache.jackrabbit.spi.Name;
> import org.apache.jackrabbit.spi.QNodeDefinition;
> import org.apache.jackrabbit.spi.QNodeTypeDefinition;
> @@ -168,6 +173,8 @@ public class RepositoryUpgrade {
>
> private List<CommitHook> customCommitHooks = null;
>
> + private VersionCopyConfiguration versionCopyConfiguration = new VersionCopyConfiguration();
> +
> /**
> * Copies the contents of the repository in the given source directory
> * to the given target node store.
> @@ -288,6 +295,41 @@ public class RepositoryUpgrade {
> }
>
> /**
> + * Configures the version storage copy. Be default all versions are copied.
> + * One may disable it completely by setting {@code null} here or limit it to
> + * a selected date range: {@code <minDate, now()>}.
> + *
> + * @param minDate
> + * minimum date of the versions to copy or {@code null} to
> + * disable the storage version copying completely. Default value:
> + * {@code 1970-01-01 00:00:00}.
> + */
> + public void setCopyVersions(Calendar minDate) {
> + versionCopyConfiguration.setCopyVersions(minDate);
> + }
> +
> + /**
> + * Configures copying of the orphaned version histories (eg. ones that are
> + * not referenced by the existing nodes). By default all orphaned version
> + * histories are copied. One may disable it completely by setting
> + * {@code null} here or limit it to a selected date range:
> + * {@code <minDate, now()>}. <br/>
> + * <br/>
> + * Please notice, that this option is overriden by the
> + * {@link #setCopyVersions(Calendar)}. You can't copy orphaned versions
> + * older than set in {@link #setCopyVersions(Calendar)} and if you set
> + * {@code null} there, this option will be ignored.
> + *
> + * @param minDate
> + * minimum date of the orphaned versions to copy or {@code null}
> + * to not copy them at all. Default value:
> + * {@code 1970-01-01 00:00:00}.
> + */
> + public void setCopyOrphanedVersions(Calendar minDate) {
> + versionCopyConfiguration.setCopyOrphanedVersions(minDate);
> + }
> +
> + /**
> * Copies the full content from the source to the target repository.
> * <p>
> * The source repository <strong>must not be modified</strong> while
> @@ -364,11 +406,10 @@ public class RepositoryUpgrade {
> new TypeEditorProvider(false).getRootEditor(
> base, builder.getNodeState(), builder, null);
>
> - Map<String, String> versionablePaths = newHashMap();
> NodeState root = builder.getNodeState();
>
> final NodeState sourceState = JackrabbitNodeState.createRootNodeState(
> - source, workspaceName, root, uriToPrefix, versionablePaths, copyBinariesByReference, skipOnError);
> + source, workspaceName, root, uriToPrefix, copyBinariesByReference, skipOnError);
>
> final Stopwatch watch = Stopwatch.createStarted();
>
> @@ -377,10 +418,15 @@ public class RepositoryUpgrade {
> builder.getNodeState(); // on TarMK this does call triggers the actual copy
> logger.info("Upgrading workspace content completed in {}s ({})", watch.elapsed(TimeUnit.SECONDS), watch);
>
> - watch.reset().start();
> - logger.info("Copying version store content");
> - copyVersionStore(sourceState, builder);
> - logger.debug("Upgrading version store content completed in {}s ({}).", watch.elapsed(TimeUnit.SECONDS), watch);
> + if (!versionCopyConfiguration.skipOrphanedVersionsCopy()) {
> + logger.info("Copying version storage");
> + watch.reset().start();
> + copyVersionStorage(sourceState, builder);
> + builder.getNodeState(); // on TarMK this does call triggers the actual copy
> + logger.info("Version storage copied in {}s ({})", watch.elapsed(TimeUnit.SECONDS), watch);
> + } else {
> + logger.info("Skipping the version storage as the copyOrphanedVersions is set to false");
> + }
>
> watch.reset().start();
> logger.info("Applying default commit hooks");
> @@ -396,7 +442,9 @@ public class RepositoryUpgrade {
> // hooks specific to the upgrade, need to run first
> hooks.add(new EditorHook(new CompositeEditorProvider(
> new RestrictionEditorProvider(),
> - new GroupEditorProvider(groupsPath)
> + new GroupEditorProvider(groupsPath),
> + // copy referenced version histories
> + new VersionableEditor.Provider(sourceState, workspaceName, versionCopyConfiguration)
> )));
>
> // security-related hooks
> @@ -768,10 +816,10 @@ public class RepositoryUpgrade {
> return tmpl;
> }
>
> - private void copyWorkspace(NodeState sourceState, NodeBuilder builder, String workspaceName)
> + private String copyWorkspace(NodeState sourceState, NodeBuilder builder, String workspaceName)
> throws RepositoryException {
> final Set<String> includes = calculateEffectiveIncludePaths(sourceState);
> - final Set<String> excludes = union(copyOf(this.excludePaths), of("/jcr:system/jcr:versionStorage", "/jcr:system/jcr:activities"));
> + final Set<String> excludes = union(copyOf(this.excludePaths), of("/jcr:system/jcr:versionStorage"));
> final Set<String> merges = union(copyOf(this.mergePaths), of("/jcr:system"));
>
> logger.info("Copying workspace {} [i: {}, e: {}, m: {}]", workspaceName, includes, excludes, merges);
> @@ -781,14 +829,22 @@ public class RepositoryUpgrade {
> .exclude(excludes)
> .merge(merges)
> .copy(sourceState, builder);
> +
> + return workspaceName;
> }
>
> - private void copyVersionStore(NodeState sourceState, NodeBuilder builder)
> + private void copyVersionStorage(NodeState sourceState, NodeBuilder builder)
> throws RepositoryException {
> - NodeStateCopier.builder()
> - .include("/jcr:system/jcr:versionStorage", "/jcr:system/jcr:activities")
> - .merge("/jcr:system")
> - .copy(sourceState, builder);
> + final NodeState versionStorage = sourceState.getChildNode(JCR_SYSTEM).getChildNode(JCR_VERSIONSTORAGE);
> + final Iterator<NodeState> versionStorageIterator = new DescendantsIterator(versionStorage, 3);
> + final VersionCopier versionCopier = new VersionCopier(sourceState, builder);
> +
> + while (versionStorageIterator.hasNext()) {
> + final NodeState versionHistoryBucket = versionStorageIterator.next();
> + for (String versionHistory : versionHistoryBucket.getChildNodeNames()) {
> + versionCopier.copyVersionHistory(versionHistory, versionCopyConfiguration.getOrphanedMinDate());
> + }
> + }
> }
>
> private Set<String> calculateEffectiveIncludePaths(NodeState state) {
>
> Added: jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java?rev=1694498&view=auto
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java (added)
> +++ jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java Thu Aug 6 13:57:58 2015
> @@ -0,0 +1,97 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one or more
> + * contributor license agreements. See the NOTICE file distributed with
> + * this work for additional information regarding copyright ownership.
> + * The ASF licenses this file to You under the Apache License, Version 2.0
> + * (the "License"); you may not use this file except in compliance with
> + * the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +package org.apache.jackrabbit.oak.upgrade.version;
> +
> +import static org.apache.jackrabbit.JcrConstants.JCR_CREATED;
> +import static org.apache.jackrabbit.JcrConstants.NT_VERSION;
> +
> +import java.util.Calendar;
> +
> +import org.apache.jackrabbit.oak.api.Type;
> +import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate;
> +import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
> +import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
> +import org.apache.jackrabbit.oak.spi.state.NodeState;
> +import org.apache.jackrabbit.oak.upgrade.nodestate.NodeStateCopier;
> +import org.apache.jackrabbit.util.ISO8601;
> +
> +import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.VERSION_STORE_PATH;
> +
> +import static org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getVersionHistoryPath;
> +import static org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getVersionHistoryNodeState;
> +
> +/**
> + * This class allows to copy the version history, optionally filtering it with a
> + * given date.
> + */
> +public class VersionCopier {
> +
> + private final TypePredicate isVersion;
> +
> + private final NodeState sourceRoot;
> +
> + private final NodeBuilder rootBuilder;
> +
> + public VersionCopier(NodeState sourceRoot, NodeBuilder rootBuilder) {
> + this.isVersion = new TypePredicate(rootBuilder.getNodeState(), NT_VERSION);
> + this.sourceRoot = sourceRoot;
> + this.rootBuilder = rootBuilder;
> + }
> +
> + /**
> + * Copy history filtering versions using passed date and returns @{code
> + * true} if at least one version has been copied.
> + *
> + * @param versionableUuid
> + * Name of the version history node
> + * @param minDate
> + * Only versions older than this date will be copied
> + * @return {@code true} if at least one version has been copied
> + */
> + public boolean copyVersionHistory(String versionableUuid, Calendar minDate) {
> + final String versionHistoryPath = getVersionHistoryPath(versionableUuid);
> + final NodeState versionHistory = getVersionHistoryNodeState(sourceRoot, versionableUuid);
> + final Calendar lastModified = getVersionHistoryLastModified(versionHistory);
> +
> + if (lastModified.after(minDate) || minDate.getTimeInMillis() == 0) {
> + NodeStateCopier.builder()
> + .include(versionHistoryPath)
> + .merge(VERSION_STORE_PATH)
> + .copy(sourceRoot, rootBuilder);
> + return true;
> + }
> + return false;
> + }
> +
> + private Calendar getVersionHistoryLastModified(final NodeState versionHistory) {
> + Calendar youngest = Calendar.getInstance();
> + youngest.setTimeInMillis(0);
> + for (final ChildNodeEntry entry : versionHistory.getChildNodeEntries()) {
> + final NodeState version = entry.getNodeState();
> + if (!isVersion.apply(version)) {
> + continue;
> + }
> + if (version.hasProperty(JCR_CREATED)) {
> + final Calendar created = ISO8601.parse(version.getProperty(JCR_CREATED).getValue(Type.DATE));
> + if (created.after(youngest)) {
> + youngest = created;
> + }
> + }
> + }
> + return youngest;
> + }
> +}
>
> Added: jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java?rev=1694498&view=auto
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java (added)
> +++ jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java Thu Aug 6 13:57:58 2015
> @@ -0,0 +1,67 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one or more
> + * contributor license agreements. See the NOTICE file distributed with
> + * this work for additional information regarding copyright ownership.
> + * The ASF licenses this file to You under the Apache License, Version 2.0
> + * (the "License"); you may not use this file except in compliance with
> + * the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +package org.apache.jackrabbit.oak.upgrade.version;
> +
> +import java.util.Calendar;
> +
> +/**
> + * This class allows to configure the behaviour of the version copier.
> + */
> +public class VersionCopyConfiguration {
> +
> + private Calendar copyVersions;
> +
> + private Calendar copyOrphanedVersions;
> +
> + public VersionCopyConfiguration() {
> + final Calendar epoch = Calendar.getInstance();
> + epoch.setTimeInMillis(0);
> + this.copyVersions = epoch;
> + this.copyOrphanedVersions = epoch;
> + }
> +
> + public void setCopyVersions(Calendar copyVersions) {
> + this.copyVersions = copyVersions;
> + }
> +
> + public void setCopyOrphanedVersions(Calendar copyOrphanedVersions) {
> + this.copyOrphanedVersions = copyOrphanedVersions;
> + }
> +
> + public Calendar getVersionsMinDate() {
> + return copyVersions;
> + }
> +
> + public Calendar getOrphanedMinDate() {
> + if (copyVersions == null) {
> + return copyVersions;
> + } else if (copyOrphanedVersions != null && copyVersions.after(copyOrphanedVersions)) {
> + return copyVersions;
> + } else {
> + return copyOrphanedVersions;
> + }
> + }
> +
> + public boolean isCopyVersions() {
> + return copyVersions != null;
> + }
> +
> + public boolean skipOrphanedVersionsCopy() {
> + return copyVersions == null || copyOrphanedVersions == null;
> + }
> +
> +}
> \ No newline at end of file
>
> Added: jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java?rev=1694498&view=auto
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java (added)
> +++ jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java Thu Aug 6 13:57:58 2015
> @@ -0,0 +1,67 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one or more
> + * contributor license agreements. See the NOTICE file distributed with
> + * this work for additional information regarding copyright ownership.
> + * The ASF licenses this file to You under the Apache License, Version 2.0
> + * (the "License"); you may not use this file except in compliance with
> + * the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +package org.apache.jackrabbit.oak.upgrade.version;
> +
> +import static com.google.common.collect.Iterables.concat;
> +import static java.util.Collections.singleton;
> +import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
> +import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONSTORAGE;
> +
> +import java.util.ArrayList;
> +import java.util.List;
> +
> +import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
> +import org.apache.jackrabbit.oak.spi.state.NodeState;
> +
> +import com.google.common.base.Joiner;
> +
> +public class VersionHistoryUtil {
> +
> + public static String getVersionHistoryPath(String versionableUuid) {
> + return Joiner.on('/').join(concat(
> + singleton(""),
> + getVersionHistoryPathSegments(versionableUuid),
> + singleton(versionableUuid)));
> + }
> +
> + static NodeState getVersionHistoryNodeState(NodeState root, String versionableUuid) {
> + NodeState historyParent = root;
> + for (String segment : getVersionHistoryPathSegments(versionableUuid)) {
> + historyParent = historyParent.getChildNode(segment);
> + }
> + return historyParent.getChildNode(versionableUuid);
> + }
> +
> + static NodeBuilder getVersionHistoryBuilder(NodeBuilder root, String versionableUuid) {
> + NodeBuilder history = root;
> + for (String segment : getVersionHistoryPathSegments(versionableUuid)) {
> + history = history.getChildNode(segment);
> + }
> + return history.getChildNode(versionableUuid);
> + }
> +
> + private static List<String> getVersionHistoryPathSegments(String versionableUuid) {
> + final List<String> segments = new ArrayList<String>();
> + segments.add(JCR_SYSTEM);
> + segments.add(JCR_VERSIONSTORAGE);
> + for (int i = 0; i < 3; i++) {
> + segments.add(versionableUuid.substring(i * 2, i * 2 + 2));
> + }
> + return segments;
> + }
> +
> +}
>
> Added: jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java?rev=1694498&view=auto
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java (added)
> +++ jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java Thu Aug 6 13:57:58 2015
> @@ -0,0 +1,226 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one or more
> + * contributor license agreements. See the NOTICE file distributed with
> + * this work for additional information regarding copyright ownership.
> + * The ASF licenses this file to You under the Apache License, Version 2.0
> + * (the "License"); you may not use this file except in compliance with
> + * the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +package org.apache.jackrabbit.oak.upgrade.version;
> +
> +import org.apache.jackrabbit.oak.api.CommitFailedException;
> +import org.apache.jackrabbit.oak.api.Type;
> +import org.apache.jackrabbit.oak.commons.PathUtils;
> +import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate;
> +import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
> +import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
> +import org.apache.jackrabbit.oak.spi.commit.Editor;
> +import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
> +import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
> +import org.apache.jackrabbit.oak.spi.state.NodeState;
> +
> +import java.util.Set;
> +
> +import static com.google.common.collect.ImmutableSet.of;
> +import static com.google.common.collect.Sets.newHashSet;
> +import static org.apache.jackrabbit.JcrConstants.JCR_BASEVERSION;
> +import static org.apache.jackrabbit.JcrConstants.JCR_ISCHECKEDOUT;
> +import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
> +import static org.apache.jackrabbit.JcrConstants.JCR_PREDECESSORS;
> +import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
> +import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONHISTORY;
> +import static org.apache.jackrabbit.JcrConstants.MIX_REFERENCEABLE;
> +import static org.apache.jackrabbit.JcrConstants.MIX_VERSIONABLE;
> +import static org.apache.jackrabbit.oak.plugins.memory.MultiGenericPropertyState.nameProperty;
> +import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.MIX_REP_VERSIONABLE_PATHS;
> +
> +/**
> + * The VersionableEditor provides two possible ways to handle
> + * versionable nodes:
> + * <ul>
> + * <li>it can copy the version histories of versionable nodes, or</li>
> + * <li>
> + * it can skip copying version histories and remove the
> + * {@code mix:versionable} mixin together with any related
> + * properties (see {@link #removeVersionProperties(NodeBuilder)}).
> + * </li>
> + * </ul>
> + */
> +public class VersionableEditor extends DefaultEditor {
> +
> + private static final Set<String> SKIPPED_PATHS = of("/oak:index", "/jcr:system/jcr:versionStorage");
> +
> + private final Provider provider;
> +
> + private final NodeBuilder rootBuilder;
> +
> + private final TypePredicate isReferenceable;
> +
> + private final TypePredicate isVersionable;
> +
> + private final VersionCopier versionCopier;
> +
> + private String path;
> +
> + private VersionableEditor(Provider provider, NodeBuilder builder) {
> + this.provider = provider;
> + this.rootBuilder = builder;
> + this.isVersionable = new TypePredicate(builder.getNodeState(), MIX_VERSIONABLE);
> + this.isReferenceable = new TypePredicate(builder.getNodeState(), MIX_REFERENCEABLE);
> + this.versionCopier = new VersionCopier(provider.sourceRoot, builder);
> + this.path = "/";
> + }
> +
> + public static class Provider implements EditorProvider {
> +
> + private final NodeState sourceRoot;
> +
> + private final String workspaceName;
> +
> + private final VersionCopyConfiguration config;
> +
> + public Provider(NodeState sourceRoot, String workspaceName, VersionCopyConfiguration config) {
> + this.sourceRoot = sourceRoot;
> + this.workspaceName = workspaceName;
> + this.config = config;
> + }
> +
> + @Override
> + public Editor getRootEditor(NodeState before, NodeState after, NodeBuilder builder, CommitInfo info) throws CommitFailedException {
> + return new VersionableEditor(this, builder);
> + }
> + }
> +
> + @Override
> + public Editor childNodeAdded(String name, NodeState after) throws CommitFailedException {
> + final String path = PathUtils.concat(this.path, name);
> + // skip deleted nodes and well known paths that may not contain versionable nodes
> + if (after == null || SKIPPED_PATHS.contains(path)) {
> + return null;
> + }
> +
> + // assign path field only after checking that we don't skip this subtree
> + this.path = path;
> +
> + final VersionCopyConfiguration c = provider.config;
> + if (isVersionable.apply(after)) {
> + final String versionableUuid = getProperty(after, JCR_UUID, Type.STRING);
> + boolean versionHistoryExists = isVersionHistoryExists(versionableUuid);
> + if (c.isCopyVersions() && c.skipOrphanedVersionsCopy()) {
> + versionHistoryExists = copyVersionHistory(after);
> + } else if (c.isCopyVersions() && !c.skipOrphanedVersionsCopy()) {
> + // all version histories have been copied, but maybe the date
> + // range for orphaned entries is narrower
> + if (c.getOrphanedMinDate().after(c.getVersionsMinDate())) {
> + versionHistoryExists = copyVersionHistory(after);
> + }
> + } else {
> + versionHistoryExists = false;
> + }
> +
> + if (versionHistoryExists) {
> + setVersionablePath(versionableUuid);
> + } else {
> + removeVersionProperties(getNodeBuilder(rootBuilder, this.path));
> + }
> + }
> +
> + return this;
> + }
> +
> + private boolean copyVersionHistory(NodeState versionable) {
> + assert versionable.exists();
> +
> + final String versionableUuid = versionable.getProperty(JCR_UUID).getValue(Type.STRING);
> + return versionCopier.copyVersionHistory(versionableUuid, provider.config.getVersionsMinDate());
> + }
> +
> + private void setVersionablePath(String versionableUuid) {
> + final NodeBuilder versionHistory = VersionHistoryUtil.getVersionHistoryBuilder(rootBuilder, versionableUuid);
> + versionHistory.setProperty(provider.workspaceName, path, Type.PATH);
> + addMixin(versionHistory, MIX_REP_VERSIONABLE_PATHS);
> + }
> +
> + private boolean isVersionHistoryExists(String versionableUuid) {
> + return VersionHistoryUtil.getVersionHistoryNodeState(rootBuilder.getNodeState(), versionableUuid).exists();
> + }
> +
> + private void removeVersionProperties(final NodeBuilder versionableBuilder) {
> + assert versionableBuilder.exists();
> +
> + removeMixin(versionableBuilder, MIX_VERSIONABLE);
> +
> + // we don't know if the UUID is otherwise referenced,
> + // so make sure the node remains referencable
> + if (!isReferenceable.apply(versionableBuilder.getNodeState())) {
> + addMixin(versionableBuilder, MIX_REFERENCEABLE);
> + }
> +
> + versionableBuilder.removeProperty(JCR_VERSIONHISTORY);
> + versionableBuilder.removeProperty(JCR_PREDECESSORS);
> + versionableBuilder.removeProperty(JCR_BASEVERSION);
> + versionableBuilder.removeProperty(JCR_ISCHECKEDOUT);
> + }
> +
> + @Override
> + public Editor childNodeChanged(String name, NodeState before, NodeState after) throws CommitFailedException {
> + return childNodeAdded(name, after);
> + }
> +
> + @Override
> + public Editor childNodeDeleted(String name, NodeState before) throws CommitFailedException {
> + return childNodeAdded(name, null);
> + }
> +
> + @Override
> + public void leave(NodeState before, NodeState after) throws CommitFailedException {
> + this.path = PathUtils.getParentPath(this.path);
> + }
> +
> + private static <T> T getProperty(NodeState state, String name, Type<T> type) {
> + if (state.hasProperty(name)) {
> + return state.getProperty(name).getValue(type);
> + }
> + return null;
> + }
> +
> + private static NodeBuilder getNodeBuilder(NodeBuilder root, String path) {
> + NodeBuilder builder = root;
> + for (String name : PathUtils.elements(path)) {
> + builder = builder.getChildNode(name);
> + }
> + return builder;
> + }
> +
> + private static void addMixin(NodeBuilder builder, String name) {
> + if (builder.hasProperty(JCR_MIXINTYPES)) {
> + final Set<String> mixins = newHashSet(builder.getProperty(JCR_MIXINTYPES).getValue(Type.NAMES));
> + if (mixins.add(name)) {
> + builder.setProperty(nameProperty(JCR_MIXINTYPES, mixins));
> + }
> + } else {
> + builder.setProperty(nameProperty(JCR_MIXINTYPES, of(name)));
> + }
> + }
> +
> + private static void removeMixin(NodeBuilder builder, String name) {
> + if (builder.hasProperty(JCR_MIXINTYPES)) {
> + final Set<String> mixins = newHashSet(builder.getProperty(JCR_MIXINTYPES).getValue(Type.NAMES));
> + if (mixins.remove(name)) {
> + if (mixins.isEmpty()) {
> + builder.removeProperty(JCR_MIXINTYPES);
> + } else {
> + builder.setProperty(nameProperty(JCR_MIXINTYPES, mixins));
> + }
> + }
> + }
> + }
> +}
>
> Modified: jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java?rev=1694498&r1=1694497&r2=1694498&view=diff
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java (original)
> +++ jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java Thu Aug 6 13:57:58 2015
> @@ -47,7 +47,7 @@ import static org.junit.Assert.assertTru
>
> public abstract class AbstractRepositoryUpgradeTest {
>
> - protected static final Credentials CREDENTIALS = new SimpleCredentials("admin", "admin".toCharArray());
> + public static final Credentials CREDENTIALS = new SimpleCredentials("admin", "admin".toCharArray());
>
> private static NodeStore targetNodeStore;
>
>
> Added: jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java?rev=1694498&view=auto
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java (added)
> +++ jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java Thu Aug 6 13:57:58 2015
> @@ -0,0 +1,277 @@
> +/*
> + * 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.core.RepositoryContext;
> +import org.apache.jackrabbit.core.config.RepositoryConfig;
> +import org.apache.jackrabbit.oak.Oak;
> +import org.apache.jackrabbit.oak.jcr.Jcr;
> +import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
> +import org.apache.jackrabbit.oak.spi.state.NodeStore;
> +import org.apache.jackrabbit.oak.upgrade.util.VersionCopyTestUtils.RepositoryUpgradeSetup;
> +import org.junit.AfterClass;
> +import org.junit.Test;
> +
> +import javax.jcr.Node;
> +import javax.jcr.Property;
> +import javax.jcr.PropertyType;
> +import javax.jcr.Repository;
> +import javax.jcr.RepositoryException;
> +import javax.jcr.Session;
> +import javax.jcr.version.VersionManager;
> +
> +import java.io.File;
> +import java.util.Calendar;
> +
> +import static org.junit.Assert.assertEquals;
> +import static org.junit.Assert.assertFalse;
> +import static org.junit.Assert.assertTrue;
> +import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.MIX_REP_VERSIONABLE_PATHS;
> +import static org.apache.jackrabbit.oak.upgrade.util.VersionCopyTestUtils.createVersionableNode;
> +import static org.apache.jackrabbit.oak.upgrade.util.VersionCopyTestUtils.isVersionable;
> +
> +public class CopyVersionHistoryTest extends AbstractRepositoryUpgradeTest {
> +
> + private static final String VERSIONABLES_OLD = "/versionables/old";
> +
> + private static final String VERSIONABLES_OLD_ORPHANED = "/versionables/oldOrphaned";
> +
> + private static final String VERSIONABLES_YOUNG = "/versionables/young";
> +
> + private static final String VERSIONABLES_YOUNG_ORPHANED = "/versionables/youngOrphaned";
> +
> + private static Calendar betweenHistories;
> +
> + /**
> + * Home directory of source repository.
> + */
> + private static File source;
> +
> + private static String oldOrphanedHistory;
> + private static String youngOrphanedHistory;
> + private static String oldHistory;
> + private static String youngHistory;
> +
> + @Override
> + protected void createSourceContent(Repository repository) throws Exception {
> + final Session session = repository.login(CREDENTIALS);
> +
> + oldHistory = createVersionableNode(session, VERSIONABLES_OLD);
> + oldOrphanedHistory = createVersionableNode(session, VERSIONABLES_OLD_ORPHANED);
> + Thread.sleep(10);
> + betweenHistories = Calendar.getInstance();
> + Thread.sleep(10);
> + youngOrphanedHistory = createVersionableNode(session, VERSIONABLES_YOUNG_ORPHANED);
> + youngHistory = createVersionableNode(session, VERSIONABLES_YOUNG);
> +
> + session.getNode(VERSIONABLES_OLD_ORPHANED).remove();
> + session.getNode(VERSIONABLES_YOUNG_ORPHANED).remove();
> + session.save();
> + }
> +
> + @Override
> + protected void doUpgradeRepository(File source, NodeStore target) throws RepositoryException {
> + // abuse this method to capture the source repo directory
> + CopyVersionHistoryTest.source = source;
> + }
> +
> + @AfterClass
> + public static void teardown() {
> + CopyVersionHistoryTest.source = null;
> + }
> +
> + @Test
> + public void copyAllVersions() throws RepositoryException {
> + assert source != null;
> +
> + Session session = performCopy(source, new RepositoryUpgradeSetup() {
> + @Override
> + public void setup(RepositoryUpgrade upgrade) {
> + // copying all versions is enabled by default
> + }
> + });
> + assertTrue(isVersionable(session, VERSIONABLES_OLD));
> + assertTrue(isVersionable(session, VERSIONABLES_YOUNG));
> + assertExisting(session, oldOrphanedHistory, youngOrphanedHistory, oldHistory, youngHistory);
> + assertHasVersionablePath(session, oldHistory, youngHistory);
> + }
> +
> + @Test
> + public void referencedSinceDate() throws RepositoryException {
> + assert source != null;
> +
> + Session session = performCopy(source, new RepositoryUpgradeSetup() {
> + @Override
> + public void setup(RepositoryUpgrade upgrade) {
> + upgrade.setCopyVersions(betweenHistories);
> + }
> + });
> +
> + assertFalse(isVersionable(session, VERSIONABLES_OLD));
> + assertTrue(isVersionable(session, VERSIONABLES_YOUNG));
> + assertMissing(session, oldHistory, oldOrphanedHistory);
> + assertExisting(session, youngHistory, youngOrphanedHistory);
> + assertHasVersionablePath(session, youngHistory);
> + }
> +
> + @Test
> + public void referencedOlderThanOrphaned() throws RepositoryException {
> + assert source != null;
> +
> + Session session = performCopy(source, new RepositoryUpgradeSetup() {
> + @Override
> + public void setup(RepositoryUpgrade upgrade) {
> + upgrade.setCopyOrphanedVersions(betweenHistories);
> + }
> + });
> +
> + assertTrue(isVersionable(session, VERSIONABLES_OLD));
> + assertTrue(isVersionable(session, VERSIONABLES_YOUNG));
> + assertMissing(session, oldOrphanedHistory);
> + assertExisting(session, oldHistory, youngHistory, youngOrphanedHistory);
> + assertHasVersionablePath(session, oldHistory, youngHistory);
> + }
> +
> + @Test
> + public void onlyReferenced() throws RepositoryException {
> + assert source != null;
> +
> + Session session = performCopy(source, new RepositoryUpgradeSetup() {
> + @Override
> + public void setup(RepositoryUpgrade upgrade) {
> + upgrade.setCopyOrphanedVersions(null);
> + }
> + });
> + assertTrue(isVersionable(session, VERSIONABLES_OLD));
> + assertTrue(isVersionable(session, VERSIONABLES_YOUNG));
> + assertMissing(session, oldOrphanedHistory, youngOrphanedHistory);
> + assertExisting(session, oldHistory, youngHistory);
> + assertHasVersionablePath(session, oldHistory, youngHistory);
> + }
> +
> + @Test
> + public void onlyReferencedAfterDate() throws RepositoryException {
> + assert source != null;
> +
> + Session session = performCopy(source, new RepositoryUpgradeSetup() {
> + @Override
> + public void setup(RepositoryUpgrade upgrade) {
> + upgrade.setCopyVersions(betweenHistories);
> + upgrade.setCopyOrphanedVersions(null);
> + }
> + });
> + assertFalse(isVersionable(session, VERSIONABLES_OLD));
> + assertTrue(isVersionable(session, VERSIONABLES_YOUNG));
> + assertMissing(session, oldHistory, oldOrphanedHistory, youngOrphanedHistory);
> + assertExisting(session, youngHistory);
> + assertHasVersionablePath(session, youngHistory);
> + }
> +
> + @Test
> + public void onlyOrphaned() throws RepositoryException {
> + assert source != null;
> +
> + Session session = performCopy(source, new RepositoryUpgradeSetup() {
> + @Override
> + public void setup(RepositoryUpgrade upgrade) {
> + upgrade.setCopyVersions(null);
> + }
> + });
> +
> + assertFalse(isVersionable(session, VERSIONABLES_OLD));
> + assertFalse(isVersionable(session, VERSIONABLES_YOUNG));
> + assertMissing(session, oldHistory, youngHistory, oldOrphanedHistory, youngOrphanedHistory);
> + }
> +
> + @Test
> + public void onlyOrphanedAfterDate() throws RepositoryException {
> + assert source != null;
> +
> + Session session = performCopy(source, new RepositoryUpgradeSetup() {
> + @Override
> + public void setup(RepositoryUpgrade upgrade) {
> + upgrade.setCopyVersions(null);
> + upgrade.setCopyOrphanedVersions(betweenHistories);
> + }
> + });
> +
> + assertFalse(isVersionable(session, VERSIONABLES_OLD));
> + assertFalse(isVersionable(session, VERSIONABLES_YOUNG));
> + assertMissing(session, oldHistory, youngHistory, oldOrphanedHistory, youngOrphanedHistory);
> + }
> +
> + @Test
> + public void dontCopyVersionHistory() throws RepositoryException {
> + assert source != null;
> +
> + Session session = performCopy(source, new RepositoryUpgradeSetup() {
> + @Override
> + public void setup(RepositoryUpgrade upgrade) {
> + upgrade.setCopyVersions(null);
> + upgrade.setCopyOrphanedVersions(null);
> + }
> + });
> +
> + assertFalse(isVersionable(session, VERSIONABLES_OLD));
> + assertFalse(isVersionable(session, VERSIONABLES_YOUNG));
> + assertMissing(session, oldHistory, youngHistory, oldOrphanedHistory, youngOrphanedHistory);
> + }
> +
> + public Session performCopy(File source, RepositoryUpgradeSetup setup) throws RepositoryException {
> + final RepositoryConfig sourceConfig = RepositoryConfig.create(source);
> + final RepositoryContext sourceContext = RepositoryContext.create(sourceConfig);
> + final NodeStore targetNodeStore = new MemoryNodeStore();
> + try {
> + final RepositoryUpgrade upgrade = new RepositoryUpgrade(sourceContext, targetNodeStore);
> + setup.setup(upgrade);
> + upgrade.copy(null);
> + } finally {
> + sourceContext.getRepository().shutdown();
> + }
> +
> + final Repository repository = new Jcr(new Oak(targetNodeStore)).createRepository();
> + return repository.login(AbstractRepositoryUpgradeTest.CREDENTIALS);
> + }
> +
> + private static void assertExisting(final Session session, final String... paths) throws RepositoryException {
> + for (final String path : paths) {
> + final String relPath = path.substring(1);
> + assertTrue("node " + path + " should exist", session.getRootNode().hasNode(relPath));
> + }
> + }
> +
> + private static void assertMissing(final Session session, final String... paths) throws RepositoryException {
> + for (final String path : paths) {
> + final String relPath = path.substring(1);
> + assertFalse("node " + path + " should not exist", session.getRootNode().hasNode(relPath));
> + }
> + }
> +
> + public static void assertHasVersionablePath(final Session session, final String... historyPaths) throws RepositoryException {
> + for (String historyPath : historyPaths) {
> + final String workspaceName = session.getWorkspace().getName();
> + final Node versionHistory = session.getNode(historyPath);
> + assertTrue(versionHistory.isNodeType(MIX_REP_VERSIONABLE_PATHS));
> + assertTrue(versionHistory.hasProperty(workspaceName));
> + final Property pathProperty = versionHistory.getProperty(workspaceName);
> + assertEquals(PropertyType.PATH, pathProperty.getType());
> +
> + final VersionManager vm = session.getWorkspace().getVersionManager();
> + assertEquals(historyPath, vm.getVersionHistory(pathProperty.getString()).getPath());
> + }
> + }
> +}
>
> Added: jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/util/VersionCopyTestUtils.java
> URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/util/VersionCopyTestUtils.java?rev=1694498&view=auto
> ==============================================================================
> --- jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/util/VersionCopyTestUtils.java (added)
> +++ jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/util/VersionCopyTestUtils.java Thu Aug 6 13:57:58 2015
> @@ -0,0 +1,57 @@
> +package org.apache.jackrabbit.oak.upgrade.util;
> +
> +import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.MIX_REP_VERSIONABLE_PATHS;
> +import static org.junit.Assert.assertEquals;
> +import static org.junit.Assert.assertTrue;
> +
> +import java.util.ArrayList;
> +import java.util.List;
> +
> +import javax.jcr.Node;
> +import javax.jcr.Property;
> +import javax.jcr.PropertyType;
> +import javax.jcr.RepositoryException;
> +import javax.jcr.Session;
> +import javax.jcr.version.Version;
> +import javax.jcr.version.VersionHistory;
> +import javax.jcr.version.VersionManager;
> +
> +import org.apache.jackrabbit.JcrConstants;
> +import org.apache.jackrabbit.commons.JcrUtils;
> +import org.apache.jackrabbit.oak.upgrade.RepositoryUpgrade;
> +
> +public class VersionCopyTestUtils {
> +
> + public static String createVersionableNode(Session session, String versionablePath)
> + throws RepositoryException, InterruptedException {
> + final VersionManager versionManager = session.getWorkspace().getVersionManager();
> + final Node versionable = JcrUtils.getOrCreateUniqueByPath(session.getRootNode(), versionablePath,
> + JcrConstants.NT_UNSTRUCTURED);
> + versionable.addMixin("mix:versionable");
> + versionable.setProperty("version", "root");
> + session.save();
> +
> + final String path = versionable.getPath();
> + final List<String> versionNames = new ArrayList<String>();
> + for (int i = 0; i < 3; i++) {
> + versionable.setProperty("version", "1." + i);
> + session.save();
> + final Version v = versionManager.checkpoint(path);
> + versionNames.add(v.getName());
> + }
> +
> + final VersionHistory history = versionManager.getVersionHistory(path);
> + for (final String versionName : versionNames) {
> + history.addVersionLabel(versionName, String.format("version %s", versionName), false);
> + }
> + return history.getPath();
> + }
> +
> + public static boolean isVersionable(Session session, String path) throws RepositoryException {
> + return session.getNode(path).isNodeType(JcrConstants.MIX_VERSIONABLE);
> + }
> +
> + public interface RepositoryUpgradeSetup {
> + void setup(RepositoryUpgrade upgrade);
> + }
> +}
>
>