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:45 UTC

[sling-org-apache-sling-feature-cpconverter] branch master updated (197d62a -> 6153de4)

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-cpconverter.git.


    from 197d62a  [maven-release-plugin] prepare for next development iteration
     new 76e045c  SLING-11134 - Extract Oak index definitions and package them as an additional file
     new fefa802  SLING-11134 - Extract Oak index definitions and package them as an additional file
     new 4439a9f  SLING-11134 - Extract Oak index definitions and package them as an additional file
     new 6153de4  SLING-11134 - Extract Oak index definitions and package them as an additional file

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 pom.xml                                            |   4 +-
 .../ContentPackage2FeatureModelConverter.java      |  16 +-
 ...ntentPackage2FeatureModelConverterLauncher.java |   2 +
 .../features/DefaultFeaturesManager.java           |  24 +-
 .../cpconverter/features/FeaturesManager.java      |   2 +
 .../handlers/IndexDefinitionsEntryHandler.java     | 135 +++++++++++
 .../cpconverter/index/DefaultIndexManager.java     |  52 ++++
 .../cpconverter/index/IndexDefinitions.java        | 184 ++++++++++++++
 .../index/IndexDefinitionsJsonWriter.java          | 155 ++++++++++++
 .../feature/cpconverter/index/IndexManager.java    |  60 +++++
 .../cpconverter/vltpkg/JcrNamespaceRegistry.java   |  35 +--
 ...sling.feature.cpconverter.handlers.EntryHandler |   1 +
 .../feature/cpconverter/AdjustedFilterTest.java    |   4 +-
 .../ContentPackage2FeatureModelConverterTest.java  |   5 +-
 .../ConverterUserAndPermissionTest.java            |   2 +
 .../handlers/IndexDefinitionsEntryHandlerTest.java | 270 +++++++++++++++++++++
 .../feature/cpconverter/handlers/TestUtils.java    |  56 +++--
 .../index/IndexDefinitionsJsonWriterTest.java      | 190 +++++++++++++++
 .../index_multiple_files/META-INF/vault/filter.xml |  21 ++
 .../META-INF/vault/nodetypes.cnd}                  |  17 +-
 .../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 ++
 .../META-INF/vault/nodetypes.cnd}                  |  16 +-
 .../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_non_root_path/META-INF/vault/config.xml  |  86 +++++++
 .../META-INF/vault/definition/.content.xml         |  37 +++
 .../index_non_root_path/META-INF/vault/filter.xml  |  20 ++
 .../META-INF/vault/properties.xml                  |  34 +++
 .../index_non_root_path/jcr_root/.content.xml      |  21 ++
 .../jcr_root/content/.content.xml                  |  28 +++
 .../jcr_root/content/_oak_index/.content.xml       |  65 +++++
 .../index_single_file/META-INF/vault/filter.xml    |  20 ++
 .../META-INF/vault/nodetypes.cnd}                  |  17 +-
 .../META-INF/vault/properties.xml                  |  34 +++
 .../index/index_single_file/jcr_root/.content.xml  |  32 +++
 .../jcr_root/_oak_index/.content.xml               |  35 +++
 42 files changed, 1948 insertions(+), 68 deletions(-)
 create mode 100644 src/main/java/org/apache/sling/feature/cpconverter/handlers/IndexDefinitionsEntryHandler.java
 create mode 100644 src/main/java/org/apache/sling/feature/cpconverter/index/DefaultIndexManager.java
 create mode 100644 src/main/java/org/apache/sling/feature/cpconverter/index/IndexDefinitions.java
 create mode 100644 src/main/java/org/apache/sling/feature/cpconverter/index/IndexDefinitionsJsonWriter.java
 create mode 100644 src/main/java/org/apache/sling/feature/cpconverter/index/IndexManager.java
 create mode 100644 src/test/java/org/apache/sling/feature/cpconverter/handlers/IndexDefinitionsEntryHandlerTest.java
 create mode 100644 src/test/java/org/apache/sling/feature/cpconverter/index/IndexDefinitionsJsonWriterTest.java
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/META-INF/vault/filter.xml
 copy src/{main/java/org/apache/sling/feature/cpconverter/shared/ConverterConstants.java => test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/META-INF/vault/nodetypes.cnd} (75%)
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/META-INF/vault/properties.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/jcr_root/_oak_index/baz/.content.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_multiple_files/jcr_root/_oak_index/lucene_custom/.content.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/META-INF/vault/filter.xml
 copy src/{main/java/org/apache/sling/feature/cpconverter/shared/ConverterConstants.java => test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/META-INF/vault/nodetypes.cnd} (75%)
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/META-INF/vault/properties.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/.content.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/_oak_index/.content.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_nested_tika/jcr_root/_oak_index/lucene-custom/tika/config.xml
 create mode 100644 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
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/config.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/definition/.content.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/filter.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/properties.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/jcr_root/.content.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/jcr_root/content/.content.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/jcr_root/content/_oak_index/.content.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/META-INF/vault/filter.xml
 copy src/{main/java/org/apache/sling/feature/cpconverter/shared/ConverterConstants.java => test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/META-INF/vault/nodetypes.cnd} (75%)
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/META-INF/vault/properties.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/jcr_root/.content.xml
 create mode 100644 src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_single_file/jcr_root/_oak_index/.content.xml

