You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2022/03/15 09:51:48 UTC
[sling-org-apache-sling-feature-cpconverter] 03/04: SLING-11134 - Extract Oak index definitions and package them as an additional file
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-cpconverter.git
commit 4439a9f0e455df61c67852ce3173264ea4517825
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Tue Mar 1 14:33:57 2022 +0200
SLING-11134 - Extract Oak index definitions and package them as an additional file
- add an IndexManager that coordinates the various components working on index definitions
parsing and storage
- add an IndexDefinitionsEntryHandler that is able to parse Oak index definitions and store them
for later use in the IndexManager
- add add IndexDefinitionsJsonWriter that ouputs the index definitions in a format known to the
oak-run tool
- wire the IndexManager to the ContentPackage2FeatureModelConverter and store the discovered index
definitions in a feature model extension
---
pom.xml | 4 +-
.../ContentPackage2FeatureModelConverter.java | 16 +-
...ntentPackage2FeatureModelConverterLauncher.java | 2 +
.../features/DefaultFeaturesManager.java | 24 ++-
.../cpconverter/features/FeaturesManager.java | 2 +
.../handlers/IndexDefinitionsEntryHandler.java | 128 ++++++++++++
.../cpconverter/index/DefaultIndexManager.java | 52 +++++
.../cpconverter/index/IndexDefinitions.java | 163 +++++++++++++++
.../index/IndexDefinitionsJsonWriter.java | 154 ++++++++++++++
.../feature/cpconverter/index/IndexManager.java | 60 ++++++
.../cpconverter/vltpkg/JcrNamespaceRegistry.java | 35 ++--
...sling.feature.cpconverter.handlers.EntryHandler | 1 +
.../feature/cpconverter/AdjustedFilterTest.java | 4 +-
.../ContentPackage2FeatureModelConverterTest.java | 4 +-
.../ConverterUserAndPermissionTest.java | 2 +
.../handlers/IndexDefinitionsEntryHandlerTest.java | 223 +++++++++++++++++++++
.../index/IndexDefinitionsJsonWriterTest.java | 190 ++++++++++++++++++
.../index_multiple_files/META-INF/vault/filter.xml | 21 ++
.../META-INF/vault/nodetypes.cnd | 25 +++
.../META-INF/vault/properties.xml | 34 ++++
.../jcr_root/_oak_index/baz/.content.xml | 24 +++
.../jcr_root/_oak_index/lucene_custom/.content.xml | 64 ++++++
.../index_nested_tika/META-INF/vault/filter.xml | 20 ++
.../index_nested_tika/META-INF/vault/nodetypes.cnd | 24 +++
.../META-INF/vault/properties.xml | 34 ++++
.../index/index_nested_tika/jcr_root/.content.xml | 21 ++
.../jcr_root/_oak_index/.content.xml | 89 ++++++++
.../_oak_index/lucene-custom/tika/config.xml | 26 +++
.../lucene-custom/tika/config.xml.dir/.content.xml | 24 +++
.../index_single_file/META-INF/vault/filter.xml | 20 ++
.../index_single_file/META-INF/vault/nodetypes.cnd | 25 +++
.../META-INF/vault/properties.xml | 34 ++++
.../index/index_single_file/jcr_root/.content.xml | 32 +++
.../jcr_root/_oak_index/.content.xml | 35 ++++
34 files changed, 1590 insertions(+), 26 deletions(-)
diff --git a/pom.xml b/pom.xml
index 945207d..40c17c7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,7 +35,7 @@
<sling.java.version>8</sling.java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<picocli.version>3.6.0</picocli.version>
- <org.apache.jackrabbit.vault.version>3.4.10</org.apache.jackrabbit.vault.version>
+ <org.apache.jackrabbit.vault.version>3.6.0</org.apache.jackrabbit.vault.version>
<jackrabbit-api.version>2.18.4</jackrabbit-api.version>
<jackrabbit-spi-commons.version>2.18.4</jackrabbit-spi-commons.version>
<mockito-core.version>3.2.0</mockito-core.version>
@@ -253,7 +253,7 @@
<dependency>
<groupId>org.apache.jackrabbit.vault</groupId>
<artifactId>vault-validation</artifactId>
- <version>3.4.6</version>
+ <version>3.6.0</version>
<scope>compile</scope>
</dependency>
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java b/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
index c077ab0..f6c5507 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
@@ -57,6 +57,7 @@ import org.apache.sling.feature.cpconverter.handlers.EntryHandler;
import org.apache.sling.feature.cpconverter.handlers.EntryHandlersManager;
import org.apache.sling.feature.cpconverter.handlers.NodeTypesEntryHandler;
import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.BundleSlingInitialContentExtractor;
+import org.apache.sling.feature.cpconverter.index.IndexManager;
import org.apache.sling.feature.cpconverter.vltpkg.BaseVaultPackageScanner;
import org.apache.sling.feature.cpconverter.vltpkg.PackagesEventsEmitter;
import org.apache.sling.feature.cpconverter.vltpkg.RecollectorVaultPackageScanner;
@@ -110,6 +111,8 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
private BundleSlingInitialContentExtractor bundleSlingInitialContentExtractor = new BundleSlingInitialContentExtractor();
+ private IndexManager indexManager;
+
public enum PackagePolicy {
/**
* References the content package in the feature model and deploys via the {@link ContentPackage2FeatureModelConverter#artifactsDeployer}
@@ -236,6 +239,15 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
return this;
}
+ public @Nullable IndexManager getIndexManager() {
+ return indexManager;
+ }
+
+ public @NotNull ContentPackage2FeatureModelConverter setIndexManager(IndexManager indexManager) {
+ this.indexManager = indexManager;
+ return this;
+ }
+
public @NotNull File getTempDirectory() {
return this.tmpDirectory;
}
@@ -315,6 +327,7 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
aclManager.addRepoinitExtension(assemblers, featuresManager);
bundleSlingInitialContentExtractor.addRepoInitExtension(assemblers, featuresManager);
+ indexManager.addRepoinitExtension(featuresManager);
logger.info("Conversion complete!");
@@ -326,6 +339,7 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
aclManager.reset();
bundleSlingInitialContentExtractor.reset();
+ indexManager.reset();
assemblers.clear();
try {
@@ -379,7 +393,7 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
// Please note: THIS IS A HACK to meet the new requirement without drastically change the original design
// temporary swap the main handler to collect stuff
VaultPackageAssembler handler = getMainPackageAssembler();
-
+
Properties parentProps = handler.getPackageProperties();
boolean isContainerPackage = PackageType.CONTAINER.equals(parentProps.get(PackageProperties.NAME_PACKAGE_TYPE));
setMainPackageAssembler(clonedPackage);
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java b/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java
index 59098fb..14948ff 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java
@@ -38,6 +38,7 @@ import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager;
import org.apache.sling.feature.cpconverter.filtering.RegexBasedResourceFilter;
import org.apache.sling.feature.cpconverter.handlers.DefaultEntryHandlersManager;
import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.BundleSlingInitialContentExtractor;
+import org.apache.sling.feature.cpconverter.index.DefaultIndexManager;
import org.apache.sling.feature.cpconverter.shared.ConverterConstants;
import org.apache.sling.feature.cpconverter.vltpkg.DefaultPackagesEventsEmitter;
import org.apache.sling.feature.io.json.FeatureJSONReader;
@@ -217,6 +218,7 @@ public final class ContentPackage2FeatureModelConverterLauncher implements Runna
.setBundleSlingInitialContentExtractor(bundleSlingInitialContentExtractor)
.setEntryHandlersManager(new DefaultEntryHandlersManager(entryHandlerConfigsMap, !disableInstallerPolicy, slingInitialContentPolicy, bundleSlingInitialContentExtractor, systemUserRelPath))
.setAclManager(aclManager)
+ .setIndexManager(new DefaultIndexManager())
.setEmitter(DefaultPackagesEventsEmitter.open(featureModelsOutputDirectory))
.setFailOnMixedPackages(failOnMixedPackages)
.setContentTypePackagePolicy(contentTypePackagePolicy);
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/features/DefaultFeaturesManager.java b/src/main/java/org/apache/sling/feature/cpconverter/features/DefaultFeaturesManager.java
index c29a9fb..3eb9e6d 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/features/DefaultFeaturesManager.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/features/DefaultFeaturesManager.java
@@ -50,6 +50,7 @@ import org.apache.sling.feature.Feature;
import org.apache.sling.feature.cpconverter.ConverterException;
import org.apache.sling.feature.cpconverter.accesscontrol.AclManager;
import org.apache.sling.feature.cpconverter.accesscontrol.Mapping;
+import org.apache.sling.feature.cpconverter.index.IndexManager;
import org.apache.sling.feature.cpconverter.interpolator.SimpleVariablesInterpolator;
import org.apache.sling.feature.cpconverter.interpolator.VariablesInterpolator;
import org.apache.sling.feature.cpconverter.repoinit.NoOpVisitor;
@@ -328,7 +329,7 @@ public class DefaultFeaturesManager implements FeaturesManager, PackagesEventsEm
}
return false;
}
-
+
private List<String> convertMappings(@Nullable String[] mappings, @NotNull String pid, boolean enforceServiceMappingByPrincipal) throws ConverterException {
if (mappings == null) {
return Collections.emptyList();
@@ -393,8 +394,8 @@ public class DefaultFeaturesManager implements FeaturesManager, PackagesEventsEm
adjustConfigurationProperties(configuration, configurationProperties);
}
-
- private void adjustConfigurationProperties(@NotNull Configuration configuration,
+
+ private void adjustConfigurationProperties(@NotNull Configuration configuration,
@NotNull Dictionary<String, Object> configurationProperties) {
Enumeration<String> keys = configurationProperties.keys();
while (keys.hasMoreElements()) {
@@ -538,7 +539,22 @@ public class DefaultFeaturesManager implements FeaturesManager, PackagesEventsEm
repoInitExtension.setText(repoInitExtension.getText().concat(System.lineSeparator()).concat(text));
}
}
-
+
+ @Override
+ public void addOrAppendOakIndexDefinitionsExtension(String source, String text)
+ throws IOException, ConverterException {
+
+ Extension oakIndexDefsExtension = getRunMode(null).getExtensions().getByName(IndexManager.EXTENSION_NAME);
+ if (oakIndexDefsExtension == null) {
+ oakIndexDefsExtension = new Extension(ExtensionType.JSON, IndexManager.EXTENSION_NAME, ExtensionState.REQUIRED);
+ getRunMode(null).getExtensions().add(oakIndexDefsExtension);
+ oakIndexDefsExtension.setJSON(text);
+ } else {
+ oakIndexDefsExtension.setJSON(oakIndexDefsExtension.getText().concat(System.lineSeparator()).concat(text));
+ }
+
+ }
+
private static void checkReferences(@NotNull final Dictionary<String, Object> configurationProperties, @NotNull final String pid) throws ConverterException {
final String[] references = Converters.standardConverter().convert(configurationProperties.get("references")).to(String[].class);
if (references != null && references.length > 0) {
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/features/FeaturesManager.java b/src/main/java/org/apache/sling/feature/cpconverter/features/FeaturesManager.java
index 13e0377..028d1d8 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/features/FeaturesManager.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/features/FeaturesManager.java
@@ -70,6 +70,8 @@ public interface FeaturesManager {
void addOrAppendRepoInitExtension(@NotNull String source, @NotNull String text, @Nullable String runMode)
throws IOException, ConverterException;
+ void addOrAppendOakIndexDefinitionsExtension(String source, String text) throws IOException, ConverterException;
+
@NotNull
Map<String, String> getNamespaceUriByPrefix();
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/IndexDefinitionsEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/IndexDefinitionsEntryHandler.java
new file mode 100644
index 0000000..b0770f8
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/IndexDefinitionsEntryHandler.java
@@ -0,0 +1,128 @@
+/*
+ * 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.sling.feature.cpconverter.handlers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Optional;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.util.Text;
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.jackrabbit.vault.fs.io.DocViewParser;
+import org.apache.jackrabbit.vault.fs.io.DocViewParser.XmlParseException;
+import org.apache.jackrabbit.vault.fs.io.DocViewParserHandler;
+import org.apache.jackrabbit.vault.util.DocViewNode2;
+import org.apache.jackrabbit.vault.util.PlatformNameFormat;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.apache.sling.feature.cpconverter.ConverterException;
+import org.apache.sling.feature.cpconverter.index.IndexDefinitions;
+import org.apache.sling.feature.cpconverter.index.IndexManager;
+import org.jetbrains.annotations.NotNull;
+import org.xml.sax.InputSource;
+
+/**
+ * Handler for Jackrabbit Oak index definitions
+ *
+ * <p>This implementation scans content packages for entries stored under <tt>/oak:index</tt>
+ * and exposes them to the {@link IndexManager} for further processing.
+ *
+ */
+public class IndexDefinitionsEntryHandler extends AbstractRegexEntryHandler {
+
+ private final class IndexDefinitionsParserHandler implements DocViewParserHandler {
+ private final WorkspaceFilter filter;
+ private IndexDefinitions definitions;
+
+ public IndexDefinitionsParserHandler(WorkspaceFilter filter, IndexDefinitions definitions) {
+ this.filter = filter;
+ this.definitions = definitions;
+ }
+
+ @Override
+ public void startDocViewNode(@NotNull String nodePath, @NotNull DocViewNode2 docViewNode,
+ @NotNull Optional<DocViewNode2> parentDocViewNode, int line, int column)
+ throws IOException, RepositoryException {
+
+ if ( nodePath.startsWith(IndexDefinitions.OAK_INDEX_PATH) && filter.contains(nodePath) ) {
+ definitions.addNode(Text.getRelativeParent(nodePath, 1), docViewNode);
+ }
+ }
+
+ @Override
+ public void endDocViewNode(@NotNull String nodePath, @NotNull DocViewNode2 docViewNode,
+ @NotNull Optional<DocViewNode2> parentDocViewNode, int line, int column)
+ throws IOException, RepositoryException {
+ // nothing to do
+ }
+
+ @Override
+ public void startPrefixMapping(String prefix, String uri) {
+ definitions.registerPrefixMapping(prefix, uri);
+ }
+ }
+
+ public IndexDefinitionsEntryHandler() {
+ super("/jcr_root/" + PlatformNameFormat.getPlatformName(IndexDefinitions.OAK_INDEX_NAME)+ "/.*(/)?/*.xml");
+ }
+
+ @Override
+ public void handle(@NotNull String path, @NotNull Archive archive, @NotNull Entry entry,
+ @NotNull ContentPackage2FeatureModelConverter converter) throws IOException, ConverterException {
+
+ IndexManager indexManager = converter.getIndexManager();
+ if ( indexManager == null ) {
+ logger.info("{} not present, will skip index definition extraction", IndexManager.class.getName());
+ } else {
+ try (InputStream is = archive.openInputStream(entry)) {
+
+ String platformPath = path.replaceAll("^/jcr_root", "")
+ .replaceAll("/\\.content\\.xml$", "")
+ .replace(".dir", "");
+ String repositoryPath = PlatformNameFormat.getRepositoryPath(platformPath);
+ InputSource inputSource = new InputSource(is);
+
+ boolean isDocView = false;
+ // DocViewParser.isDocView closes the input stream it is passed
+ try ( InputStream isCheck = archive.openInputStream(entry) ) {
+ isDocView = DocViewParser.isDocView(new InputSource(isCheck));
+ }
+ if ( isDocView ) {
+ DocViewParser parser = new DocViewParser();
+ IndexDefinitionsParserHandler handler = new IndexDefinitionsParserHandler(archive.getMetaInf().getFilter(), indexManager.getIndexes());
+
+ parser.parse(repositoryPath, inputSource, handler);
+
+ } else {
+ // binary file, should we attach?
+ if ( archive.getMetaInf().getFilter().contains(repositoryPath)) {
+ indexManager.getIndexes().registerBinary(repositoryPath, is);
+ }
+ }
+
+
+ } catch (XmlParseException e) {
+ throw new ConverterException("Failed parsing the index definitions", e);
+ }
+ }
+
+ converter.getMainPackageAssembler().addEntry(path, archive, entry);
+ }
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/index/DefaultIndexManager.java b/src/main/java/org/apache/sling/feature/cpconverter/index/DefaultIndexManager.java
new file mode 100644
index 0000000..df68e01
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/index/DefaultIndexManager.java
@@ -0,0 +1,52 @@
+/*
+ * 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.sling.feature.cpconverter.index;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.sling.feature.cpconverter.ConverterException;
+import org.apache.sling.feature.cpconverter.features.FeaturesManager;
+
+public class DefaultIndexManager implements IndexManager {
+
+ private IndexDefinitions indexDefinitions = new IndexDefinitions();
+ private IndexDefinitionsJsonWriter writer = new IndexDefinitionsJsonWriter(indexDefinitions);
+
+ @Override
+ public void addRepoinitExtension(FeaturesManager features) throws IOException, ConverterException {
+
+ if ( indexDefinitions.getIndexes().isEmpty() )
+ return;
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writer.writeAsJson(out);
+ features.addOrAppendOakIndexDefinitionsExtension("content-package", out.toString(StandardCharsets.UTF_8.toString()));
+ }
+
+ @Override
+ public IndexDefinitions getIndexes() {
+ return indexDefinitions;
+ }
+
+ @Override
+ public void reset() {
+ indexDefinitions = new IndexDefinitions();
+ writer = new IndexDefinitionsJsonWriter(indexDefinitions);
+ }
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/index/IndexDefinitions.java b/src/main/java/org/apache/sling/feature/cpconverter/index/IndexDefinitions.java
new file mode 100644
index 0000000..300e42c
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/index/IndexDefinitions.java
@@ -0,0 +1,163 @@
+/*
+ * 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.sling.feature.cpconverter.index;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.vault.util.DocViewNode2;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Holds information about discovered index definitions
+ *
+ */
+public class IndexDefinitions {
+
+ public static final String OAK_INDEX_NAME = "oak:index";
+ public static final String OAK_INDEX_PATH = "/" + OAK_INDEX_NAME; // NOSONAR - java:S1075 does not apply as this is not a filesystem path
+
+ private final Map<String, List<DocViewNode2>> children = new HashMap<>();
+ private final Map<String, byte[]> binaries = new HashMap<>();
+ private Map<String, String> prefixesToUris = new HashMap<>();
+ private Map<String, String> urisToPrefixes = new HashMap<>();
+
+ public void addNode(@NotNull String parentPath, @NotNull DocViewNode2 node) {
+ List<DocViewNode2> currentChildren = children.computeIfAbsent(parentPath, k -> new ArrayList<>());
+ DocViewNode2 existing = null;
+ for ( DocViewNode2 currentChild : currentChildren ) {
+
+ // prevent duplicates
+ if ( currentChild.getName().equals(node.getName() )) {
+ // new node holds less information. There should not be a scenario where we need to
+ // merge properties.
+ if ( node.getProperties().size() <= currentChild.getProperties().size() ) {
+ return;
+ }
+
+ existing = currentChild;
+ }
+ }
+
+ // remove node marked as placeholder
+ if ( existing != null ) {
+ currentChildren.remove(existing);
+ }
+
+ // add new node
+ currentChildren.add(node);
+ }
+
+ public @NotNull List<DocViewNode2> getIndexes() {
+ return getChildren(OAK_INDEX_PATH);
+ }
+
+ public @NotNull List<DocViewNode2> getChildren(@NotNull String parentPath) {
+ return children.getOrDefault(parentPath, Collections.emptyList());
+ }
+
+
+ /**
+ * Returns a name in compact format
+ *
+ * <p>Maps a fully qualified {@link Name name}, e.g. ['http://jackrabbit.apache.org/oak/ns/1.0','index'] to a compact name
+ * like <tt>oak:index</tt></p>
+ *
+ * @param name The name to map
+ * @return the compact name
+ */
+ public @NotNull String toShortName(@NotNull Name name) {
+ if ( name.getNamespaceURI().length() == 0 )
+ return name.getLocalName();
+ return urisToPrefixes.get(name.getNamespaceURI()) + ":" + name.getLocalName();
+ }
+
+ /**
+ * Registers a binary entry at the specified repository path
+ *
+ * <p>The input stream may be fully read into memory, and therefore is not expected to be unreasonably large.</p>
+ *
+ * <p>The input stream will be fully read, but not closed.</p>
+ *
+ * @param repositoryPath The JCR repository path where the binary was found
+ * @param is the input stream for the binary
+ * @throws IOException in case of I/O problems
+ */
+ public void registerBinary(@NotNull String repositoryPath, @NotNull InputStream is) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ IOUtils.copy(is, out);
+ binaries.put(repositoryPath, out.toByteArray());
+ }
+
+ /**
+ * Returns a potential binary registered for a repository path
+ *
+ * @param repositoryPath the path of the repository
+ * @return an optional wrapping the binary data, possibly {@link Optional#empty() empty}
+ */
+ public @NotNull Optional<byte[]> getBinary(@NotNull String repositoryPath) {
+ return Optional.ofNullable(binaries.get(repositoryPath));
+ }
+
+ /**
+ * Registers a prefix mapping for a specified uri
+ *
+ * @param prefix the prefix
+ * @param uri the uri
+ */
+ public void registerPrefixMapping(@NotNull String prefix, @NotNull String uri) {
+ prefixesToUris.put(prefix, uri);
+ urisToPrefixes.put(uri, prefix);
+ }
+
+ /**
+ * Dumps a compact representation of the data
+ *
+ * <p>Useful for debugging purposes only</p>
+ *
+ * @param out the PrintStream to use
+ */
+ public void dump(@NotNull PrintStream out) {
+ out.println("---------");
+ out.println(OAK_INDEX_NAME);
+ dumpChildren(out, OAK_INDEX_PATH);
+ out.println("---------");
+ }
+
+ private void dumpChildren(PrintStream out, String parentPath) {
+
+ StringBuilder padding = new StringBuilder();
+ int depth = parentPath.split("/").length - 1;
+ for ( int i = 0 ; i < 2 * depth; i++)
+ padding.append(' ');
+
+ for ( DocViewNode2 node : children.getOrDefault(parentPath, Collections.emptyList()) ) {
+ out.println(padding.toString() + toShortName(node.getName()));
+ dumpChildren(out, parentPath + '/' + node.getName().getLocalName());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/index/IndexDefinitionsJsonWriter.java b/src/main/java/org/apache/sling/feature/cpconverter/index/IndexDefinitionsJsonWriter.java
new file mode 100644
index 0000000..a14fab5
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/index/IndexDefinitionsJsonWriter.java
@@ -0,0 +1,154 @@
+/*
+ * 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.sling.feature.cpconverter.index;
+
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+
+import javax.jcr.PropertyType;
+import javax.json.Json;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonValue;
+import javax.json.stream.JsonGenerator;
+
+import org.apache.jackrabbit.util.Base64;
+import org.apache.jackrabbit.vault.util.DocViewNode2;
+import org.apache.jackrabbit.vault.util.DocViewProperty2;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Writes index definitions in a JSON format that can be consumed by the <tt>oak-run</tt> tool.
+ *
+ * @see <a href=
+ * "https://jackrabbit.apache.org/oak/docs/query/oak-run-indexing.html">Oak-Run
+ * indexing</a>
+ */
+public class IndexDefinitionsJsonWriter {
+
+ private static final Function<String, JsonValue> BLOB_MAPPER = s -> Json.createValue(":blobid:" + Base64.encode(s));
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final IndexDefinitions indexDefinitions;
+
+ public IndexDefinitionsJsonWriter(@NotNull IndexDefinitions indexDefinitions) {
+ this.indexDefinitions = indexDefinitions;
+ }
+
+ /**
+ * Writes the index definitions to the specified <tt>out</tt>
+ *
+ * @param out the output stream to write to
+ */
+ public void writeAsJson(@NotNull OutputStream out) {
+ try ( JsonGenerator root = Json.createGenerator(out) ) {
+ root.writeStartObject();
+ for ( DocViewNode2 index : indexDefinitions.getIndexes() ) {
+ write(root, index, IndexDefinitions.OAK_INDEX_PATH);
+ }
+ root.writeEnd(); // end object declaration
+ }
+ }
+
+ private void write(JsonGenerator json, DocViewNode2 index, String parentPath) {
+
+ String nodeName = indexDefinitions.toShortName(index.getName());
+ String objectKey = parentPath.equals(IndexDefinitions.OAK_INDEX_PATH) ?
+ IndexDefinitions.OAK_INDEX_PATH + "/" + nodeName : nodeName;
+
+ // 1. start object
+ json.writeStartObject(objectKey);
+
+ // 2. write properties
+ for ( DocViewProperty2 property : index.getProperties() ) {
+
+ String propertyName = indexDefinitions.toShortName(property.getName());
+
+ switch ( property.getType() ) {
+ case PropertyType.STRING:
+ case PropertyType.UNDEFINED:
+ write(json, propertyName, property.getStringValues(), s -> Json.createValue("str:" + s ));
+ break;
+ case PropertyType.LONG:
+ write(json, propertyName, property.getStringValues(), s -> Json.createValue(Long.parseLong(s) ));
+ break;
+ case PropertyType.BOOLEAN:
+ write(json, propertyName, property.getStringValues(), s -> ( Boolean.parseBoolean(s) ? JsonValue.TRUE : JsonValue.FALSE) );
+ break;
+ case PropertyType.NAME:
+ write(json, propertyName, property.getStringValues(), s -> Json.createValue("nam:" + s ));
+ break;
+ case PropertyType.DOUBLE:
+ write(json, propertyName, property.getStringValues(), s -> Json.createValue(Double.parseDouble(s) ));
+ break;
+ case PropertyType.DATE:
+ write(json, propertyName, property.getStringValues(), s -> Json.createValue("dat:" + s) );
+ break;
+ case PropertyType.PATH:
+ write(json, propertyName, property.getStringValues(), s -> Json.createValue("pat:" + s) );
+ break;
+ case PropertyType.URI:
+ write(json, propertyName, property.getStringValues(), s -> Json.createValue("uri:" + s) );
+ break;
+ case PropertyType.BINARY:
+ write(json, propertyName, property.getStringValues(), BLOB_MAPPER );
+ break;
+ default:
+ logger.warn("Skipping property {}, don't know how to handle type {}; values: {}", property.getName(), property.getType(), property.getStringValues());
+
+ }
+ }
+
+ // 3. write nt:data entries for nt:resource children of nt:files
+ // in this case, this is the nt:resource node
+ Optional<byte[]> binary = indexDefinitions.getBinary(parentPath);
+ if ( binary.isPresent() ) {
+ String blobAsString = new String(binary.get(), StandardCharsets.UTF_8);
+ write(json, "jcr:data", Collections.singletonList(blobAsString), BLOB_MAPPER);
+ };
+
+ // 4. write children
+ String nodePath = parentPath + "/" + nodeName; // NOSONAR - java:S1075 does not apply as this is not a filesystem path
+ for ( DocViewNode2 child : indexDefinitions.getChildren(nodePath)) {
+ write(json, child, nodePath);
+ }
+
+ // 5. end object
+ json.writeEnd();
+ }
+
+ private void write(JsonGenerator json, String propertyName, List<String> propertyValues, Function<String, JsonValue> mapper) {
+ if ( propertyValues.size() == 1 ) {
+ json.write(propertyName, mapper.apply(propertyValues.get(0)));
+ return;
+ }
+
+ JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+ propertyValues.stream()
+ .map( mapper )
+ .forEach( arrayBuilder::add );
+
+ json.write(propertyName, arrayBuilder.build());
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/index/IndexManager.java b/src/main/java/org/apache/sling/feature/cpconverter/index/IndexManager.java
new file mode 100644
index 0000000..1f4573f
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/index/IndexManager.java
@@ -0,0 +1,60 @@
+/*
+ * 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.sling.feature.cpconverter.index;
+
+import java.io.IOException;
+
+import org.apache.sling.feature.cpconverter.ConverterException;
+import org.apache.sling.feature.cpconverter.features.FeaturesManager;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Point of entry for logic related to handling Oak indexes
+ *
+ * @see <a href=
+ * "https://jackrabbit.apache.org/oak/docs/query/oak-run-indexing.html">Oak-Run
+ * indexing</a>
+ */
+public interface IndexManager {
+
+ public static final String EXTENSION_NAME = "oak-index-definitions";
+
+ /**
+ * Returns the index definitions managed by this instance
+ *
+ * <p>The returned object may be used to record data discovered about oak indexes</p>
+ *
+ * @return the index definitions
+ */
+ @NotNull IndexDefinitions getIndexes();
+
+ /**
+ * Records the Oak index data using the features manager
+ *
+ * <p>The index definitions will be recoreded as a JSON repoinit extension named {@value #EXTENSION_NAME} .</p>
+ *
+ * @param features
+ * @throws IOException
+ * @throws ConverterException
+ */
+ void addRepoinitExtension(FeaturesManager features) throws IOException, ConverterException;
+
+ /**
+ * Resets the internal state
+ */
+ void reset();
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/JcrNamespaceRegistry.java b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/JcrNamespaceRegistry.java
index e1ea0b5..d88f63e 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/JcrNamespaceRegistry.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/JcrNamespaceRegistry.java
@@ -16,37 +16,38 @@
*/
package org.apache.sling.feature.cpconverter.vltpkg;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFactory;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.xml.namespace.NamespaceContext;
+
import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.commons.SimpleValueFactory;
import org.apache.jackrabbit.commons.cnd.CndImporter;
import org.apache.jackrabbit.commons.cnd.ParseException;
import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
-import org.apache.jackrabbit.vault.validation.spi.impl.nodetype.NodeTypeManagerProvider;
+import org.apache.jackrabbit.vault.util.StandaloneManagerProvider;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.jcr.NamespaceException;
-import javax.jcr.NamespaceRegistry;
-import javax.jcr.RepositoryException;
-import javax.jcr.ValueFactory;
-import javax.jcr.nodetype.NodeTypeManager;
-import javax.xml.namespace.NamespaceContext;
-import java.io.IOException;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-
/** Simple namespace registry backed by a map */
public class JcrNamespaceRegistry implements NamespaceRegistry, NamespaceResolver, NamespaceContext {
-
+
private final Collection<String> registeredCndSystemIds = new ArrayList<>();
- private final NodeTypeManagerProvider ntManagerProvider = new NodeTypeManagerProvider();
+ private final StandaloneManagerProvider ntManagerProvider = new StandaloneManagerProvider();
private final NodeTypeManager ntManager = ntManagerProvider.getNodeTypeManager();
private final Logger logger = LoggerFactory.getLogger(JcrNamespaceRegistry.class);
-
+
public JcrNamespaceRegistry() throws RepositoryException, ParseException, IOException {
ntManagerProvider.registerNamespace(PREFIX_XML, NAMESPACE_XML);
ntManagerProvider.registerNamespace("sling", "http://sling.apache.org/jcr/sling/1.0");
diff --git a/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.handlers.EntryHandler b/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.handlers.EntryHandler
index 46fb089..b8ad4c8 100644
--- a/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.handlers.EntryHandler
+++ b/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.handlers.EntryHandler
@@ -2,6 +2,7 @@ org.apache.sling.feature.cpconverter.handlers.BundleEntryHandler
org.apache.sling.feature.cpconverter.handlers.ConfigurationEntryHandler
org.apache.sling.feature.cpconverter.handlers.ContentPackageEntryHandler
org.apache.sling.feature.cpconverter.handlers.GroupEntryHandler
+org.apache.sling.feature.cpconverter.handlers.IndexDefinitionsEntryHandler
org.apache.sling.feature.cpconverter.handlers.JsonConfigurationEntryHandler
org.apache.sling.feature.cpconverter.handlers.NodeTypesEntryHandler
org.apache.sling.feature.cpconverter.handlers.PrivilegesHandler
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/AdjustedFilterTest.java b/src/test/java/org/apache/sling/feature/cpconverter/AdjustedFilterTest.java
index 0792378..86fd426 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/AdjustedFilterTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/AdjustedFilterTest.java
@@ -27,6 +27,7 @@ import org.apache.sling.feature.cpconverter.features.FeaturesManager;
import org.apache.sling.feature.cpconverter.handlers.DefaultEntryHandlersManager;
import org.apache.sling.feature.cpconverter.handlers.EntryHandlersManager;
import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.BundleSlingInitialContentExtractor;
+import org.apache.sling.feature.cpconverter.index.DefaultIndexManager;
import org.apache.sling.feature.cpconverter.shared.ConverterConstants;
import org.apache.sling.feature.cpconverter.vltpkg.DefaultPackagesEventsEmitter;
import org.jetbrains.annotations.NotNull;
@@ -59,7 +60,8 @@ public class AdjustedFilterTest extends AbstractConverterTest {
converter = new ContentPackage2FeatureModelConverter()
.setEntryHandlersManager(handlersManager)
- .setAclManager(aclManager);
+ .setAclManager(aclManager)
+ .setIndexManager(new DefaultIndexManager());
outputDirectory = new File(System.getProperty("java.io.tmpdir"), getClass().getName() + '_' + System.currentTimeMillis());
FeaturesManager featuresManager = new DefaultFeaturesManager(true, 5, outputDirectory, null, null, null, aclManager);
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java b/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
index 4aea947..f4e0441 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
@@ -68,6 +68,7 @@ import org.apache.sling.feature.cpconverter.filtering.RegexBasedResourceFilter;
import org.apache.sling.feature.cpconverter.handlers.DefaultEntryHandlersManager;
import org.apache.sling.feature.cpconverter.handlers.EntryHandlersManager;
import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.BundleSlingInitialContentExtractor;
+import org.apache.sling.feature.cpconverter.index.DefaultIndexManager;
import org.apache.sling.feature.cpconverter.shared.ConverterConstants;
import org.apache.sling.feature.cpconverter.vltpkg.DefaultPackagesEventsEmitter;
import org.apache.sling.feature.io.json.FeatureJSONReader;
@@ -100,7 +101,8 @@ public class ContentPackage2FeatureModelConverterTest extends AbstractConverterT
converter = new ContentPackage2FeatureModelConverter()
.setEntryHandlersManager(handlersManager)
.setFeaturesManager(new DefaultFeaturesManager(new File("")))
- .setAclManager(new DefaultAclManager());
+ .setAclManager(new DefaultAclManager())
+ .setIndexManager(new DefaultIndexManager());
}
@After
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/ConverterUserAndPermissionTest.java b/src/test/java/org/apache/sling/feature/cpconverter/ConverterUserAndPermissionTest.java
index 6ff8aea..9e46b80 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/ConverterUserAndPermissionTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/ConverterUserAndPermissionTest.java
@@ -35,6 +35,7 @@ import org.apache.sling.feature.cpconverter.features.FeaturesManager;
import org.apache.sling.feature.cpconverter.handlers.DefaultEntryHandlersManager;
import org.apache.sling.feature.cpconverter.handlers.EntryHandlersManager;
import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.BundleSlingInitialContentExtractor;
+import org.apache.sling.feature.cpconverter.index.DefaultIndexManager;
import org.apache.sling.feature.cpconverter.shared.ConverterConstants;
import org.apache.sling.feature.cpconverter.vltpkg.DefaultPackagesEventsEmitter;
import org.apache.sling.feature.io.json.FeatureJSONReader;
@@ -146,6 +147,7 @@ public class ConverterUserAndPermissionTest extends AbstractConverterTest {
converter = new ContentPackage2FeatureModelConverter()
.setEntryHandlersManager(handlersManager)
.setAclManager(aclManager)
+ .setIndexManager(new DefaultIndexManager())
.setContentTypePackagePolicy(PackagePolicy.REFERENCE);
outputDirectory = new File(System.getProperty("java.io.tmpdir"), getClass().getName() + '_' + System.currentTimeMillis());
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/IndexDefinitionsEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/IndexDefinitionsEntryHandlerTest.java
new file mode 100644
index 0000000..6bf7c55
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/IndexDefinitionsEntryHandlerTest.java
@@ -0,0 +1,223 @@
+/*
+ * 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.sling.feature.cpconverter.handlers;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+
+import javax.jcr.NamespaceRegistry;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.jackrabbit.vault.fs.io.FileArchive;
+import org.apache.jackrabbit.vault.packaging.impl.ZipVaultPackage;
+import org.apache.jackrabbit.vault.util.DocViewNode2;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.apache.sling.feature.cpconverter.ConverterException;
+import org.apache.sling.feature.cpconverter.index.DefaultIndexManager;
+import org.apache.sling.feature.cpconverter.index.IndexDefinitions;
+import org.apache.sling.feature.cpconverter.vltpkg.BaseVaultPackageScanner;
+import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
+import org.assertj.core.api.Condition;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+public class IndexDefinitionsEntryHandlerTest {
+
+ @Test
+ public void matches() {
+ IndexDefinitionsEntryHandler handler = new IndexDefinitionsEntryHandler();
+ assertThat(handler.matches("/jcr_root/_oak_index/.content.xml")).isTrue();
+ assertThat(handler.matches("/jcr_root/_oak_index/bar/.content.xml")).isTrue();
+ assertThat(handler.matches("/jcr_root/_oak_index/lucene/tika/config.xml")).isTrue();
+ assertThat(handler.matches("/jcr_root/_oak_index/.vlt")).isFalse();
+ assertThat(handler.matches("/jcr_root/apps/_oak_index/.content.xml")).isFalse();
+ }
+
+ @Test
+ public void handleSingleFileIndexDefinition() throws IOException, ConverterException {
+
+ DefaultIndexManager manager = new DefaultIndexManager();
+
+ traverseForIndexing(manager, "index_single_file");
+
+ IndexDefinitions defs = manager.getIndexes();
+ List<DocViewNode2> indexes = defs.getIndexes();
+
+ assertThat(indexes).as("index definitions")
+ .hasSize(1)
+ .element(0)
+ .has( Conditions.localName("foo") )
+ .has( Conditions.property("type", "property") );
+
+ }
+
+ @Test
+ public void handleMultiFileIndexDefinition() throws IOException, ConverterException {
+
+ DefaultIndexManager manager = new DefaultIndexManager();
+
+ traverseForIndexing(manager, "index_multiple_files");
+
+ IndexDefinitions defs = manager.getIndexes();
+ List<DocViewNode2> indexes = defs.getIndexes();
+
+ assertThat(indexes).as("index definitions")
+ .hasSize(2);
+
+ assertThat(indexes).as("baz index")
+ .element(0).has( Conditions.localName("baz") );
+ assertThat(indexes).as("lucene_custom index")
+ .element(1)
+ .has( Conditions.localName("lucene_custom") )
+ .has( Conditions.property("type", "lucene") )
+ .has(Conditions.childWithLocalName("/oak:index/lucene_custom", "indexRules", defs));
+
+ }
+
+ @Test
+ public void handleIndexDefinitionWithNestedTikaXml() throws IOException, ConverterException, ParserConfigurationException, SAXException {
+ DefaultIndexManager manager = new DefaultIndexManager();
+
+ traverseForIndexing(manager, "index_nested_tika");
+
+ IndexDefinitions defs = manager.getIndexes();
+ List<DocViewNode2> indexes = defs.getIndexes();
+
+ assertThat(indexes).as("index definitions")
+ .hasSize(1)
+ .element(0)
+ .has(Conditions.localName("lucene-custom"));
+
+ DocViewNode2 luceneCustom = indexes.get(0);
+ assertThat(luceneCustom).as("lucene index definition")
+ .has(Conditions.childWithLocalName("/oak:index/lucene-custom", "indexRules", defs))
+ .has(Conditions.childWithLocalName("/oak:index/lucene-custom", "tika", defs));
+
+ List<DocViewNode2> luceneCustomChildren = defs.getChildren("/oak:index/lucene-custom");
+ assertThat(luceneCustomChildren).as("lucene index definition children")
+ .hasSize(2);
+
+ DocViewNode2 tikaConfigNode = luceneCustomChildren.stream()
+ .filter( c -> c.getName().getLocalName().equals("tika") )
+ .findFirst()
+ .get();
+
+ assertThat(tikaConfigNode).as("tika config node")
+ .has(Conditions.childWithLocalName("/oak:index/lucene-custom/tika","config.xml", defs));
+
+ List<DocViewNode2> children = defs.getChildren("/oak:index/lucene-custom/tika");
+ assertThat(children).as("tika config child nodes")
+ .hasSize(1)
+ .element(0)
+ .has( Conditions.localName("config.xml") )
+ .has( Conditions.property(NamespaceRegistry.NAMESPACE_JCR, "primaryType", "nt:file", defs) );
+
+ byte[] tikaConfig = defs.getBinary("/oak:index/lucene-custom/tika/config.xml").get();
+ assertIsValidXml(tikaConfig);
+ }
+
+
+ private void assertIsValidXml(byte[] tikeConfig) throws ParserConfigurationException, SAXException, IOException {
+
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder documentBuilder = dbFactory.newDocumentBuilder();
+ documentBuilder.parse(new InputSource(new ByteArrayInputStream(tikeConfig)));
+ }
+
+ private void traverseForIndexing(DefaultIndexManager manager, String testPackageDirectory) throws IOException, ConverterException {
+
+ try ( Archive archive = new FileArchive(TestUtils.getPackageRelativeFile(getClass(), "index", testPackageDirectory)) ) {
+ archive.open(true);
+
+ try ( ContentPackage2FeatureModelConverter converter = new ContentPackage2FeatureModelConverter() ) {
+
+ converter.setMainPackageAssembler(Mockito.mock(VaultPackageAssembler.class))
+ .setIndexManager(manager);
+ IndexDefinitionsEntryHandler handler = new IndexDefinitionsEntryHandler();
+
+ new BaseVaultPackageScanner(true) {
+ @Override
+ protected void onFile(@NotNull String path, @NotNull Archive archive, @NotNull Entry entry)
+ throws IOException, ConverterException {
+ if ( handler.matches(path) )
+ handler.handle(path, archive, entry, converter);
+ }
+ }.traverse(new ZipVaultPackage(archive, true));
+ }
+
+ }
+ }
+
+ static class Conditions {
+ static final Condition<DocViewNode2> localName(String localName) {
+ return new Condition<DocViewNode2>("Node with name " + localName) {
+ @Override
+ public boolean matches(DocViewNode2 value) {
+ return value.getName().getLocalName().equals(localName);
+ }
+ };
+ }
+
+ static final Condition<DocViewNode2> childWithLocalName(String path, String childName, IndexDefinitions defs) {
+ return new Condition<DocViewNode2>("Node with a child with localName " + childName) {
+ @Override
+ public boolean matches(DocViewNode2 value) {
+ return defs.getChildren(path).stream().filter( n -> n.getName().getLocalName().equals(childName)).findAny().isPresent();
+ }
+ };
+ }
+
+ static final Condition<DocViewNode2> property(String localPropertyName, String propertyValue) {
+ return new Condition<DocViewNode2>("Node with property '" + localPropertyName + "' equal to '" + propertyValue + "'") {
+ @Override
+ public boolean matches(DocViewNode2 value) {
+ return value.getProperties().stream().
+ anyMatch( p -> {
+ return p.getName().getLocalName().equals(localPropertyName)
+ && p.getStringValue().isPresent() && Objects.equals(p.getStringValue().get(), propertyValue);
+ });
+ }
+ };
+ }
+
+ static final Condition<DocViewNode2> property(String uri, String localPropertyName, String propertyValue, IndexDefinitions defs) {
+ return new Condition<DocViewNode2>("Node with property '{" + uri +"}" + localPropertyName + "' equal to '" + propertyValue + "'") {
+ @Override
+ public boolean matches(DocViewNode2 value) {
+ return value.getProperties().stream().
+ anyMatch( p -> {
+ return p.getName().getLocalName().equals(localPropertyName)
+ && Objects.equals(p.getName().getNamespaceURI(), uri)
+ && p.getStringValue().isPresent() && Objects.equals(p.getStringValue().get(), propertyValue);
+ });
+ }
+ };
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/index/IndexDefinitionsJsonWriterTest.java b/src/test/java/org/apache/sling/feature/cpconverter/index/IndexDefinitionsJsonWriterTest.java
new file mode 100644
index 0000000..a68ccad
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/cpconverter/index/IndexDefinitionsJsonWriterTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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.sling.feature.cpconverter.index;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.PropertyType;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import javax.json.stream.JsonParser;
+
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
+import org.apache.jackrabbit.util.Base64;
+import org.apache.jackrabbit.vault.util.DocViewNode2;
+import org.apache.jackrabbit.vault.util.DocViewProperty2;
+import org.assertj.core.api.Condition;
+import org.junit.Before;
+import org.junit.Test;
+
+public class IndexDefinitionsJsonWriterTest {
+
+ // copied from oak-spi-core/NamespaceConstants to not add a dependency on Oak
+ private static final String PREFIX_OAK = "oak";
+ private static final String NAMESPACE_OAK = "http://jackrabbit.apache.org/oak/ns/1.0";
+
+ private NameFactory nameFactory;
+ private IndexDefinitions definitions;
+
+ @Before
+ public void setUp() {
+ nameFactory = NameFactoryImpl.getInstance();
+ definitions = new IndexDefinitions();
+ definitions.registerPrefixMapping(NamespaceRegistry.PREFIX_NT, NamespaceRegistry.NAMESPACE_NT);
+ definitions.registerPrefixMapping(NamespaceRegistry.PREFIX_JCR, NamespaceRegistry.NAMESPACE_JCR);
+ definitions.registerPrefixMapping(PREFIX_OAK, NAMESPACE_OAK);
+
+ }
+
+ @Test
+ public void emptyInput() throws IOException {
+ JsonObject root = generateAndParse(definitions);
+ assertThat(root).as("index definitions").isEmpty();
+ }
+
+ @Test
+ public void propertyIndexDefinition() throws IOException {
+
+ Collection<DocViewProperty2> fooProps = new ArrayList<>();
+ fooProps.add(new DocViewProperty2(nameFactory.create("{}type"), "property"));
+ fooProps.add(new DocViewProperty2(nameFactory.create(NamespaceRegistry.NAMESPACE_JCR, "primaryType"), PREFIX_OAK+":QueryIndexDefinition"));
+ fooProps.add(new DocViewProperty2(nameFactory.create("{}reindex"), Boolean.FALSE.toString(), PropertyType.BOOLEAN));
+ fooProps.add(new DocViewProperty2(nameFactory.create("{}reindexCount"), "1", PropertyType.LONG));
+
+ definitions.addNode("/oak:index", new DocViewNode2(nameFactory.create("{}foo"), fooProps));
+
+ Collection<DocViewProperty2> barProps = new ArrayList<>();
+ fooProps.add(new DocViewProperty2(nameFactory.create("{}type"), "property"));
+ fooProps.add(new DocViewProperty2(nameFactory.create(NamespaceRegistry.NAMESPACE_JCR, "primaryType"), PREFIX_OAK+":QueryIndexDefinition"));
+ fooProps.add(new DocViewProperty2(nameFactory.create("{}reindex"), Boolean.TRUE.toString(), PropertyType.BOOLEAN));
+ fooProps.add(new DocViewProperty2(nameFactory.create("{}reindexCount"), "25", PropertyType.LONG));
+
+ definitions.addNode("/oak:index", new DocViewNode2(nameFactory.create("{}bar"), barProps));
+
+ JsonObject root = generateAndParse(definitions);
+ assertThat(root).as("indexDefinitions")
+ .hasSize(2)
+ .hasEntrySatisfying("/oak:index/foo", Conditions.isJsonObject())
+ .hasEntrySatisfying("/oak:index/bar", Conditions.isJsonObject());
+
+ JsonObject fooIndex = root.getJsonObject("/oak:index/foo");
+ assertThat(fooIndex).as("foo index")
+ .hasSize(4)
+ .contains(entry("type", Json.createValue("str:property")))
+ .contains(entry("jcr:primaryType", Json.createValue("nam:oak:QueryIndexDefinition")))
+ .contains(entry("reindex", JsonObject.FALSE))
+ .contains(entry("reindexCount", Json.createValue(1)));
+ }
+
+ @Test
+ public void luceneIndexDefinitionWithTikaConfig() throws IOException {
+
+ String configXmlFileContents = "<properties/>";
+
+ // lucene index
+ Collection<DocViewProperty2> luceneProps = new ArrayList<>();
+ luceneProps.add(new DocViewProperty2(nameFactory.create("{}type"), "lucene"));
+ luceneProps.add(new DocViewProperty2(nameFactory.create(NamespaceRegistry.NAMESPACE_JCR, "primaryType"), PREFIX_OAK+":QueryIndexDefinition"));
+ luceneProps.add(new DocViewProperty2(nameFactory.create("{}reindex"), Boolean.FALSE.toString(), PropertyType.BOOLEAN));
+ luceneProps.add(new DocViewProperty2(nameFactory.create("{}reindexCount"), "1", PropertyType.LONG));
+ luceneProps.add(new DocViewProperty2(nameFactory.create("{}includePropertyTypes"), Arrays.asList("String", "Binary"), PropertyType.STRING));
+
+ definitions.addNode("/oak:index", new DocViewNode2(nameFactory.create("{}lucene"), luceneProps));
+
+ // index rules node
+ List<DocViewProperty2> indexRulesProps = Collections.singletonList(new DocViewProperty2(nameFactory.create(NamespaceRegistry.NAMESPACE_JCR, "primaryType"), "nt:unstructured"));
+
+ definitions.addNode("/oak:index/lucene", new DocViewNode2(nameFactory.create("{}indexRules"), indexRulesProps));
+
+ // tika node
+ List<DocViewProperty2> tikaProps = Collections.singletonList(new DocViewProperty2(nameFactory.create(NamespaceRegistry.NAMESPACE_JCR, "primaryType"), "nt:unstructured"));
+
+ definitions.addNode("/oak:index/lucene", new DocViewNode2(nameFactory.create("{}tika"), tikaProps));
+
+ // tika config.xml node
+ List<DocViewProperty2> configXmlProps = Collections.singletonList(new DocViewProperty2(nameFactory.create(NamespaceRegistry.NAMESPACE_JCR, "primaryType"), "nt:file"));
+
+ definitions.addNode("/oak:index/lucene/tika", new DocViewNode2(nameFactory.create("{}config.xml"), configXmlProps));
+ definitions.registerBinary("/oak:index/lucene/tika/config.xml", new ByteArrayInputStream(configXmlFileContents.getBytes(StandardCharsets.UTF_8)));
+
+ // tika config.xml jcr:content node
+ List<DocViewProperty2> jcrContentProps = Collections.singletonList(new DocViewProperty2(nameFactory.create(NamespaceRegistry.NAMESPACE_JCR, "primaryType"), "nt:resource"));
+ definitions.addNode("/oak:index/lucene/tika/config.xml", new DocViewNode2(nameFactory.create(NamespaceRegistry.NAMESPACE_JCR, "resource"), jcrContentProps));
+
+ JsonObject root = generateAndParse(definitions);
+ System.out.println(root);
+
+ assertThat(root).as("root index")
+ .hasEntrySatisfying("/oak:index/lucene", Conditions.isJsonObject());
+
+ JsonObject lucene = root.getJsonObject("/oak:index/lucene");
+ assertThat(lucene).as("lucene index")
+ .hasEntrySatisfying("tika", Conditions.isJsonObject());
+
+ JsonObject tika = lucene.getJsonObject("tika");
+ assertThat(tika).as("tika node index")
+ .hasEntrySatisfying("config.xml", Conditions.isJsonObject());
+
+ JsonObject configNode = tika.getJsonObject("config.xml");
+ assertThat(configNode).as("config node")
+ .hasEntrySatisfying("jcr:resource", Conditions.isJsonObject());
+
+ JsonObject jcrResource = configNode.getJsonObject("jcr:resource");
+ JsonString binaryEntry = jcrResource.getJsonString("jcr:data");
+ assertThat(binaryEntry).as("config.xml blob")
+ .hasFieldOrPropertyWithValue("string", ":blobid:" + Base64.encode(configXmlFileContents));
+ }
+
+
+ private JsonObject generateAndParse(IndexDefinitions definitions) throws IOException {
+
+ IndexDefinitionsJsonWriter writer = new IndexDefinitionsJsonWriter(definitions);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writer.writeAsJson(out);
+
+ JsonParser parser = Json.createParser(new ByteArrayInputStream(out.toByteArray()));
+ JsonObject root = parser.getObject();
+ return root;
+ }
+
+ static class Conditions {
+ public static Condition<JsonValue> isJsonObject() {
+ return new Condition<JsonValue>("Is a " + JsonObject.class.getSimpleName()) {
+ @Override
+ public boolean matches(JsonValue value) {
+ return value instanceof JsonObject;
+ }
+ };
+ }
+ }
+
+}
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/META-INF/vault/filter.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/META-INF/vault/filter.xml
new file mode 100644
index 0000000..9e1e22c
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/META-INF/vault/filter.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<workspaceFilter version="1.0">
+ <filter root="/oak:index/baz"/>
+ <filter root="/oak:index/lucene_custom"/>
+</workspaceFilter>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/META-INF/vault/nodetypes.cnd b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/META-INF/vault/nodetypes.cnd
new file mode 100644
index 0000000..29762da
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/META-INF/vault/nodetypes.cnd
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+<'sling'='http://sling.apache.org/jcr/sling/1.0'>
+<'nt'='http://www.jcp.org/jcr/nt/1.0'>
+
+[sling:Folder] > nt:folder
+ - * (undefined)
+ - * (undefined) multiple
+ + * (nt:base) = sling:Folder version
+
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/META-INF/vault/properties.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/META-INF/vault/properties.xml
new file mode 100644
index 0000000..8a7b22d
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/META-INF/vault/properties.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!--
+ 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.
+ -->
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties>
+<comment>FileVault Package Properties</comment>
+<entry key="createdBy">admin</entry>
+<entry key="name">index_multiple_files</entry>
+<entry key="lastModified">2011-11-15T09:45:14.664+01:00</entry>
+<entry key="lastModifiedBy">admin</entry>
+<entry key="created">2011-11-15T09:45:14.685+01:00</entry>
+<entry key="buildCount">1</entry>
+<entry key="version"/>
+<entry key="dependencies"/>
+<entry key="packageFormatVersion">2</entry>
+<entry key="description"/>
+<entry key="lastWrapped">2011-11-15T09:45:14.664+01:00</entry>
+<entry key="group"/>
+<entry key="lastWrappedBy">admin</entry>
+</properties>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/jcr_root/_oak_index/baz/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/jcr_root/_oak_index/baz/.content.xml
new file mode 100644
index 0000000..2296ed9
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/jcr_root/_oak_index/baz/.content.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<jcr:root xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:rep="internal"
+ jcr:primaryType="oak:QueryIndexDefinition"
+ propertyNames="[foo]"
+ reindex="{Boolean}false"
+ reindexCount="{Long}1"
+ type="property">
+</jcr:root>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/jcr_root/_oak_index/lucene_custom/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/jcr_root/_oak_index/lucene_custom/.content.xml
new file mode 100644
index 0000000..cbb532c
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/jcr_root/_oak_index/lucene_custom/.content.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<jcr:root xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+ jcr:primaryType="oak:QueryIndexDefinition"
+ async="[async]"
+ includePropertyTypes="[String,Binary]"
+ reindex="{Boolean}false"
+ reindexCount="{Long}1"
+ seed="{Long}4516265077047968953"
+ type="lucene">
+ <indexRules
+ jcr:primaryType="nt:unstructured">
+ <nt:base
+ jcr:primaryType="nt:unstructured"
+ includePropertyTypes="[String,Binary]">
+ <properties
+ jcr:primaryType="nt:unstructured">
+ <sling:alias
+ jcr:primaryType="nt:unstructured"
+ index="{Boolean}false"
+ name="sling:alias"/>
+ <jcr:lastmodifiedby
+ jcr:primaryType="nt:unstructured"
+ index="{Boolean}false"
+ name="jcr:lastmodifiedby"/>
+ <sling:resourcetype
+ jcr:primaryType="nt:unstructured"
+ index="{Boolean}false"
+ name="sling:resourcetype"/>
+ <jcr:createdby
+ jcr:primaryType="nt:unstructured"
+ index="{Boolean}false"
+ name="jcr:createdby"/>
+ <sling:vanitypath
+ jcr:primaryType="nt:unstructured"
+ index="{Boolean}false"
+ name="sling:vanitypath"/>
+ <prop0
+ jcr:primaryType="nt:unstructured"
+ analyzed="{Boolean}true"
+ isRegexp="{Boolean}true"
+ name="^[^\\/]*$"
+ nodeScopeIndex="{Boolean}true"
+ propertyIndex="{Boolean}false"
+ useInExcerpt="{Boolean}true"/>
+ </properties>
+ </nt:base>
+ </indexRules>
+</jcr:root>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/META-INF/vault/filter.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/META-INF/vault/filter.xml
new file mode 100644
index 0000000..1da236d
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/META-INF/vault/filter.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<workspaceFilter version="1.0">
+ <filter root="/oak:index/lucene-custom"/>
+</workspaceFilter>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/META-INF/vault/nodetypes.cnd b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/META-INF/vault/nodetypes.cnd
new file mode 100644
index 0000000..82c6bd6
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/META-INF/vault/nodetypes.cnd
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+<'sling'='http://sling.apache.org/jcr/sling/1.0'>
+<'nt'='http://www.jcp.org/jcr/nt/1.0'>
+
+[sling:Folder] > nt:folder
+ - * (undefined)
+ - * (undefined) multiple
+ + * (nt:base) = sling:Folder version
+
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/META-INF/vault/properties.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/META-INF/vault/properties.xml
new file mode 100644
index 0000000..1032a7f
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/META-INF/vault/properties.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!--
+ 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.
+ -->
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties>
+<comment>FileVault Package Properties</comment>
+<entry key="createdBy">admin</entry>
+<entry key="name">index_single_file</entry>
+<entry key="lastModified">2011-11-15T09:45:14.664+01:00</entry>
+<entry key="lastModifiedBy">admin</entry>
+<entry key="created">2011-11-15T09:45:14.685+01:00</entry>
+<entry key="buildCount">1</entry>
+<entry key="version"/>
+<entry key="dependencies"/>
+<entry key="packageFormatVersion">2</entry>
+<entry key="description"/>
+<entry key="lastWrapped">2011-11-15T09:45:14.664+01:00</entry>
+<entry key="group"/>
+<entry key="lastWrappedBy">admin</entry>
+</properties>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/.content.xml
new file mode 100644
index 0000000..741d31d
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/.content.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+ jcr:primaryType="rep:root"
+ sling:resourceType="sling:redirect"
+ sling:target="/starter.html"/>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/_oak_index/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/_oak_index/.content.xml
new file mode 100644
index 0000000..5db0614
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/_oak_index/.content.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+ jcr:primaryType="nt:unstructured">
+ <jcrLanguage/>
+ <event.job.topic/>
+ <extensionType/>
+ <slingeventEventId/>
+ <repMembers/>
+ <counter/>
+ <acPrincipalName/>
+ <uuid/>
+ <slingVanityPath/>
+ <jcrLockOwner/>
+ <status/>
+ <type/>
+ <slingResource/>
+ <nodetype/>
+ <reference/>
+ <lucene-custom
+ jcr:primaryType="oak:QueryIndexDefinition"
+ async="async"
+ includePropertyTypes="[String,Binary]"
+ reindex="{Boolean}false"
+ reindexCount="{Long}3"
+ seed="{Long}8337987644672197141"
+ type="lucene">
+ <indexRules jcr:primaryType="nt:unstructured">
+ <nt:base
+ jcr:primaryType="nt:unstructured"
+ includePropertyTypes="[String,Binary]">
+ <properties jcr:primaryType="nt:unstructured">
+ <sling:alias
+ jcr:primaryType="nt:unstructured"
+ index="{Boolean}false"
+ name="sling:alias"/>
+ <jcr:lastmodifiedby
+ jcr:primaryType="nt:unstructured"
+ index="{Boolean}false"
+ name="jcr:lastmodifiedby"/>
+ <sling:resourcetype
+ jcr:primaryType="nt:unstructured"
+ index="{Boolean}false"
+ name="sling:resourcetype"/>
+ <jcr:createdby
+ jcr:primaryType="nt:unstructured"
+ index="{Boolean}false"
+ name="jcr:createdby"/>
+ <sling:vanitypath
+ jcr:primaryType="nt:unstructured"
+ index="{Boolean}false"
+ name="sling:vanitypath"/>
+ <prop0
+ jcr:primaryType="nt:unstructured"
+ analyzed="{Boolean}true"
+ isRegexp="{Boolean}true"
+ name="^[^\\/]*$"
+ nodeScopeIndex="{Boolean}true"
+ propertyIndex="{Boolean}false"
+ useInExcerpt="{Boolean}true"/>
+ </properties>
+ </nt:base>
+ </indexRules>
+ <tika jcr:primaryType="nt:unstructured">
+ <config.xml/>
+ </tika>
+ </lucene-custom>
+ <lockCreated/>
+ <principalName/>
+ <lucene/>
+ <slingAlias/>
+ <authorizableId/>
+ <slingResourceType/>
+</jcr:root>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/_oak_index/lucene-custom/tika/config.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/_oak_index/lucene-custom/tika/config.xml
new file mode 100644
index 0000000..fa7c2be
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/_oak_index/lucene-custom/tika/config.xml
@@ -0,0 +1,26 @@
+<!--
+ 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.
+ -->
+<properties>
+ <parsers>
+ <parser class="org.apache.tika.parser.EmptyParser">
+ <mime>application/zip</mime>
+ <mime>application/msword</mime>
+ <mime>application/vnd.ms-excel</mime>
+ <mime>application/pdf</mime>
+ </parser>
+ </parsers>
+</properties>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/_oak_index/lucene-custom/tika/config.xml.dir/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/_oak_index/lucene-custom/tika/config.xml.dir/.content.xml
new file mode 100644
index 0000000..0436956
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/_oak_index/lucene-custom/tika/config.xml.dir/.content.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
+ jcr:primaryType="nt:file">
+ <jcr:content
+ jcr:lastModifiedBy="admin"
+ jcr:mimeType="application/xml"
+ jcr:primaryType="nt:resource"/>
+</jcr:root>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/META-INF/vault/filter.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/META-INF/vault/filter.xml
new file mode 100644
index 0000000..43772b3
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/META-INF/vault/filter.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<workspaceFilter version="1.0">
+ <filter root="/oak:index/foo"/>
+</workspaceFilter>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/META-INF/vault/nodetypes.cnd b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/META-INF/vault/nodetypes.cnd
new file mode 100644
index 0000000..29762da
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/META-INF/vault/nodetypes.cnd
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+<'sling'='http://sling.apache.org/jcr/sling/1.0'>
+<'nt'='http://www.jcp.org/jcr/nt/1.0'>
+
+[sling:Folder] > nt:folder
+ - * (undefined)
+ - * (undefined) multiple
+ + * (nt:base) = sling:Folder version
+
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/META-INF/vault/properties.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/META-INF/vault/properties.xml
new file mode 100644
index 0000000..1032a7f
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/META-INF/vault/properties.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!--
+ 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.
+ -->
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties>
+<comment>FileVault Package Properties</comment>
+<entry key="createdBy">admin</entry>
+<entry key="name">index_single_file</entry>
+<entry key="lastModified">2011-11-15T09:45:14.664+01:00</entry>
+<entry key="lastModifiedBy">admin</entry>
+<entry key="created">2011-11-15T09:45:14.685+01:00</entry>
+<entry key="buildCount">1</entry>
+<entry key="version"/>
+<entry key="dependencies"/>
+<entry key="packageFormatVersion">2</entry>
+<entry key="description"/>
+<entry key="lastWrapped">2011-11-15T09:45:14.664+01:00</entry>
+<entry key="group"/>
+<entry key="lastWrappedBy">admin</entry>
+</properties>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/jcr_root/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/jcr_root/.content.xml
new file mode 100644
index 0000000..fd08de2
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/jcr_root/.content.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal"
+ jcr:mixinTypes="[rep:AccessControllable]"
+ jcr:primaryType="rep:root"
+ sling:resourceType="sling:redirect"
+ sling:target="/index.html">
+ <rep:policy/>
+ <jcr:system/>
+ <var/>
+ <libs/>
+ <etc/>
+ <apps/>
+ <content/>
+ <tmp/>
+ <home/>
+</jcr:root>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/jcr_root/_oak_index/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/jcr_root/_oak_index/.content.xml
new file mode 100644
index 0000000..3f1e4c7
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/jcr_root/_oak_index/.content.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<jcr:root xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:rep="internal"
+ jcr:mixinTypes="[rep:AccessControllable]"
+ jcr:primaryType="nt:unstructured">
+ <foo
+ jcr:primaryType="oak:QueryIndexDefinition"
+ propertyNames="[foo]"
+ reindex="{Boolean}false"
+ reindexCount="{Long}1"
+ type="property">
+ </foo>
+ <bar
+ jcr:primaryType="oak:QueryIndexDefinition"
+ propertyNames="[bar]"
+ reindex="{Boolean}false"
+ reindexCount="{Long}1"
+ type="property">
+ </bar>
+</jcr:root>