You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ba...@apache.org on 2015/08/06 15:57:59 UTC
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...
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);
+ }
+}
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...
Posted by Julian Sedding <js...@gmail.com>.
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);
> + }
> +}
>
>