From f84cecf444f96e13f0d36d7332c8a1d0a5dc7c70 Mon Sep 17 00:00:00 2001 From: Tomasz Rekawek Date: Thu, 11 Aug 2016 14:06:09 +0200 Subject: [PATCH] OAK-4666 Allow to convert normal index data nodes into private ones during the migration --- .../apache/jackrabbit/oak/upgrade/IndexCopier.java | 29 ++--- .../oak/upgrade/RepositorySidegrade.java | 8 +- .../oak/upgrade/cli/MigrationFactory.java | 1 + .../oak/upgrade/cli/parser/MigrationOptions.java | 10 ++ .../upgrade/cli/parser/OptionParserFactory.java | 3 +- .../oak/upgrade/IncludeIndexMountNameTest.java | 131 +++++++++++++++++++++ 6 files changed, 166 insertions(+), 16 deletions(-) create mode 100644 oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/IncludeIndexMountNameTest.java diff --git a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/IndexCopier.java b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/IndexCopier.java index 285c5e5..24f10af 100644 --- a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/IndexCopier.java +++ b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/IndexCopier.java @@ -48,7 +48,7 @@ public final class IndexCopier { * @param targetRoot the target * @param includes indexing data for these paths will be copied */ - public static void copy(NodeState sourceRoot, NodeBuilder targetRoot, Set includes) { + public static void copy(NodeState sourceRoot, NodeBuilder targetRoot, Set includes, String mountName) { NodeState oakIndex = sourceRoot.getChildNode(INDEX_DEFINITIONS_NAME); NodeBuilder targetOakIndex = copySingleNode(oakIndex, targetRoot, INDEX_DEFINITIONS_NAME); @@ -64,14 +64,14 @@ public final class IndexCopier { switch (type) { case "property": if (indexDef.getBoolean(UNIQUE_PROPERTY_NAME)) { - copyUniqueIndex(indexDef, targetIndexDef, includes); + copyUniqueIndex(indexDef, targetIndexDef, includes, mountName); } else { - copyMirrorIndex(indexDef, targetIndexDef, includes); + copyMirrorIndex(indexDef, targetIndexDef, includes, mountName); } break; case "counter": - copyMirrorIndex(indexDef, targetIndexDef, includes); + copyMirrorIndex(indexDef, targetIndexDef, includes, mountName); case "lucene": copyLuceneIndex(indexDef, targetIndexDef, includes); @@ -82,35 +82,32 @@ public final class IndexCopier { } } - private static void copyUniqueIndex(NodeState indexDef, NodeBuilder targetIndexDef, Set includes) { + private static void copyUniqueIndex(NodeState indexDef, NodeBuilder targetIndexDef, Set includes, String mountName) { for (ChildNodeEntry childIndexNode : getIndexNodes(indexDef)) { NodeState indexNode = childIndexNode.getNodeState(); - NodeBuilder targetIndexNode = copySingleNode(indexNode, targetIndexDef, childIndexNode.getName()); + NodeBuilder targetIndexNode = copyIndexDataNode(childIndexNode.getName(), indexNode, targetIndexDef, mountName); - boolean anyAttrCopied = false; for (ChildNodeEntry attr : indexNode.getChildNodeEntries()) { Iterable entries = attr.getNodeState().getStrings("entry"); if (entries != null) { for (String e : entries) { if (startsWithAny(e, includes)) { copySingleNode(attr, targetIndexNode); - anyAttrCopied = true; } } } } - if (!anyAttrCopied) { + if (Iterables.isEmpty(targetIndexNode.getChildNodeNames())) { targetIndexNode.remove(); } } } - private static void copyMirrorIndex(NodeState indexDef, NodeBuilder targetIndexDef, Set includes) { + private static void copyMirrorIndex(NodeState indexDef, NodeBuilder targetIndexDef, Set includes, String mountName) { for (ChildNodeEntry childIndexNode : getIndexNodes(indexDef)) { NodeState indexNode = childIndexNode.getNodeState(); - NodeBuilder targetIndexNode = copySingleNode(indexNode, targetIndexDef, childIndexNode.getName()); + NodeBuilder targetIndexNode = copyIndexDataNode(childIndexNode.getName(), indexNode, targetIndexDef, mountName); - boolean anyAttrCopied = false; for (ChildNodeEntry attr : indexNode.getChildNodeEntries()) { NodeBuilder targetAttr = copySingleNode(attr, targetIndexNode); boolean copied = NodeStateCopier.builder() @@ -119,9 +116,8 @@ public final class IndexCopier { if (!copied) { targetAttr.remove(); } - anyAttrCopied = copied || anyAttrCopied; } - if (!anyAttrCopied) { + if (Iterables.isEmpty(targetIndexNode.getChildNodeNames())) { targetIndexNode.remove(); } } @@ -143,6 +139,11 @@ public final class IndexCopier { .copy(indexDef, targetIndexDef); } + private static NodeBuilder copyIndexDataNode(String name, NodeState srcIndexNode, NodeBuilder targetIndexDef, String mountName) { + String dstName = mountName == null ? name : ":oak:mount-" + mountName + "-index"; + return copySingleNode(srcIndexNode, targetIndexDef, dstName); + } + private static NodeBuilder copySingleNode(ChildNodeEntry source, NodeBuilder targetParent) { return copySingleNode(source.getNodeState(), targetParent, source.getName()); } diff --git a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegrade.java b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegrade.java index a9c9710..606a7c9 100755 --- a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegrade.java +++ b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegrade.java @@ -93,6 +93,8 @@ public class RepositorySidegrade { private boolean includeIndex = false; + private String includeIndexMountName = null; + private boolean filterLongNames = true; private boolean skipInitialization = false; @@ -193,6 +195,10 @@ public class RepositorySidegrade { this.includeIndex = includeIndex; } + public void setIncludeIndexMountName(String includeIndexMountName) { + this.includeIndexMountName = includeIndexMountName; + } + /** * Sets the paths that should be merged when the source repository * is copied to the target repository. @@ -279,7 +285,7 @@ public class RepositorySidegrade { copyWorkspace(sourceRoot, targetRoot); if (includeIndex) { - IndexCopier.copy(sourceRoot, targetRoot, includePaths); + IndexCopier.copy(sourceRoot, targetRoot, includePaths, includeIndexMountName); } boolean isRemoveCheckpointReferences = false; diff --git a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/MigrationFactory.java b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/MigrationFactory.java index 360de24..cc9d1ce 100644 --- a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/MigrationFactory.java +++ b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/MigrationFactory.java @@ -118,6 +118,7 @@ public class MigrationFactory { sidegrade.setFilterLongNames(stores.getSrcType().isSupportLongNames() && !stores.getDstType().isSupportLongNames()); sidegrade.setSkipInitialization(options.isSkipInitialization()); sidegrade.setIncludeIndex(options.isIncludeIndex()); + sidegrade.setIncludeIndexMountName(options.getIncludeIndexMountName()); return sidegrade; } diff --git a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/MigrationOptions.java b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/MigrationOptions.java index cd1d1b5..e45092b 100644 --- a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/MigrationOptions.java +++ b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/MigrationOptions.java @@ -48,6 +48,8 @@ public class MigrationOptions { private final boolean includeIndex; + private final String includeIndexMountName; + private final boolean failOnError; private final boolean earlyShutdown; @@ -83,6 +85,7 @@ public class MigrationOptions { this.excludePaths = split(args.getOption(OptionParserFactory.EXCLUDE_PATHS)); this.mergePaths = split(args.getOption(OptionParserFactory.MERGE_PATHS)); this.includeIndex = args.hasOption(OptionParserFactory.INCLUDE_INDEX); + this.includeIndexMountName = args.getOption(OptionParserFactory.INCLUDE_INDEX); this.failOnError = args.hasOption(OptionParserFactory.FAIL_ON_ERROR); this.earlyShutdown = args.hasOption(OptionParserFactory.EARLY_SHUTDOWN); this.skipInitialization = args.hasOption(OptionParserFactory.SKIP_INIT); @@ -143,6 +146,10 @@ public class MigrationOptions { return includeIndex; } + public String getIncludeIndexMountName() { + return includeIndexMountName; + } + public boolean isIgnoreMissingBinaries() { return ignoreMissingBinaries; } @@ -196,6 +203,9 @@ public class MigrationOptions { if (includeIndex) { log.info("Index data for the paths {} will be copied", (Object) includePaths); + if (includeIndexMountName != null) { + log.info("Mount name to be used by index data is {}", includeIndexMountName); + } } if (ignoreMissingBinaries) { diff --git a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/OptionParserFactory.java b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/OptionParserFactory.java index 9d7b04f..75a50d6 100644 --- a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/OptionParserFactory.java +++ b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/cli/parser/OptionParserFactory.java @@ -127,7 +127,8 @@ public class OptionParserFactory { .ofType(String.class); op.accepts(MERGE_PATHS, "Comma-separated list of paths to merge during copy.").withRequiredArg() .ofType(String.class); - op.accepts(INCLUDE_INDEX, "Copy index data for paths specified in the " + INCLUDE_PATHS + " option"); + op.accepts(INCLUDE_INDEX, "Copy index data for paths specified in the " + INCLUDE_PATHS + " option. Optional argument defines " + + "the mount name").withOptionalArg().ofType(String.class); } private static void addVersioningOptions(OptionParser op) { diff --git a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/IncludeIndexMountNameTest.java b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/IncludeIndexMountNameTest.java new file mode 100644 index 0000000..203dd4f --- /dev/null +++ b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/IncludeIndexMountNameTest.java @@ -0,0 +1,131 @@ +/* + * 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 com.google.common.base.Joiner; +import org.apache.jackrabbit.oak.commons.IOUtils; +import org.apache.jackrabbit.oak.commons.PathUtils; +import org.apache.jackrabbit.oak.spi.state.NodeState; +import org.apache.jackrabbit.oak.spi.state.NodeStore; +import org.apache.jackrabbit.oak.upgrade.cli.AbstractOak2OakTest; +import org.apache.jackrabbit.oak.upgrade.cli.OakUpgrade; +import org.apache.jackrabbit.oak.upgrade.cli.container.BlobStoreContainer; +import org.apache.jackrabbit.oak.upgrade.cli.container.FileDataStoreContainer; +import org.apache.jackrabbit.oak.upgrade.cli.container.MongoNodeStoreContainer; +import org.apache.jackrabbit.oak.upgrade.cli.container.NodeStoreContainer; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.RepositoryException; +import java.io.IOException; + +import static junit.framework.TestCase.assertTrue; +import static org.apache.jackrabbit.oak.upgrade.cli.container.MongoNodeStoreContainer.isMongoAvailable; +import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeTrue; + +public class IncludeIndexMountNameTest extends AbstractOak2OakTest { + + private static final Logger log = LoggerFactory.getLogger(IncludeIndexMountNameTest.class); + + private final BlobStoreContainer blob; + + private final NodeStoreContainer source; + + private final NodeStoreContainer destination; + + private NodeStore nodeStore; + + @Before + public void prepare() throws Exception { + NodeStore source = getSourceContainer().open(); + try { + initContent(source); + } finally { + getSourceContainer().close(); + } + + String[] args = getArgs(); + log.info("oak2oak {}", Joiner.on(' ').join(args)); + OakUpgrade.main(args); + + nodeStore = destination.open(); + } + + @After + public void clean() throws IOException { + IOUtils.closeQuietly(getDestinationContainer()); + getDestinationContainer().clean(); + getSourceContainer().clean(); + } + + public IncludeIndexMountNameTest() throws IOException { + assumeTrue(isMongoAvailable()); + blob = new FileDataStoreContainer(); + source = new MongoNodeStoreContainer(blob); + destination = new MongoNodeStoreContainer(blob); + } + + @Override + protected NodeStoreContainer getSourceContainer() { + return source; + } + + @Override + protected NodeStoreContainer getDestinationContainer() { + return destination; + } + + @Override + protected String[] getArgs() { + return new String[] { "--src-datastore", blob.getDescription(), "--copy-versions=false", "--skip-init", "--include-index=private", "--include-paths=/apps,/libs", source.getDescription(), destination.getDescription() }; + } + + @Test + public void validateMigration() throws RepositoryException, IOException { + assertNodeExists("/oak:index/nodetype/:oak:mount-private-index/nt%3Afile/libs/sling/xss/config.xml"); + assertNodeExists("/oak:index/uuid/:oak:mount-private-index/" + getUuid("/apps/repl/components/repl/repl.html/jcr:content")); + + assertNodeMissing("/oak:index/nodetype/:index/nt%3Afile/sling.css"); + assertNodeMissing("/oak:index/uuid/:index/" + getUuid("/index.html/jcr:content")); + } + + private String getUuid(String path) { + return getNode(path).getString("jcr:uuid"); + } + + private void assertNodeMissing(String path) { + assertFalse(getNode(path).exists()); + } + + private void assertNodeExists(String path) { + assertTrue(getNode(path).exists()); + } + + private NodeState getNode(String path) { + NodeState ns = nodeStore.getRoot(); + for (String element : PathUtils.elements(path)) { + ns = ns.getChildNode(element); + } + return ns; + } +} \ No newline at end of file -- 2.2.1