[sling-org-apache-sling-feature-cpconverter] 01/04: SLING-11134 - Extract Oak index definitions and package them as an additional file

Posted by ro...@apache.org.
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 76e045cd78cec5df8b94979bd32cd07bfe39fbee
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Mon Feb 21 15:39:10 2022 +0100

    SLING-11134 - Extract Oak index definitions and package them as an additional file
    
    Add TestUtils.getPackageRelativeFile
---
 .../feature/cpconverter/handlers/TestUtils.java    | 52 +++++++++++++++++-----
 1 file changed, 41 insertions(+), 11 deletions(-)

diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/TestUtils.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/TestUtils.java
index f470427..032977e 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/TestUtils.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/TestUtils.java
@@ -16,6 +16,20 @@
  */
 package org.apache.sling.feature.cpconverter.handlers;
 
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Extension;
@@ -29,17 +43,6 @@ import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Collections;
-
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
 class TestUtils {
 
     private static final Logger log = LoggerFactory.getLogger(TestUtils.class);
@@ -72,4 +75,31 @@ class TestUtils {
         converter.getAclManager().addRepoinitExtension(Collections.singletonList(packageAssembler), featuresManager);
         return feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT);
     }
+
+    /**
+     * Returns a test file that is located in a similar directory to the specified class
+     *
+     * <p>This is intended to work on a similar way to <tt>getClass().getResourceAsStream()</tt>, but with
+     * files instead.</p>
+     *
+     * @param klazz the class used to locate the file
+     * @param pathElement a path element
+     * @param pathElements additional, optional, elements
+     * @return a file that exists
+     * @throws IllegalArgumentException if the file does not exist
+     */
+    static File getPackageRelativeFile(Class<?> klazz, String pathElement, String... pathElements) {
+        List<CharSequence> segments =  new ArrayList<>();
+        segments.addAll(Arrays.asList("src", "test", "resources"));
+        segments.addAll(Arrays.asList(klazz.getPackage().getName().split("\\.")));
+        segments.add(pathElement);
+        if ( pathElements != null )
+            segments.addAll(Arrays.asList(pathElements));
+
+        String fileName = String.join(File.separator, segments.toArray(new CharSequence[0]));
+        File file = new File(fileName);
+        if ( !file.exists() )
+            throw new IllegalArgumentException("File " + file + " does not exist");
+        return file;
+    }
 }
\ No newline at end of file

[sling-org-apache-sling-feature-cpconverter] 04/04: SLING-11134 - Extract Oak index definitions and package them as an additional file

Posted by ro...@apache.org.
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 6153de41ac6dea87c21dadc16d89e43387c2ac22
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Mon Mar 14 17:27:52 2022 +0100

    SLING-11134 - Extract Oak index definitions and package them as an additional file
    
    Support non-root index definitions.
---
 .../handlers/IndexDefinitionsEntryHandler.java     | 11 ++-
 .../cpconverter/index/IndexDefinitions.java        | 27 ++++++-
 .../index/IndexDefinitionsJsonWriter.java          |  7 +-
 .../ContentPackage2FeatureModelConverterTest.java  |  1 +
 .../handlers/IndexDefinitionsEntryHandlerTest.java | 65 +++++++++++++---
 .../index_non_root_path/META-INF/vault/config.xml  | 86 ++++++++++++++++++++++
 .../META-INF/vault/definition/.content.xml         | 37 ++++++++++
 .../index_non_root_path/META-INF/vault/filter.xml  | 20 +++++
 .../META-INF/vault/properties.xml                  | 34 +++++++++
 .../index_non_root_path/jcr_root/.content.xml      | 21 ++++++
 .../jcr_root/content/.content.xml                  | 28 +++++++
 .../jcr_root/content/_oak_index/.content.xml       | 65 ++++++++++++++++
 12 files changed, 385 insertions(+), 17 deletions(-)

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
index b0770f8..a684cb1 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/IndexDefinitionsEntryHandler.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/IndexDefinitionsEntryHandler.java
@@ -47,6 +47,13 @@ import org.xml.sax.InputSource;
  */
 public class IndexDefinitionsEntryHandler extends AbstractRegexEntryHandler {
 
+    private static final String PATH_PATTERN = "" +
+            "/jcr_root/" + // jcr_root dir
+            "(.*/)?" + // optional path segment
+            PlatformNameFormat.getPlatformName(IndexDefinitions.OAK_INDEX_NAME) +
+            "(.*/)?" + // additional path segments
+            "/.*xml"; // only xml files
+
     private final class IndexDefinitionsParserHandler implements DocViewParserHandler {
         private final WorkspaceFilter filter;
         private IndexDefinitions definitions;
@@ -61,7 +68,7 @@ public class IndexDefinitionsEntryHandler extends AbstractRegexEntryHandler {
                 @NotNull Optional<DocViewNode2> parentDocViewNode, int line, int column)
                 throws IOException, RepositoryException {
 
-            if ( nodePath.startsWith(IndexDefinitions.OAK_INDEX_PATH) && filter.contains(nodePath) ) {
+            if ( nodePath.contains(IndexDefinitions.OAK_INDEX_PATH) && filter.contains(nodePath) ) {
                 definitions.addNode(Text.getRelativeParent(nodePath, 1), docViewNode);
             }
         }
@@ -80,7 +87,7 @@ public class IndexDefinitionsEntryHandler extends AbstractRegexEntryHandler {
     }
 
     public IndexDefinitionsEntryHandler() {
-        super("/jcr_root/" + PlatformNameFormat.getPlatformName(IndexDefinitions.OAK_INDEX_NAME)+ "/.*(/)?/*.xml");
+        super(PATH_PATTERN);
     }
 
     @Override
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
index 300e42c..9e32cc9 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/index/IndexDefinitions.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/index/IndexDefinitions.java
@@ -26,6 +26,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.stream.Collectors;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.spi.Name;
@@ -35,6 +36,12 @@ import org.jetbrains.annotations.NotNull;
 /**
  * Holds information about discovered index definitions
  *
+ * <p>According to the Oak documentation, indexes are located under a root <tt>/oak:index</tt>, or (lucene indexes only)
+ * under arbitrary repository locations, as long as they have an <tt>oak:index</tt> parent node.</p>
+ *
+ * <p>This class supports non-root indexes but does not attempt to enforce Oak-level invariants, such as which index
+ * types support non-root locations.</p>
+ *
  */
 public class IndexDefinitions {
 
@@ -72,15 +79,29 @@ public class IndexDefinitions {
         currentChildren.add(node);
     }
 
-    public @NotNull List<DocViewNode2> getIndexes() {
-        return getChildren(OAK_INDEX_PATH);
+    /**
+     * Returns the discovered index definitions by location
+     *
+     * <p>The returned map has the index parent location as keys and the index definitions as values, for instance:</p>
+     *
+     * <pre>
+     *  &sol;oak:index -> [counter, uuid]
+     *  &sol;content&sol;oak:index -> [lucene-2]
+     * </pre>
+     *
+     *
+     * @return a map of discovered index locations, possibly empty
+     */
+    public @NotNull Map<String, List<DocViewNode2>> getIndexes() {
+        return children.entrySet().stream()
+            .filter( e -> e.getKey().endsWith(OAK_INDEX_PATH) )
+            .collect( Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue) );
     }
 
     public @NotNull List<DocViewNode2> getChildren(@NotNull String parentPath) {
         return children.getOrDefault(parentPath, Collections.emptyList());
     }
 
-
     /**
      * Returns a name in compact format
      *
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
index a14fab5..2c8c106 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/index/IndexDefinitionsJsonWriter.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/index/IndexDefinitionsJsonWriter.java
@@ -20,6 +20,7 @@ import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.function.Function;
 
@@ -63,9 +64,9 @@ public class IndexDefinitionsJsonWriter {
     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);
-            }
+            for ( Map.Entry<String, List<DocViewNode2>> indexEntry : indexDefinitions.getIndexes().entrySet() )
+                for ( DocViewNode2 index : indexEntry.getValue() )
+                    write(root, index, indexEntry.getKey());
             root.writeEnd(); // end object declaration
         }
     }
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 f4e0441..940c780 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
@@ -487,6 +487,7 @@ public class ContentPackage2FeatureModelConverterTest extends AbstractConverterT
             converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, null, null, new HashMap<>(), aclManager))
                     .setBundlesDeployer(new SimpleFolderArtifactsDeployer(outputDirectory))
                     .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
+                    .setIndexManager(new DefaultIndexManager())
                     .convert(packageFile);
         } finally {
             verify(aclManager, times(1)).addPrivilegeDefinitions(any(PrivilegeDefinitions.class));
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
index 6bf7c55..1cdfff6 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/IndexDefinitionsEntryHandlerTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/IndexDefinitionsEntryHandlerTest.java
@@ -20,7 +20,9 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 import javax.jcr.NamespaceRegistry;
@@ -52,10 +54,13 @@ public class IndexDefinitionsEntryHandlerTest {
     public void matches() {
         IndexDefinitionsEntryHandler handler = new IndexDefinitionsEntryHandler();
         assertThat(handler.matches("/jcr_root/_oak_index/.content.xml")).isTrue();
+        assertThat(handler.matches("/jcr_root/not_oak_index/.content.xml")).isFalse();
         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();
+        assertThat(handler.matches("/jcr_root/apps/_oak_index/.content.xml")).isTrue();
+        assertThat(handler.matches("/jcr_root/apps/.content.xml")).isFalse();
+        assertThat(handler.matches("/jcr_root/not_oak_index/.content.xml")).isFalse();
     }
 
     @Test
@@ -66,14 +71,18 @@ public class IndexDefinitionsEntryHandlerTest {
         traverseForIndexing(manager, "index_single_file");
 
         IndexDefinitions defs = manager.getIndexes();
-        List<DocViewNode2> indexes = defs.getIndexes();
+        Map<String, List<DocViewNode2>> indexes = defs.getIndexes();
 
         assertThat(indexes).as("index definitions")
             .hasSize(1)
+            .containsKey("/oak:index");
+
+       List<DocViewNode2> rootIndexes = indexes.get("/oak:index");
+       assertThat(rootIndexes).as("root oak indexes")
+            .hasSize(1)
             .element(0)
                 .has( Conditions.localName("foo") )
                 .has( Conditions.property("type", "property") );
-
     }
 
     @Test
@@ -84,18 +93,26 @@ public class IndexDefinitionsEntryHandlerTest {
         traverseForIndexing(manager, "index_multiple_files");
 
         IndexDefinitions defs = manager.getIndexes();
-        List<DocViewNode2> indexes = defs.getIndexes();
+        Map<String, List<DocViewNode2>> indexes = defs.getIndexes();
 
         assertThat(indexes).as("index definitions")
+            .hasSize(1)
+            .containsKey("/oak:index");
+
+        List<DocViewNode2> rootIndexes = indexes.get("/oak:index");
+        assertThat(rootIndexes).as("root indexes")
             .hasSize(2);
 
-        assertThat(indexes).as("baz index")
+        // ensure consistent order
+        Collections.sort(rootIndexes, (a, b) -> defs.toShortName(a.getName()).compareTo(defs.toShortName(b.getName())));
+
+        assertThat(rootIndexes).as("baz index")
             .element(0).has( Conditions.localName("baz") );
-        assertThat(indexes).as("lucene_custom index")
+        assertThat(rootIndexes).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));
+                .has( Conditions.childWithLocalName("/oak:index/lucene_custom", "indexRules", defs));
 
     }
 
@@ -106,14 +123,22 @@ public class IndexDefinitionsEntryHandlerTest {
         traverseForIndexing(manager, "index_nested_tika");
 
         IndexDefinitions defs = manager.getIndexes();
-        List<DocViewNode2> indexes = defs.getIndexes();
+        Map<String, List<DocViewNode2>> indexes = defs.getIndexes();
 
         assertThat(indexes).as("index definitions")
             .hasSize(1)
+            .containsKey("/oak:index");
+
+        List<DocViewNode2> rootIndexes = indexes.get("/oak:index");
+        assertThat(rootIndexes).as("root indexes")
+            .hasSize(1);
+
+        assertThat(rootIndexes).as("index definitions")
+            .hasSize(1)
             .element(0)
                 .has(Conditions.localName("lucene-custom"));
 
-        DocViewNode2 luceneCustom = indexes.get(0);
+        DocViewNode2 luceneCustom = rootIndexes.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));
@@ -142,6 +167,28 @@ public class IndexDefinitionsEntryHandlerTest {
     }
 
 
+    @Test
+    public void handleIndexDefinitionUnderNonRootPath() throws IOException, ConverterException {
+
+        DefaultIndexManager manager = new DefaultIndexManager();
+
+        traverseForIndexing(manager, "index_non_root_path");
+
+        IndexDefinitions defs = manager.getIndexes();
+        Map<String, List<DocViewNode2>> indexes = defs.getIndexes();
+        assertThat(indexes).as("index definitions")
+            .hasSize(1)
+            .containsKey("/content/oak:index");
+
+        List<DocViewNode2> contentIndexes = indexes.get("/content/oak:index");
+        assertThat(contentIndexes).as("/content indexes")
+            .hasSize(1)
+            .element(0)
+                .has( Conditions.localName("lucene") )
+                .has( Conditions.property("type", "lucene") );
+
+    }
+
     private void assertIsValidXml(byte[] tikeConfig) throws ParserConfigurationException, SAXException, IOException {
 
         DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/config.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/config.xml
new file mode 100644
index 0000000..f1b081a
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/config.xml
@@ -0,0 +1,86 @@
+<!--
+  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.
+  -->
+<vaultfs version="1.1">
+    <!--
+        Defines the content aggregation. The order of the defined aggregates
+        is important for finding the correct aggregator.
+    -->
+    <aggregates>
+        <!--
+            Defines an aggregate that handles nt:file and nt:resource nodes.
+        -->
+        <aggregate type="file" title="File Aggregate"/>
+
+        <!--
+            Defines an aggregate that handles file/folder like nodes. It matches
+            all nt:hierarchyNode nodes that have or define a jcr:content
+            child node and excludes child nodes that are nt:hierarchyNodes.
+        -->
+        <aggregate type="filefolder" title="File/Folder Aggregate"/>
+
+        <!--
+            Defines an aggregate that defines full coverage for certain node
+            types that cannot be covered by the default aggregator.
+        -->
+        <aggregate type="full" title="Full Coverage Aggregate">
+            <matches>
+                <include nodeType="rep:AccessControl" respectSupertype="true" />
+                <include nodeType="rep:Policy" respectSupertype="true" />
+                <include nodeType="cq:Widget" respectSupertype="true" />
+                <include nodeType="cq:EditConfig" respectSupertype="true" />
+                <include nodeType="cq:WorkflowModel" respectSupertype="true" />
+                <include nodeType="vlt:FullCoverage" respectSupertype="true" />
+                <include nodeType="mix:language" respectSupertype="true" />
+                <include nodeType="sling:OsgiConfig" respectSupertype="true" />
+            </matches>
+        </aggregate>
+
+        <!--
+            Defines an aggregate that handles nt:folder like nodes.
+        -->
+        <aggregate type="generic" title="Folder Aggregate">
+            <matches>
+                <include nodeType="nt:folder" respectSupertype="true" />
+            </matches>
+            <contains>
+                <exclude isNode="true" />
+            </contains>
+        </aggregate>
+
+        <!--
+            Defines the default aggregate
+        -->
+        <aggregate type="generic" title="Default Aggregator" isDefault="true">
+            <matches>
+                <!-- all -->
+            </matches>
+            <contains>
+                <exclude nodeType="nt:hierarchyNode" respectSupertype="true" />
+            </contains>
+        </aggregate>
+
+    </aggregates>
+
+    <!--
+      defines the input handlers
+    -->
+    <handlers>
+        <handler type="folder"/>
+        <handler type="file"/>
+        <handler type="generic"/>
+    </handlers>
+</vaultfs>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/definition/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/definition/.content.xml
new file mode 100644
index 0000000..2a9e723
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/definition/.content.xml
@@ -0,0 +1,37 @@
+<?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:vlt="http://www.day.com/jcr/vault/1.0"
+    jcr:lastModified="{Date}2022-03-04T18:13:03.077+02:00"
+    jcr:lastModifiedBy="admin"
+    jcr:primaryType="vlt:PackageDefinition"
+    buildCount="1"
+    group="com.example"
+    lastUnwrapped="{Date}2022-03-04T18:13:03.077+02:00"
+    lastUnwrappedBy="admin"
+    lastWrapped="{Date}2022-03-04T18:13:03.077+02:00"
+    lastWrappedBy="admin"
+    name="index-under-content"
+    version="1.0">
+    <filter jcr:primaryType="nt:unstructured">
+        <f0
+            jcr:primaryType="nt:unstructured"
+            mode="replace"
+            root="/content/oak:index"
+            rules="[]"/>
+    </filter>
+</jcr:root>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/filter.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/filter.xml
new file mode 100644
index 0000000..cd9c945
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/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="/content/oak:index"/>
+</workspaceFilter>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/properties.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/properties.xml
new file mode 100644
index 0000000..367512c
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/META-INF/vault/properties.xml
@@ -0,0 +1,34 @@
+<?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.
+  -->
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties>
+<comment>FileVault Package Properties</comment>
+<entry key="packageType">content</entry>
+<entry key="lastWrappedBy">admin</entry>
+<entry key="packageFormatVersion">2</entry>
+<entry key="group">com.example</entry>
+<entry key="created">2022-03-04T18:13:03.105+02:00</entry>
+<entry key="lastModifiedBy">admin</entry>
+<entry key="buildCount">1</entry>
+<entry key="lastWrapped">2022-03-04T18:13:03.077+02:00</entry>
+<entry key="version">1.0</entry>
+<entry key="dependencies"></entry>
+<entry key="createdBy">admin</entry>
+<entry key="name">index-under-content</entry>
+<entry key="lastModified">2022-03-04T18:13:03.077+02:00</entry>
+</properties>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/jcr_root/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/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_non_root_path/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_non_root_path/jcr_root/content/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/jcr_root/content/.content.xml
new file mode 100644
index 0000000..a576d5d
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/jcr_root/content/.content.xml
@@ -0,0 +1,28 @@
+<?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:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:rep="internal" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+    jcr:mixinTypes="[rep:AccessControllable]"
+    jcr:primaryType="sling:OrderedFolder"
+    sling:resourceType="sling:redirect"
+    sling:target="/starter.html">
+    <rep:policy/>
+    <htl/>
+    <slingshot/>
+    <starter/>
+    <oak:index/>
+</jcr:root>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/jcr_root/content/_oak_index/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/jcr_root/content/_oak_index/.content.xml
new file mode 100644
index 0000000..5727fe6
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/index/index_non_root_path/jcr_root/content/_oak_index/.content.xml
@@ -0,0 +1,65 @@
+<?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">
+    <lucene
+        jcr:primaryType="oak:QueryIndexDefinition"
+        async="async"
+        includePropertyTypes="[String,Binary]"
+        reindex="{Boolean}false"
+        reindexCount="{Long}3"
+        seed="{Long}4650018324827688380"
+        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>
+    </lucene>
+</jcr:root>

[sling-org-apache-sling-feature-cpconverter] 03/04: SLING-11134 - Extract Oak index definitions and package them as an additional file

Posted by ro...@apache.org.
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>

[sling-org-apache-sling-feature-cpconverter] 02/04: SLING-11134 - Extract Oak index definitions and package them as an additional file

Posted by ro...@apache.org.
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 fefa802052ffe23f2921a72814d15ea103b5b23c
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Mon Feb 21 15:39:56 2022 +0100

    SLING-11134 - Extract Oak index definitions and package them as an additional file
    
    Remove unused TestUtils.log
---
 .../java/org/apache/sling/feature/cpconverter/handlers/TestUtils.java | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/TestUtils.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/TestUtils.java
index 032977e..30e1d82 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/TestUtils.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/TestUtils.java
@@ -40,13 +40,9 @@ import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager;
 import org.apache.sling.feature.cpconverter.features.FeaturesManager;
 import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
 import org.jetbrains.annotations.NotNull;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 class TestUtils {
 
-    private static final Logger log = LoggerFactory.getLogger(TestUtils.class);
-
     private TestUtils() {}
 
     static Extension createRepoInitExtension(@NotNull EntryHandler handler, @NotNull AclManager aclManager, @NotNull String path, @NotNull InputStream is) throws Exception {