You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by pa...@apache.org on 2022/01/17 15:49:45 UTC

[sling-org-apache-sling-feature-cpconverter] branch master updated: Sling 10931 (#125)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new f01d77a  Sling 10931 (#125)
f01d77a is described below

commit f01d77a60949595f9d266fcc06a72e243078902d
Author: Niek Raaijmakers <ni...@gmail.com>
AuthorDate: Mon Jan 17 16:49:38 2022 +0100

    Sling 10931 (#125)
    
    * SLING-10931 - refactoring the sling initial content part of CPConverter to follow the content creator pattern. Buffering XML files.
    
    
    Co-authored-by: Niek Raaijmakers <ra...@adobe.com>
---
 pom.xml                                            |  68 ++++
 .../ContentPackage2FeatureModelConverter.java      |  21 +-
 .../accesscontrol/DefaultAclManager.java           |  63 +--
 ...ntentPackage2FeatureModelConverterLauncher.java |   5 +-
 .../cpconverter/handlers/BundleEntryHandler.java   | 294 +-------------
 .../handlers/DefaultEntryHandlersManager.java      |   8 +-
 .../handlers/SlingInitialContentBundleHandler.java |  31 +-
 .../slinginitialcontent/AssemblerProvider.java     | 120 ++++++
 .../BundleSlingInitialContentExtractContext.java   | 120 ++++++
 .../BundleSlingInitialContentExtractor.java        | 132 +++++++
 ...BundleSlingInitialContentJarEntryExtractor.java | 150 +++++++
 .../ContentPackageEntryPathComputer.java           |  74 ++++
 .../slinginitialcontent/ContentReaderProvider.java |  55 +++
 .../JcrNamespaceRegistryProvider.java              | 122 ++++++
 .../ParentFolderRepoInitHandler.java               | 120 ++++++
 .../SlingInitialContentBundleEntryMetaData.java    |  77 ++++
 ...InitialContentBundleEntryMetaDataCollector.java | 204 ++++++++++
 .../VaultContentXMLContentCreator.java             | 232 +++++++++++
 .../XMLNodeToXMLFileWriter.java                    | 110 ++++++
 .../slinginitialcontent/readers/XMLReader.java}    |  31 +-
 .../slinginitialcontent/xmlbuffer/XMLNode.java     | 158 ++++++++
 .../createpath/CreatePathSegmentProcessor.java     | 105 +++++
 .../createpath}/MixinParser.java                   |   4 +-
 .../createpath}/PrimaryTypeParser.java             |   4 +-
 .../cpconverter/shared/ConverterConstants.java     |   1 +
 .../vltpkg/DocViewSerializerContentHandler.java    |  73 +++-
 .../cpconverter/vltpkg/JcrNamespaceRegistry.java   |  31 +-
 .../cpconverter/vltpkg/VaultPackageAssembler.java  |  12 +-
 .../feature/cpconverter/AdjustedFilterTest.java    |   3 +-
 .../ContentPackage2FeatureModelConverterTest.java  | 106 +----
 .../ConverterUserAndPermissionTest.java            |   5 +-
 .../BundleEntryHandleSlingInitialContentTest.java  | 439 ++++++++++++++++++++-
 .../handlers/BundleEntryHandlerTest.java           |   1 +
 .../handlers/GavDeclarationsInBundleTest.java      |   1 +
 .../vltpkg/JcrNamespaceRegistryTest.java           |  16 +-
 .../build_playground.ui.content-1.0-SNAPSHOT.zip   | Bin 17699035 -> 0 bytes
 .../handlers/bundle-entry-xmls/%3c%22&%3e.xml      |   7 +
 .../handlers/bundle-entry-xmls/11mumbojumbo.xml    |   5 +
 .../handlers/bundle-entry-xmls/homepage.xml        |   3 +
 .../bundle-entry-xmls/include-redirectStatus.xml   |  17 +
 .../handlers/bundle-entry-xmls/nodeName.xml        |   7 +
 .../handlers/bundle-entry-xmls/someprop.xml        |   6 +
 .../cpconverter/handlers/bundle-entry-xmls/xyz.xml |   5 +
 .../i18n-jsonfile-xml-descriptor-test/en.json      |  10 +
 .../en.json.dir/.content.xml                       |   8 +
 .../handlers/io.wcm.handler.link-1.7.02.jar        | Bin 0 -> 85560 bytes
 .../mysite-nodetype-and-page-json-xml-result.xml   |  44 +++
 ...re-1.0.0-SNAPSHOT-i18n-xml-folderdescriptor.jar | Bin 0 -> 20655 bytes
 .../mysite.core-1.0.0-SNAPSHOT-pagejson.jar        | Bin 0 -> 20243 bytes
 ...ore-1.0.0-SNAPSHOT-slinginitialcontent-test.jar | Bin 0 -> 21283 bytes
 ....0.0-SNAPSHOT-specialchars-json-inputstream.jar | Bin 0 -> 18815 bytes
 .../cpconverter/mysite.all-1.0.0-SNAPSHOT2.zip     |   0
 .../org/apache/sling/feature/cpconverter/seed.json |   9 +
 53 files changed, 2590 insertions(+), 527 deletions(-)

diff --git a/pom.xml b/pom.xml
index ba5cfeb..3c0aa14 100644
--- a/pom.xml
+++ b/pom.xml
@@ -256,17 +256,85 @@
       <version>3.4.6</version>
       <scope>compile</scope>
     </dependency>
+
+    <dependency>
+      <groupId>stax-utils</groupId>
+      <artifactId>stax-utils</artifactId>
+      <version>snapshot-20040917</version>
+      <scope>compile</scope>
+    </dependency>
+    
+    <!-- https://mvnrepository.com/artifact/org.xmlunit/xmlunit-core -->
+    <dependency>
+      <groupId>org.xmlunit</groupId>
+      <artifactId>xmlunit-core</artifactId>
+      <version>2.6.2</version>
+      <scope>test</scope>
+    </dependency>
+    <!-- https://mvnrepository.com/artifact/org.xmlunit/xmlunit-matchers -->
+    <dependency>
+      <groupId>org.xmlunit</groupId>
+      <artifactId>xmlunit-matchers</artifactId>
+      <version>2.6.2</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.xmlunit</groupId>
+      <artifactId>xmlunit-assertj</artifactId>
+      <version>2.6.2</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.xml.bind</groupId>
+      <artifactId>jaxb-api</artifactId>
+      <version>2.3.1</version>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- https://mvnrepository.com/artifact/javax.activation/activation -->
+    <dependency>
+      <groupId>javax.activation</groupId>
+      <artifactId>activation</artifactId>
+      <version>1.1</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>com.google.code.gson</groupId>
+      <artifactId>gson</artifactId>
+      <version>2.8.6</version>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
+    <dependency>
+      <groupId>org.glassfish.jaxb</groupId>
+      <artifactId>jaxb-runtime</artifactId>
+      <version>2.3.2</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
     <plugins>
       <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.8.0</version>
+        <configuration>
+          <source>1.8</source>
+          <target>1.8</target>
+        </configuration>
+      </plugin>
+      <plugin>
         <groupId>org.apache.rat</groupId>
         <artifactId>apache-rat-plugin</artifactId>
           <configuration>
           <excludes>
             <exclude>src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.handlers.EntryHandler</exclude>
             <exclude>src/main/legal/NOTICE-with-deps</exclude>
+            <exclude>src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/**</exclude>
+            <exclude>src/test/resources/org/apache/sling/feature/cpconverter/handlers/i18n-jsonfile-xml-descriptor-test/**</exclude>
           </excludes>
         </configuration>
       </plugin>
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 4ac12b7..f342e41 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
@@ -56,6 +56,7 @@ import org.apache.sling.feature.cpconverter.handlers.DefaultHandler;
 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.vltpkg.BaseVaultPackageScanner;
 import org.apache.sling.feature.cpconverter.vltpkg.PackagesEventsEmitter;
 import org.apache.sling.feature.cpconverter.vltpkg.RecollectorVaultPackageScanner;
@@ -104,6 +105,8 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
     private PackagePolicy contentTypePackagePolicy = PackagePolicy.REFERENCE;
 
     private boolean removeInstallHooks = false;
+    
+    private BundleSlingInitialContentExtractor bundleSlingInitialContentExtractor = new BundleSlingInitialContentExtractor();
 
     public enum PackagePolicy {
         /**
@@ -266,6 +269,8 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
             logger.info("content-package '{}' successfully read!", contentPackage);
 
             aclManager.reset();
+            bundleSlingInitialContentExtractor.reset();
+            
         }
 
         logger.info("Ordering input content-package(s) {}...", idPackageMapping.keySet());
@@ -302,7 +307,8 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
                     // finally serialize the Feature Model(s) file(s)
 
                     aclManager.addRepoinitExtension(assemblers, featuresManager);
-
+                    bundleSlingInitialContentExtractor.addRepoInitExtension(assemblers, featuresManager);
+                    
                     logger.info("Conversion complete!");
 
                     featuresManager.serialize();
@@ -310,7 +316,9 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
                     emitters.stream().forEach(e -> e.endPackage(vaultPackage.getId(), result));
                 }
             } finally {
+                
                 aclManager.reset();
+                bundleSlingInitialContentExtractor.reset();
                 assemblers.clear();
 
                 try {
@@ -364,10 +372,11 @@ 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();
-        assemblers.add(handler);
+      
         Properties parentProps = handler.getPackageProperties();
         boolean isContainerPackage = PackageType.CONTAINER.equals(parentProps.get(PackageProperties.NAME_PACKAGE_TYPE));
         setMainPackageAssembler(clonedPackage);
+        assemblers.add(clonedPackage);
 
         // scan the detected package, first
         traverse(vaultPackage);
@@ -494,6 +503,11 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
         }
     }
 
+    public ContentPackage2FeatureModelConverter setBundleSlingInitialContentExtractor(BundleSlingInitialContentExtractor bundleSlingInitialContentExtractor) {
+        this.bundleSlingInitialContentExtractor = bundleSlingInitialContentExtractor;
+        return this;
+    }
+
     @Override
     protected void onFile(@NotNull String entryPath, @NotNull Archive archive, @NotNull Entry entry) throws IOException, ConverterException {
         try {
@@ -544,4 +558,7 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
         cleanup();
     }
 
+    public List<VaultPackageAssembler> getAssemblers() {
+        return new ArrayList<>(assemblers);
+    }
 }
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/DefaultAclManager.java b/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/DefaultAclManager.java
index b69039a..067a9c7 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/DefaultAclManager.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/DefaultAclManager.java
@@ -22,11 +22,11 @@ import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
 import org.apache.jackrabbit.spi.commons.conversion.NameResolver;
 import org.apache.jackrabbit.util.Text;
 import org.apache.jackrabbit.vault.fs.spi.PrivilegeDefinitions;
-import org.apache.jackrabbit.vault.util.PlatformNameFormat;
 import org.apache.sling.feature.cpconverter.ConverterException;
 import org.apache.sling.feature.cpconverter.features.FeaturesManager;
 import org.apache.sling.feature.cpconverter.repoinit.NoOpVisitor;
 import org.apache.sling.feature.cpconverter.repoinit.OperationProcessor;
+import org.apache.sling.feature.cpconverter.repoinit.createpath.CreatePathSegmentProcessor;
 import org.apache.sling.feature.cpconverter.shared.ConverterConstants;
 import org.apache.sling.feature.cpconverter.shared.RepoPath;
 import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
@@ -48,8 +48,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.jcr.NamespaceException;
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.StringReader;
 import java.util.ArrayList;
@@ -71,8 +69,6 @@ import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import static org.apache.jackrabbit.vault.util.Constants.DOT_CONTENT_XML;
-
 public class DefaultAclManager implements AclManager, EnforceInfo {
 
     private static final Logger log = LoggerFactory.getLogger(DefaultAclManager.class);
@@ -429,7 +425,7 @@ public class DefaultAclManager implements AclManager, EnforceInfo {
         }
         
         CreatePath cp = new CreatePath(null);
-        boolean foundType = processSegments(path, packageAssemblers, cp);
+        boolean foundType = CreatePathSegmentProcessor.processSegments(path, packageAssemblers, cp);
         
         if (!foundType && isBelowUserRoot(path)) {
             // if no type information has been detected, don't issue a 'create path' statement for nodes below the 
@@ -442,61 +438,6 @@ public class DefaultAclManager implements AclManager, EnforceInfo {
             return cp;
         }
     }
-    
-    private static boolean processSegments(@NotNull RepoPath path, @NotNull List<VaultPackageAssembler> packageAssemblers, @NotNull CreatePath cp) {
-        String platformPath = "";
-        boolean foundType = false;
-        for (String part : path.getSegments()) {
-            String platformname = PlatformNameFormat.getPlatformName(part);
-            platformPath += platformPath.isEmpty() ? platformname : "/" + platformname;
-            boolean segmentAdded = false;
-            for (VaultPackageAssembler packageAssembler : packageAssemblers) {
-                File currentContent = packageAssembler.getEntry(platformPath + "/" + DOT_CONTENT_XML);
-                if (currentContent.isFile()) {
-                    segmentAdded =  addSegment(cp, part, currentContent);
-                    if (segmentAdded) {
-                        foundType = true;
-                        break;
-                    }
-                }
-            }
-            if (!segmentAdded) {
-                cp.addSegment(part, null);
-            }
-        }
-        return foundType;
-    }
-
-    private static boolean addSegment(@NotNull CreatePath cp, @NotNull String part, @NotNull File currentContent) {
-        try (FileInputStream input = new FileInputStream(currentContent);
-             FileInputStream input2 = new FileInputStream(currentContent)) {
-            String primary = new PrimaryTypeParser().parse(input);
-            if (primary != null) {
-                List<String> mixins = new ArrayList<>();
-                String mixin = new MixinParser().parse(input2);
-                if (mixin != null) {
-                    mixin = mixin.trim();
-                    if (mixin.startsWith("[")) {
-                        mixin = mixin.substring(1, mixin.length() - 1);
-                    }
-                    for (String m : mixin.split(",")) {
-                        String mixinName = m.trim();
-                        if (!mixinName.isEmpty()) {
-                            mixins.add(mixinName);
-                        }
-                    }
-                }
-                cp.addSegment(part, primary, mixins);
-                return true;
-            }
-        } catch (Exception e) {
-            throw new RuntimeException("A fatal error occurred while parsing the '"
-                    + currentContent
-                    + "' file, see nested exceptions: "
-                    + e);
-        }
-        return false;
-    }
 
     @NotNull
     private String getRepoInitPath(@NotNull RepoPath path, @NotNull SystemUser systemUser) {
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 fcf33a1..d7ccbfa 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
@@ -37,6 +37,7 @@ import org.apache.sling.feature.cpconverter.artifacts.LocalMavenRepositoryArtifa
 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.shared.ConverterConstants;
 import org.apache.sling.feature.cpconverter.vltpkg.DefaultPackagesEventsEmitter;
 import org.apache.sling.feature.io.json.FeatureJSONReader;
@@ -207,9 +208,11 @@ public final class ContentPackage2FeatureModelConverterLauncher implements Runna
                 }
 
                 try (ContentPackage2FeatureModelConverter converter = new ContentPackage2FeatureModelConverter(strictValidation, slingInitialContentPolicy)) {
+                    BundleSlingInitialContentExtractor bundleSlingInitialContentExtractor = new BundleSlingInitialContentExtractor();
                     converter.setFeaturesManager(featuresManager)
                              .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(artifactsOutputDirectory))
-                             .setEntryHandlersManager(new DefaultEntryHandlersManager(entryHandlerConfigsMap, !disableInstallerPolicy, slingInitialContentPolicy, systemUserRelPath))
+                             .setBundleSlingInitialContentExtractor(bundleSlingInitialContentExtractor)
+                             .setEntryHandlersManager(new DefaultEntryHandlersManager(entryHandlerConfigsMap, !disableInstallerPolicy, slingInitialContentPolicy, bundleSlingInitialContentExtractor, systemUserRelPath))
                              .setAclManager(aclManager)
                              .setEmitter(DefaultPackagesEventsEmitter.open(featureModelsOutputDirectory))
                              .setFailOnMixedPackages(failOnMixedPackages)
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandler.java
index e6ff430..4412fea 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandler.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandler.java
@@ -19,73 +19,34 @@ package org.apache.sling.feature.cpconverter.handlers;
 import static java.util.Objects.requireNonNull;
 import static org.osgi.framework.Version.parseVersion;
 
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
-import java.io.Reader;
-import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-import java.util.AbstractMap;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.EnumMap;
-import java.util.EnumSet;
 import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.Map;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.Properties;
-import java.util.StringTokenizer;
-import java.util.jar.Attributes;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
-import java.util.jar.JarOutputStream;
 import java.util.jar.Manifest;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.felix.utils.manifest.Clause;
 import org.apache.felix.utils.manifest.Parser;
-import org.apache.jackrabbit.commons.cnd.ParseException;
-import org.apache.jackrabbit.util.Text;
-import org.apache.jackrabbit.vault.fs.api.ImportMode;
-import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
-import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
-import org.apache.jackrabbit.vault.packaging.PackageId;
 import org.apache.jackrabbit.vault.packaging.PackageProperties;
-import org.apache.jackrabbit.vault.packaging.PackageType;
-import org.apache.jackrabbit.vault.util.PlatformNameFormat;
-import org.apache.sling.commons.osgi.ManifestHeader;
-import org.apache.sling.contentparser.api.ContentParser;
-import org.apache.sling.contentparser.api.ParserOptions;
-import org.apache.sling.contentparser.json.JSONParserFeature;
-import org.apache.sling.contentparser.json.JSONParserOptions;
-import org.apache.sling.contentparser.json.internal.JSONContentParser;
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
 import org.apache.sling.feature.cpconverter.ConverterException;
 import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.SlingInitialContentPolicy;
 import org.apache.sling.feature.cpconverter.artifacts.InputStreamArtifactWriter;
-import org.apache.sling.feature.cpconverter.vltpkg.DocViewSerializerContentHandler;
-import org.apache.sling.feature.cpconverter.vltpkg.DocViewSerializerContentHandlerException;
-import org.apache.sling.feature.cpconverter.vltpkg.JcrNamespaceRegistry;
-import org.apache.sling.feature.cpconverter.vltpkg.SingleFileArchive;
-import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
-import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageUtils;
-import org.apache.sling.jcr.contentloader.PathEntry;
+import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.BundleSlingInitialContentExtractor;
+import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.BundleSlingInitialContentExtractContext;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.osgi.framework.Constants;
@@ -98,18 +59,15 @@ public class BundleEntryHandler extends AbstractRegexEntryHandler {
     private static final String NAME_ARTIFACT_ID = "artifactId";
 
     private static final String JAR_TYPE = "jar";
-
-    public static final String NODETYPES_BUNDLE_HEADER = "Sling-Nodetypes";
-
-    public static final String NAMESPACES_BUNDLE_HEADER = "Sling-Namespaces";
-
+    
     private static final Pattern POM_PROPERTIES_PATTERN = Pattern.compile("META-INF/maven/[^/]+/[^/]+/pom.properties");
 
     private static final Pattern POM_XML_PATTERN = Pattern.compile("META-INF/maven/[^/]+/[^/]+/pom.xml");
 
     private boolean enforceBundlesBelowInstallFolder;
 
-    private SlingInitialContentPolicy slingInitialContentPolicy;
+    protected SlingInitialContentPolicy slingInitialContentPolicy;
+    private BundleSlingInitialContentExtractor bundleSlingInitialContentExtractor = new BundleSlingInitialContentExtractor();
 
     public BundleEntryHandler() {
         super("/jcr_root/(?:apps|libs)/.+/(?<foldername>install|config)(?:\\.(?<runmode>[^/]+))?/(?:(?<startlevel>[0-9]+)/)?.+\\.jar");
@@ -186,14 +144,15 @@ public class BundleEntryHandler extends AbstractRegexEntryHandler {
     }
 
     void processBundleInputStream(@NotNull String path, @NotNull Path originalBundleFile, @NotNull String bundleName, @Nullable String runMode, @Nullable Integer startLevel, @NotNull ContentPackage2FeatureModelConverter converter)
-        throws ConverterException, IOException {
+            throws ConverterException, IOException {
         try (JarFile jarFile = new JarFile(originalBundleFile.toFile())) {
             // first extract bundle metadata from JAR input stream
             Artifact artifact = extractFeatureArtifact(bundleName, jarFile);
             ArtifactId id = artifact.getId();
 
-            try (InputStream strippedBundleInput = extractSlingInitialContent(path, originalBundleFile, artifact, jarFile, converter, runMode)) {
-                if (strippedBundleInput != null && slingInitialContentPolicy == SlingInitialContentPolicy.EXTRACT_AND_REMOVE) {
+            BundleSlingInitialContentExtractContext context = new BundleSlingInitialContentExtractContext(slingInitialContentPolicy, path, id, jarFile, converter, runMode);
+            try (InputStream strippedBundleInput = bundleSlingInitialContentExtractor.extract(context)) {
+                if (strippedBundleInput != null && slingInitialContentPolicy == ContentPackage2FeatureModelConverter.SlingInitialContentPolicy.EXTRACT_AND_REMOVE) {
                     id = id.changeVersion(id.getVersion() + "-" + ContentPackage2FeatureModelConverter.PACKAGE_CLASSIFIER);
                     Objects.requireNonNull(converter.getArtifactsDeployer()).deploy(new InputStreamArtifactWriter(strippedBundleInput), id);
                 } else {
@@ -213,232 +172,6 @@ public class BundleEntryHandler extends AbstractRegexEntryHandler {
         }
     }
 
-    static Version getModifiedOsgiVersion(Version originalVersion) {
-        return new Version(originalVersion.getMajor(), originalVersion.getMinor(), originalVersion.getMicro(), originalVersion.getQualifier() + "_" + ContentPackage2FeatureModelConverter.PACKAGE_CLASSIFIER);
-    }
-
-    @Nullable InputStream extractSlingInitialContent(@NotNull String path, @NotNull Path bundlePath, @NotNull Artifact bundleArtifact, @NotNull JarFile jarFile, @NotNull ContentPackage2FeatureModelConverter converter, @Nullable String runMode) throws IOException, ConverterException {
-        if (slingInitialContentPolicy == SlingInitialContentPolicy.KEEP) {
-            return null;
-        }
-        // parse "Sling-Initial-Content" header
-        Manifest manifest = Objects.requireNonNull(jarFile.getManifest());
-        Iterator<PathEntry> pathEntries = PathEntry.getContentPaths(manifest, -1);
-        if (pathEntries == null) {
-            return null;
-        }
-        logger.info("Extracting Sling-Initial-Content from '{}'", bundleArtifact.getId());
-        Collection<PathEntry> pathEntryList = new ArrayList<>();
-        pathEntries.forEachRemaining(pathEntryList::add);
-
-        // remove header
-        manifest.getMainAttributes().remove(new Attributes.Name(PathEntry.CONTENT_HEADER));
-        // change version to have suffix
-        Version originalVersion = new Version(Objects.requireNonNull(manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION)));
-        manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION, getModifiedOsgiVersion(originalVersion).toString());
-        Path newBundleFile = Files.createTempFile(converter.getTempDirectory().toPath(), "newBundle", ".jar");
-        
-        // create JAR file to prevent extracting it twice and for random access
-        JcrNamespaceRegistry namespaceRegistry = createNamespaceRegistry(manifest, jarFile, converter.getFeaturesManager().getNamespaceUriByPrefix());
-        
-        Map<PackageType, VaultPackageAssembler> packageAssemblers = new EnumMap<>(PackageType.class);
-        try (OutputStream fileOutput = Files.newOutputStream(newBundleFile, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
-            JarOutputStream bundleOutput = new JarOutputStream(fileOutput, manifest)) {
-            
-            for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements();) {
-                JarEntry jarEntry = e.nextElement();
-                if (!jarEntry.isDirectory()) {
-                    try (InputStream input = jarFile.getInputStream(jarEntry)) {
-                        if (!extractSlingInitialContent(jarEntry, input, bundleArtifact, pathEntryList, packageAssemblers, namespaceRegistry, converter)) {
-                            // skip manifest, as already written in the constructor (as first entry)
-                            if (jarEntry.getName().equals(JarFile.MANIFEST_NAME)) {
-                                continue;
-                            }
-                            // copy entry as is to the stripped bundle
-                            bundleOutput.putNextEntry(jarEntry);
-                            IOUtils.copy(input, bundleOutput);
-                            bundleOutput.closeEntry();
-                        }
-                    }
-                }
-            }
-        }
-        // add additional content packages to feature model
-        finalizePackageAssembly(path, packageAssemblers, converter, runMode);
-        
-        // return stripped bundle's inputstream which must be deleted on close
-        return Files.newInputStream(newBundleFile, StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE);
-    }
-
-    /**
-     * 
-     * @param jarEntry
-     * @param bundleFileInputStream
-     * @param packageAssemblers
-     * @param converter
-     * @return {@code true} in case the given entry was part of the initial content otherwise {@code false}
-     * @throws Exception 
-     */
-    boolean extractSlingInitialContent(@NotNull JarEntry jarEntry, @NotNull InputStream bundleFileInputStream, @NotNull Artifact bundleArtifact, @NotNull Collection<PathEntry> pathEntries, @NotNull Map<PackageType, VaultPackageAssembler> packageAssemblers, @NotNull JcrNamespaceRegistry nsRegistry, @NotNull ContentPackage2FeatureModelConverter converter) throws IOException, ConverterException {
-        final String entryName = jarEntry.getName();
-        // check if current JAR entry is initial content
-        Optional<PathEntry> pathEntry = pathEntries.stream().filter(p -> entryName.startsWith(p.getPath())).findFirst();
-        if (!pathEntry.isPresent()) {
-            return false;
-        }
-        Map.Entry<ContentParser, ParserOptions> contentParserAndOptions = getContentParserForEntry(jarEntry, pathEntry.get());
-
-        // https://sling.apache.org/documentation/bundles/content-loading-jcr-contentloader.html#file-name-escaping
-        String repositoryPath = (pathEntry.get().getTarget() != null ? pathEntry.get().getTarget() : "/") + URLDecoder.decode(entryName.substring(pathEntry.get().getPath().length()), "UTF-8");
-        // all entry paths used by entry handlers start with "/"
-        String contentPackageEntryPath = "/" + org.apache.jackrabbit.vault.util.Constants.ROOT_DIR + PlatformNameFormat.getPlatformPath(repositoryPath);
-
-        Path tmpDocViewInputFile = null;
-        try {
-            if (contentParserAndOptions != null) {
-                // convert to docview xml
-                tmpDocViewInputFile = Files.createTempFile(converter.getTempDirectory().toPath(), "docview", ".xml");
-                try (OutputStream docViewOutput = Files.newOutputStream(tmpDocViewInputFile, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
-                     DocViewSerializerContentHandler contentHandler = new DocViewSerializerContentHandler(docViewOutput, nsRegistry)) {
-                    contentParserAndOptions.getKey().parse(contentHandler, bundleFileInputStream, contentParserAndOptions.getValue());
-                    contentPackageEntryPath = FilenameUtils.removeExtension(contentPackageEntryPath) + ".xml";
-                } catch (IOException e) {
-                    throw new IOException("Can not parse " + jarEntry, e);
-                } catch (DocViewSerializerContentHandlerException e) {
-                    throw new IOException("Can not convert " + jarEntry + " to enhanced DocView format", e);
-                }
-            }
-    
-            // remap CND files to make sure they are picked up by NodeTypesEntryHandler;
-            if (nsRegistry.getRegisteredCndSystemIds().contains(jarEntry.getName())) {
-                contentPackageEntryPath = "/META-INF/vault/" + Text.getName(jarEntry.getName()) + ".cnd";
-            }
-            try (Archive virtualArchive = SingleFileArchive.fromPathOrInputStream(tmpDocViewInputFile, bundleFileInputStream, 
-                    () -> Files.createTempFile(converter.getTempDirectory().toPath(), "initial-content", Text.getName(jarEntry.getName())), contentPackageEntryPath)) {
-                // in which content package should this end up?
-                VaultPackageAssembler packageAssembler = initPackageAssemblerForPath(bundleArtifact.getId(), repositoryPath, pathEntry.get(), packageAssemblers, converter);
-                if (tmpDocViewInputFile != null) {
-                    packageAssembler.addEntry(contentPackageEntryPath, tmpDocViewInputFile.toFile());
-                } else {
-                    packageAssembler.addEntry(contentPackageEntryPath, bundleFileInputStream);
-                }
-            }
-        } finally {
-            if (tmpDocViewInputFile != null) {
-                Files.delete(tmpDocViewInputFile);
-            }
-        }
-        return true;
-    }
-
-    JcrNamespaceRegistry createNamespaceRegistry(@NotNull Manifest manifest, @NotNull JarFile jarFile, @NotNull Map<String, String> predefinedNamespaceUriByPrefix) throws IOException {
-        try {
-            JcrNamespaceRegistry registry = new JcrNamespaceRegistry();
-            for (Map.Entry<String, String> entry : predefinedNamespaceUriByPrefix.entrySet()) {
-                registry.registerNamespace(entry.getKey(), entry.getValue());
-            }
-
-            // parse Sling-Namespaces header (https://github.com/apache/sling-org-apache-sling-jcr-base/blob/66be360910c265473799635fcac0e23895898913/src/main/java/org/apache/sling/jcr/base/internal/loader/Loader.java#L192)
-            final String namespacesDefinitionHeader = manifest.getMainAttributes().getValue(NAMESPACES_BUNDLE_HEADER);
-            if (namespacesDefinitionHeader != null) {
-                final StringTokenizer st = new StringTokenizer(namespacesDefinitionHeader, ",");
-
-                while ( st.hasMoreTokens() ) {
-                    final String token = st.nextToken().trim();
-                    int pos = token.indexOf('=');
-                    if ( pos == -1 ) {
-                        logger.warn("createNamespaceRegistry: Bundle {} has an invalid namespace manifest header entry: {}",
-                                manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME), token);
-                    } else {
-                        final String prefix = token.substring(0, pos).trim();
-                        final String namespace = token.substring(pos+1).trim();
-                        registry.registerNamespace(prefix, namespace);
-                    }
-                }
-            }
-
-            // parse Sling-Nodetypes header
-            final String typesHeader = manifest.getMainAttributes().getValue(NODETYPES_BUNDLE_HEADER);
-            if (typesHeader != null) {
-                for (ManifestHeader.Entry entry : ManifestHeader.parse(typesHeader).getEntries()) {
-                    JarEntry jarEntry = jarFile.getJarEntry(entry.getValue());
-                    if (jarEntry == null) {
-                        logger.warn("createNamespaceRegistry: Bundle {} has referenced a non existing node type definition: {}",
-                                manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME), entry.getValue());
-                    } else {
-                        try (InputStream inputStream = jarFile.getInputStream(jarEntry);
-                            Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
-                            registry.registerCnd(reader, entry.getValue());
-                        }
-                    }
-                }
-            }
-            return registry;
-        } catch ( final RepositoryException | ParseException e) {
-            throw new IOException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * Lazily initializes the cache with the necessary VaultPackageAssemblers
-     * @param bundleArtifactId
-     * @param repositoryPath
-     * @param cache
-     * @param converter
-     * @return the VaultPackageAssembler from the cache to use for the given repository path
-     */
-    public VaultPackageAssembler initPackageAssemblerForPath(@NotNull ArtifactId bundleArtifactId, @NotNull String repositoryPath, @NotNull PathEntry pathEntry, @NotNull Map<PackageType, VaultPackageAssembler> cache, @NotNull ContentPackage2FeatureModelConverter converter) 
-    throws ConverterException {
-        PackageType packageType = VaultPackageUtils.detectPackageType(repositoryPath);
-        VaultPackageAssembler assembler = cache.get(packageType);
-        if (assembler == null) {
-            final String packageNameSuffix;
-            switch (packageType) {
-                case APPLICATION:
-                    packageNameSuffix = "-apps";
-                    break;
-                case CONTENT:
-                    packageNameSuffix = "-content";
-                    break;
-                default:
-                    throw new ConverterException("Unexpected package type " + packageType + " detected for path " + repositoryPath);
-            }
-            final PackageId packageId = new PackageId(bundleArtifactId.getGroupId(), bundleArtifactId.getArtifactId()+packageNameSuffix, bundleArtifactId.getVersion());
-            assembler = VaultPackageAssembler.create(converter.getTempDirectory(), packageId, "Generated out of Sling Initial Content from bundle " + bundleArtifactId + " by cp2fm");
-            cache.put(packageType, assembler);
-            logger.info("Created package {} out of Sling-Initial-Content from '{}'", packageId, bundleArtifactId);
-        }
-        DefaultWorkspaceFilter filter = assembler.getFilter();
-        if (!filter.covers(repositoryPath)) {
-            PathFilterSet pathFilterSet = new PathFilterSet(pathEntry.getTarget() != null ? pathEntry.getTarget() : "/");
-            ImportMode importMode;
-            if (pathEntry.isOverwrite()) {
-                importMode = ImportMode.REPLACE;
-            } else {
-                importMode = ImportMode.MERGE;
-            }
-            // TODO: add handling for merge, mergeProperties and overwriteProperties (https://issues.apache.org/jira/browse/SLING-10318)
-            pathFilterSet.setImportMode(importMode);
-            filter.add(pathFilterSet);
-        }
-        return assembler;
-    }
-
-    void finalizePackageAssembly(@NotNull String path, @NotNull Map<PackageType, VaultPackageAssembler> packageAssemblers, @NotNull ContentPackage2FeatureModelConverter converter, @Nullable String runMode) throws IOException, ConverterException {
-        for (java.util.Map.Entry<PackageType, VaultPackageAssembler> entry : packageAssemblers.entrySet()) {
-            File packageFile = entry.getValue().createPackage();
-            converter.processSubPackage(path + "-" + entry.getKey(), runMode, converter.open(packageFile), false);
-        }
-    }
-
-    Map.Entry<ContentParser, ParserOptions> getContentParserForEntry(JarEntry entry, PathEntry pathEntry) {
-        if (entry.getName().endsWith(".json") && !pathEntry.isIgnoredImportProvider("json")) {
-            return new AbstractMap.SimpleEntry<>(new JSONContentParser(), new JSONParserOptions().withFeatures(EnumSet.of(JSONParserFeature.COMMENTS, JSONParserFeature.QUOTE_TICK)));
-        } else {
-            return null;
-        }
-    }
-
     protected @NotNull Artifact extractFeatureArtifact(@NotNull String bundleName, @NotNull JarFile jarFile) throws IOException {
         String artifactId = null;
         String version = null;
@@ -500,7 +233,7 @@ public class BundleEntryHandler extends AbstractRegexEntryHandler {
             }
         }
 
-        
+
         if (groupId == null) {
             // maybe the included jar is just an OSGi bundle but not a valid Maven artifact
             groupId = getCheckedProperty(jarFile.getManifest(), Constants.BUNDLE_SYMBOLICNAME);
@@ -538,8 +271,11 @@ public class BundleEntryHandler extends AbstractRegexEntryHandler {
             property = property.trim();
         }
         return requireNonNull(property, "Jar file can not be defined as a valid OSGi bundle without specifying a valid '"
-                                         + name
-                                         + "' property.");
+                + name
+                + "' property.");
     }
 
+    public void setBundleSlingInitialContentExtractor(BundleSlingInitialContentExtractor bundleSlingInitialContentExtractor) {
+        this.bundleSlingInitialContentExtractor = bundleSlingInitialContentExtractor;
+    }
 }
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/DefaultEntryHandlersManager.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/DefaultEntryHandlersManager.java
index 4491625..ed7015b 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/DefaultEntryHandlersManager.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/DefaultEntryHandlersManager.java
@@ -17,6 +17,7 @@
 package org.apache.sling.feature.cpconverter.handlers;
 
 import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.SlingInitialContentPolicy;
+import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.BundleSlingInitialContentExtractor;
 import org.apache.sling.feature.cpconverter.shared.ConverterConstants;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -32,11 +33,11 @@ public class DefaultEntryHandlersManager implements EntryHandlersManager {
     private final List<EntryHandler> entryHandlers = new LinkedList<>();
 
     public DefaultEntryHandlersManager() {
-        this(Collections.emptyMap(), false, SlingInitialContentPolicy.KEEP, ConverterConstants.SYSTEM_USER_REL_PATH_DEFAULT);
+        this(Collections.emptyMap(), false, SlingInitialContentPolicy.KEEP,new BundleSlingInitialContentExtractor(),  ConverterConstants.SYSTEM_USER_REL_PATH_DEFAULT);
     }
 
-    public DefaultEntryHandlersManager(@NotNull Map<String, String> configs, boolean enforceConfigurationsAndBundlesBelowProperFolder, 
-                                       @NotNull SlingInitialContentPolicy slingInitialContentPolicy, @NotNull String systemUserRelPath) {
+    public DefaultEntryHandlersManager(@NotNull Map<String, String> configs, boolean enforceConfigurationsAndBundlesBelowProperFolder,
+                                       @NotNull SlingInitialContentPolicy slingInitialContentPolicy, @NotNull BundleSlingInitialContentExtractor bundleSlingInitialContentExtractor,  @NotNull String systemUserRelPath) {
         ServiceLoader<EntryHandler> entryHandlersLoader = ServiceLoader.load(EntryHandler.class);
         for (EntryHandler entryHandler : entryHandlersLoader) {
             if (configs.containsKey(entryHandler.getClass().getName())) {
@@ -47,6 +48,7 @@ public class DefaultEntryHandlersManager implements EntryHandlersManager {
             } else if (entryHandler instanceof BundleEntryHandler) {
                 ((BundleEntryHandler) entryHandler).setEnforceBundlesBelowInstallFolder(enforceConfigurationsAndBundlesBelowProperFolder);
                 ((BundleEntryHandler) entryHandler).setSlingInitialContentPolicy(slingInitialContentPolicy);
+                ((BundleEntryHandler) entryHandler).setBundleSlingInitialContentExtractor(bundleSlingInitialContentExtractor);
             } else if (entryHandler instanceof AbstractUserEntryHandler) {
                 ((AbstractUserEntryHandler) entryHandler).setSystemUserRelPath(systemUserRelPath);
             }
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/SlingInitialContentBundleHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/SlingInitialContentBundleHandler.java
index 64330d0..2e43c46 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/SlingInitialContentBundleHandler.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/SlingInitialContentBundleHandler.java
@@ -16,11 +16,14 @@
  */
 package org.apache.sling.feature.cpconverter.handlers;
 
+
 import org.apache.jackrabbit.vault.packaging.PackageType;
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
 import org.apache.sling.feature.cpconverter.ConverterException;
+import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.BundleSlingInitialContentExtractor;
+import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.BundleSlingInitialContentExtractContext;
 import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -29,7 +32,6 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Path;
-import java.util.Map;
 import java.util.jar.JarFile;
 
 public class SlingInitialContentBundleHandler extends BundleEntryHandler {
@@ -39,22 +41,31 @@ public class SlingInitialContentBundleHandler extends BundleEntryHandler {
         this.handler = handler;
         setSlingInitialContentPolicy(slingInitialContentPolicy);
     }
-
+    
     @Override
     void processBundleInputStream(@NotNull String path, @NotNull Path originalBundleFile, @NotNull String bundleName, @Nullable String runMode, @Nullable Integer startLevel, @NotNull ContentPackage2FeatureModelConverter converter) throws IOException, ConverterException {
         try (JarFile jarFile = new JarFile(originalBundleFile.toFile())) {
             // first extract bundle metadata from JAR input stream
             Artifact artifact = extractFeatureArtifact(bundleName, jarFile);
-
-            try (InputStream ignored = extractSlingInitialContent(path, originalBundleFile, artifact, jarFile, converter, runMode)) {}
+            ArtifactId id = artifact.getId();
+            
+            BundleSlingInitialContentExtractContext context = new BundleSlingInitialContentExtractContext(slingInitialContentPolicy, path, id, jarFile, converter, runMode);
+            try (InputStream ignored = new BundleSlingInitialContentExtractorOverride().extract(context)) {
+                logger.info("Ignoring inputstream {} with id {}", path, id);
+            }
         }
     }
-
-    @Override
-    void finalizePackageAssembly(@NotNull String path, @NotNull Map<PackageType, VaultPackageAssembler> packageAssemblers, @NotNull ContentPackage2FeatureModelConverter converter, @Nullable String runMode) throws IOException, ConverterException {
-        for (java.util.Map.Entry<PackageType, VaultPackageAssembler> entry : packageAssemblers.entrySet()) {
-            File packageFile = entry.getValue().createPackage();
-            handler.processSubPackage(path + "-" + entry.getKey(), runMode, converter.open(packageFile), converter, true);
+    
+    class BundleSlingInitialContentExtractorOverride extends BundleSlingInitialContentExtractor{
+        @Override
+        protected void finalizePackageAssembly(@NotNull BundleSlingInitialContentExtractContext context) throws IOException, ConverterException {
+            for (java.util.Map.Entry<PackageType, VaultPackageAssembler> entry : assemblerProvider.getPackageAssemblerEntrySet()) {
+                File packageFile = entry.getValue().createPackage();
+                ContentPackage2FeatureModelConverter converter = context.getConverter();
+                handler.processSubPackage(context.getPath() + "-" + entry.getKey(), context.getRunMode(), converter.open(packageFile), converter, true);
+            }
         }
     }
+
+   
 }
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/AssemblerProvider.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/AssemblerProvider.java
new file mode 100644
index 0000000..91ae911
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/AssemblerProvider.java
@@ -0,0 +1,120 @@
+/*
+ * 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.slinginitialcontent;
+
+import org.apache.jackrabbit.vault.fs.api.ImportMode;
+import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
+import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
+import org.apache.jackrabbit.vault.packaging.PackageId;
+import org.apache.jackrabbit.vault.packaging.PackageType;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.cpconverter.ConverterException;
+import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
+import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageUtils;
+import org.apache.sling.jcr.contentloader.PathEntry;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Encapsulates the VaultPackage assembler logic for the sling initial content extraction
+ */
+public class AssemblerProvider {
+
+    private static final Logger logger = LoggerFactory.getLogger(AssemblerProvider.class);
+
+    private final Map<PackageType, VaultPackageAssembler> packageAssemblers = new EnumMap<>(PackageType.class);
+
+    AssemblerProvider() {
+    }
+
+    /**
+     * Lazily initializes the cache with the necessary VaultPackageAssemblers
+     *
+     * @param repositoryPath repository path of the sling initial content entry
+     * @return the VaultPackageAssembler from the cache to use for the given repository path
+     */
+    @NotNull
+    public VaultPackageAssembler initPackageAssemblerForPath(@NotNull BundleSlingInitialContentExtractContext context,
+                                                             @NotNull String repositoryPath,
+                                                             @NotNull PathEntry pathEntry)
+            throws ConverterException {
+
+        ArtifactId bundleArtifactId = context.getBundleArtifactId();
+        PackageType packageType = VaultPackageUtils.detectPackageType(repositoryPath);
+        VaultPackageAssembler assembler = lazyConstruct(context, repositoryPath, bundleArtifactId, packageType);
+        addPathFilterSetToAssemblerFilter(pathEntry, assembler);
+        return assembler;
+    }
+
+    @NotNull
+    public Set<Map.Entry<PackageType, VaultPackageAssembler>> getPackageAssemblerEntrySet() {
+        return packageAssemblers.entrySet();
+    }
+
+    @NotNull
+    private VaultPackageAssembler lazyConstruct(@NotNull BundleSlingInitialContentExtractContext context,
+                                                @NotNull String repositoryPath,
+                                                @NotNull ArtifactId bundleArtifactId,
+                                                @NotNull PackageType packageType) throws ConverterException {
+        VaultPackageAssembler assembler = packageAssemblers.get(packageType);
+        if (assembler == null) {
+            final String packageNameSuffix;
+            switch (packageType) {
+                case APPLICATION:
+                    packageNameSuffix = "-apps";
+                    break;
+                case CONTENT:
+                    packageNameSuffix = "-content";
+                    break;
+                default:
+                    throw new ConverterException("Unexpected package type " + packageType + " detected for path " + repositoryPath);
+            }
+            final PackageId packageId = new PackageId(bundleArtifactId.getGroupId(), bundleArtifactId.getArtifactId() + packageNameSuffix, bundleArtifactId.getVersion());
+            assembler = VaultPackageAssembler.create(context.getConverter().getTempDirectory(), packageId, "Generated out of Sling Initial Content from bundle " + bundleArtifactId + " by cp2fm");
+            packageAssemblers.put(packageType, assembler);
+            logger.info("Created package {} out of Sling-Initial-Content from '{}'", packageId, bundleArtifactId);
+        }
+        return assembler;
+    }
+
+    private void addPathFilterSetToAssemblerFilter(@NotNull PathEntry pathEntry, @NotNull VaultPackageAssembler assembler) {
+        ImportMode importMode;
+        if (pathEntry.isOverwrite()) {
+            importMode = ImportMode.UPDATE;
+        } else {
+            importMode = ImportMode.MERGE;
+        }
+
+        DefaultWorkspaceFilter filter = assembler.getFilter();
+        if (filter.getFilterSets().stream().noneMatch(set -> set.getRoot().equals(pathEntry.getTarget() != null ? pathEntry.getTarget() : "/") &&
+                set.getImportMode() == importMode)) {
+            PathFilterSet pathFilterSet = new PathFilterSet(pathEntry.getTarget() != null ? pathEntry.getTarget() : "/");
+            // TODO: add handling for merge, mergeProperties and overwriteProperties (https://issues.apache.org/jira/browse/SLING-10318)
+            pathFilterSet.setImportMode(importMode);
+            filter.add(pathFilterSet);
+        }
+    }
+
+    public void clear() {
+        this.packageAssemblers.clear();
+    }
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/BundleSlingInitialContentExtractContext.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/BundleSlingInitialContentExtractContext.java
new file mode 100644
index 0000000..390946a
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/BundleSlingInitialContentExtractContext.java
@@ -0,0 +1,120 @@
+/*
+ * 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.slinginitialcontent;
+
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.apache.sling.feature.cpconverter.vltpkg.JcrNamespaceRegistry;
+import org.apache.sling.jcr.contentloader.PathEntry;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+/**
+ * Holds various context variables for the BundleSlingInitialContentExtractor
+ */
+public class BundleSlingInitialContentExtractContext {
+
+    private final ContentPackage2FeatureModelConverter.SlingInitialContentPolicy slingInitialContentPolicy;
+    private final String path;
+    private final ArtifactId bundleArtifactId;
+    private final JarFile jarFile;
+    private final ContentPackage2FeatureModelConverter converter;
+    private final String runMode;
+    private final Manifest manifest;
+    private final JcrNamespaceRegistry namespaceRegistry;
+    private final List<PathEntry> pathEntryList = new ArrayList<>();
+
+    public BundleSlingInitialContentExtractContext(@NotNull ContentPackage2FeatureModelConverter.SlingInitialContentPolicy slingInitialContentPolicy,
+                                                   @NotNull String path,
+                                                   @NotNull ArtifactId bundleArtifactId,
+                                                   @NotNull JarFile jarFile,
+                                                   @NotNull ContentPackage2FeatureModelConverter converter,
+                                                   @Nullable String runMode) throws IOException {
+        this.slingInitialContentPolicy = slingInitialContentPolicy;
+        this.path = path;
+        this.bundleArtifactId = bundleArtifactId;
+        this.jarFile = jarFile;
+        this.converter = converter;
+        this.runMode = runMode;
+
+        this.manifest = Objects.requireNonNull(jarFile.getManifest());
+        this.namespaceRegistry =
+                new JcrNamespaceRegistryProvider(manifest,
+                        jarFile,
+                        converter.getFeaturesManager().getNamespaceUriByPrefix()
+                ).provideRegistryFromBundle();
+
+        Iterator<PathEntry> pathEntries = PathEntry.getContentPaths(manifest, -1);
+
+        if (pathEntries != null) {
+            pathEntries.forEachRemaining(pathEntryList::add);
+        }
+    }
+
+    @NotNull
+    public ContentPackage2FeatureModelConverter.SlingInitialContentPolicy getSlingInitialContentPolicy() {
+        return slingInitialContentPolicy;
+    }
+
+    @NotNull
+    public String getPath() {
+        return path;
+    }
+
+    @NotNull
+    public ArtifactId getBundleArtifactId() {
+        return bundleArtifactId;
+    }
+
+    @NotNull
+    public ContentPackage2FeatureModelConverter getConverter() {
+        return converter;
+    }
+
+    @Nullable
+    public String getRunMode() {
+        return runMode;
+    }
+
+    @NotNull
+    public JarFile getJarFile() {
+        return jarFile;
+    }
+
+    @NotNull
+    public Manifest getManifest() {
+        return manifest;
+    }
+
+    @NotNull
+    public JcrNamespaceRegistry getNamespaceRegistry() {
+        return namespaceRegistry;
+    }
+
+    @NotNull
+    public List<PathEntry> getPathEntryList() {
+        return new ArrayList<>(pathEntryList);
+    }
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/BundleSlingInitialContentExtractor.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/BundleSlingInitialContentExtractor.java
new file mode 100644
index 0000000..941b67f
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/BundleSlingInitialContentExtractor.java
@@ -0,0 +1,132 @@
+/*
+ * 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.slinginitialcontent;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.jackrabbit.vault.packaging.PackageType;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.apache.sling.feature.cpconverter.ConverterException;
+import org.apache.sling.feature.cpconverter.features.FeaturesManager;
+import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
+import org.apache.sling.jcr.contentloader.PathEntry;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+/**
+ * Extracts the sling initial content from a bundle to an java.io.InputStream.
+ */
+public class BundleSlingInitialContentExtractor {
+
+    protected final AssemblerProvider assemblerProvider = new AssemblerProvider();
+    protected final ContentReaderProvider contentReaderProvider = new ContentReaderProvider();
+    protected final ParentFolderRepoInitHandler parentFolderRepoInitHandler = new ParentFolderRepoInitHandler();
+
+    /**
+     * Extract the bundle sling initial content, assemble it into package assemblers into a different package,
+     * And strip the bundle of all the sling initial content.
+     *
+     * @param context all context variables needed to perform the extraction.
+     * @return stripped bundle inputstream
+     * @throws IOException
+     * @throws ConverterException
+     */
+    @Nullable
+    public InputStream extract(@NotNull BundleSlingInitialContentExtractContext context) throws IOException, ConverterException {
+
+        ContentPackage2FeatureModelConverter contentPackage2FeatureModelConverter = context.getConverter();
+
+        if (context.getSlingInitialContentPolicy() == ContentPackage2FeatureModelConverter.SlingInitialContentPolicy.KEEP) {
+            return null;
+        }
+        if (CollectionUtils.isEmpty(context.getPathEntryList())) {
+            return null;
+        }
+
+        // create a bundle file that will contain all non-sling initial content 
+        Path strippedBundleFile = getNewBundleFile(context, contentPackage2FeatureModelConverter);
+
+        // collect the metadata into a set first, we need all the data upfront in our second loop.
+        SlingInitialContentBundleEntryMetaDataCollector collector =
+                new SlingInitialContentBundleEntryMetaDataCollector(context, contentPackage2FeatureModelConverter, strippedBundleFile);
+        Set<SlingInitialContentBundleEntryMetaData> collectedSlingInitialContentBundleEntries = collector.collectFromContextAndWriteTmpFiles();
+
+        // now that we got collectedSlingInitialContentBundleEntries ready, we loop it and perform an extract for each entry.
+        // then add it into the appropriate vault package assemblers
+        BundleSlingInitialContentJarEntryExtractor jarEntryExtractor =
+                new BundleSlingInitialContentJarEntryExtractor(assemblerProvider, contentReaderProvider, parentFolderRepoInitHandler);
+
+        for (SlingInitialContentBundleEntryMetaData slingInitialContentBundleEntryMetaData : collectedSlingInitialContentBundleEntries) {
+            jarEntryExtractor.extractAndAddToAssembler(context, slingInitialContentBundleEntryMetaData, collectedSlingInitialContentBundleEntries);
+        }
+
+        // add additional content packages to feature model
+        finalizePackageAssembly(context);
+
+        // return bundle's inputstream, stripped off sling initial content, which must be deleted on close
+        return Files.newInputStream(strippedBundleFile, StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE);
+    }
+
+    static Version getModifiedOsgiVersion(@NotNull Version originalVersion) {
+        return new Version(originalVersion.getMajor(),
+                originalVersion.getMinor(),
+                originalVersion.getMicro(),
+                originalVersion.getQualifier() + "_" + ContentPackage2FeatureModelConverter.PACKAGE_CLASSIFIER);
+    }
+
+    public void reset() {
+        parentFolderRepoInitHandler.reset();
+    }
+
+    public void addRepoInitExtension(@NotNull List<VaultPackageAssembler> assemblers, @NotNull FeaturesManager featureManager) throws IOException, ConverterException {
+        parentFolderRepoInitHandler.addRepoinitExtension(assemblers, featureManager);
+    }
+
+    protected void finalizePackageAssembly(@NotNull BundleSlingInitialContentExtractContext context) throws IOException, ConverterException {
+        for (Map.Entry<PackageType, VaultPackageAssembler> entry : assemblerProvider.getPackageAssemblerEntrySet()) {
+            File packageFile = entry.getValue().createPackage();
+            ContentPackage2FeatureModelConverter converter = context.getConverter();
+            converter.processSubPackage(context.getPath() + "-" + entry.getKey(), context.getRunMode(), converter.open(packageFile), false);
+        }
+        assemblerProvider.clear();
+    }
+
+    private Path getNewBundleFile(@NotNull BundleSlingInitialContentExtractContext context, ContentPackage2FeatureModelConverter contentPackage2FeatureModelConverter) throws IOException {
+        final Manifest manifest = context.getManifest();
+        // remove header
+        manifest.getMainAttributes().remove(new Attributes.Name(PathEntry.CONTENT_HEADER));
+        // change version to have suffix
+        Version originalVersion = new Version(Objects.requireNonNull(manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION)));
+        manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION, getModifiedOsgiVersion(originalVersion).toString());
+        return Files.createTempFile(contentPackage2FeatureModelConverter.getTempDirectory().toPath(), "newBundle", ".jar");
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/BundleSlingInitialContentJarEntryExtractor.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/BundleSlingInitialContentJarEntryExtractor.java
new file mode 100644
index 0000000..25d31d2
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/BundleSlingInitialContentJarEntryExtractor.java
@@ -0,0 +1,150 @@
+/*
+ * 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.slinginitialcontent;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.jackrabbit.util.Text;
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.util.PlatformNameFormat;
+import org.apache.sling.feature.cpconverter.ConverterException;
+import org.apache.sling.feature.cpconverter.vltpkg.DocViewSerializerContentHandlerException;
+import org.apache.sling.feature.cpconverter.vltpkg.SingleFileArchive;
+import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
+import org.apache.sling.jcr.contentloader.ContentReader;
+import org.apache.sling.jcr.contentloader.PathEntry;
+import org.jetbrains.annotations.NotNull;
+
+import javax.jcr.RepositoryException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.Set;
+
+import static org.apache.sling.feature.cpconverter.shared.ConverterConstants.SLASH;
+
+/**
+ * Handles the bundle sling initial content extraction on the jarEntry level.
+ */
+class BundleSlingInitialContentJarEntryExtractor {
+
+    private final AssemblerProvider assemblerProvider;
+    private final ContentReaderProvider contentReaderProvider;
+    private final ParentFolderRepoInitHandler parentFolderRepoInitHandler;
+
+    BundleSlingInitialContentJarEntryExtractor(@NotNull AssemblerProvider assemblerProvider,
+                                               @NotNull ContentReaderProvider contentReaderProvider,
+                                               @NotNull ParentFolderRepoInitHandler parentFolderRepoInitHandler) {
+        this.assemblerProvider = assemblerProvider;
+        this.contentReaderProvider = contentReaderProvider;
+        this.parentFolderRepoInitHandler = parentFolderRepoInitHandler;
+    }
+
+    /**
+     * Extract slingInitialContentBundleEntryMetaData and add it to the assembler
+     *
+     * @param context                                   BundleSlingInitialContentExtractContext
+     * @param slingInitialContentBundleEntryMetaData    SlingInitialContentBundleEntryMetaData
+     * @param collectedSlingInitialContentBundleEntries complete set of all SlingInitialContentBundleEntryMetaData for the bundle
+     * @throws IOException
+     * @throws ConverterException
+     */
+    void extractAndAddToAssembler(@NotNull BundleSlingInitialContentExtractContext context,
+                                  @NotNull SlingInitialContentBundleEntryMetaData slingInitialContentBundleEntryMetaData,
+                                  @NotNull Set<SlingInitialContentBundleEntryMetaData> collectedSlingInitialContentBundleEntries) throws IOException, ConverterException {
+
+        String repositoryPath = slingInitialContentBundleEntryMetaData.getRepositoryPath();
+        File file = slingInitialContentBundleEntryMetaData.getTargetFile();
+        PathEntry pathEntryValue = slingInitialContentBundleEntryMetaData.getPathEntry();
+        // all entry paths used by entry handlers start with "/"
+        String contentPackageEntryPath = SLASH + org.apache.jackrabbit.vault.util.Constants.ROOT_DIR + PlatformNameFormat.getPlatformPath(repositoryPath);
+
+        Path tmpDocViewInputFile = null;
+
+        try (InputStream bundleFileInputStream = new FileInputStream(file)) {
+            VaultPackageAssembler packageAssembler = assemblerProvider.initPackageAssemblerForPath(context, repositoryPath, pathEntryValue);
+
+            final ContentReader contentReader = contentReaderProvider.getContentReaderForEntry(file, pathEntryValue);
+            if (contentReader != null) {
+
+                // convert to docview xml
+                tmpDocViewInputFile = Files.createTempFile(context.getConverter().getTempDirectory().toPath(), "docview", ".xml");
+                try (OutputStream docViewOutput = Files.newOutputStream(tmpDocViewInputFile, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
+
+                    repositoryPath = FilenameUtils.removeExtension(repositoryPath);
+                    boolean isFileDescriptorEntry = isFileDescriptor(collectedSlingInitialContentBundleEntries, contentPackageEntryPath);
+                    VaultContentXMLContentCreator contentCreator = new VaultContentXMLContentCreator(StringUtils.substringBeforeLast(repositoryPath, "/"), docViewOutput, context.getNamespaceRegistry(), packageAssembler, isFileDescriptorEntry);
+
+
+                    if (file.getName().endsWith(".xml")) {
+                        contentCreator.setIsXmlProcessed();
+                    }
+
+                    contentReader.parse(file.toURI().toURL(), contentCreator);
+                    contentPackageEntryPath = new ContentPackageEntryPathComputer(collectedSlingInitialContentBundleEntries, contentPackageEntryPath, contentCreator).compute();
+                    contentCreator.finish();
+
+                } catch (IOException e) {
+                    throw new IOException("Can not parse " + file, e);
+                } catch (DocViewSerializerContentHandlerException | RepositoryException e) {
+                    throw new IOException("Can not convert " + file + " to enhanced DocView format", e);
+                }
+
+                // remap CND files to make sure they are picked up by the NodeTypesEntryHandler
+                if (context.getNamespaceRegistry().getRegisteredCndSystemIds().contains(file.getName())) {
+                    contentPackageEntryPath = "/META-INF/vault/" + Text.getName(file.getName()) + ".cnd";
+                }
+
+
+            }
+
+            try (Archive virtualArchive = SingleFileArchive.fromPathOrInputStream(tmpDocViewInputFile, bundleFileInputStream,
+                    () -> Files.createTempFile(context.getConverter().getTempDirectory().toPath(), "initial-content", Text.getName(file.getName())), contentPackageEntryPath)) {
+                // in which content package should this end up?
+
+                if (tmpDocViewInputFile != null) {
+                    packageAssembler.addEntry(contentPackageEntryPath, tmpDocViewInputFile.toFile());
+                } else {
+                    packageAssembler.addEntry(contentPackageEntryPath, bundleFileInputStream);
+                }
+                parentFolderRepoInitHandler.addParentsForPath(contentPackageEntryPath);
+            }
+
+        } finally {
+            if (tmpDocViewInputFile != null) {
+                Files.delete(tmpDocViewInputFile);
+            }
+        }
+    }
+
+    @NotNull
+    private boolean isFileDescriptor(@NotNull Set<SlingInitialContentBundleEntryMetaData> bundleEntries, @NotNull final String contentPackageEntryPath) {
+
+        //sometimes we are dealing with double extensions (.json.xml)
+        String recomputedContentPackageEntryPath = FilenameUtils.removeExtension(contentPackageEntryPath);
+
+        final String checkIfRecomputedPathCandidate = StringUtils.removeStart(recomputedContentPackageEntryPath, "/jcr_root");
+        return bundleEntries.stream().anyMatch(bundleEntry -> StringUtils.equals(checkIfRecomputedPathCandidate, bundleEntry.getRepositoryPath()));
+
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/ContentPackageEntryPathComputer.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/ContentPackageEntryPathComputer.java
new file mode 100644
index 0000000..96ab646
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/ContentPackageEntryPathComputer.java
@@ -0,0 +1,74 @@
+/*
+ * 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.slinginitialcontent;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Set;
+
+import static org.apache.jackrabbit.vault.util.Constants.DOT_CONTENT_XML;
+import static org.apache.sling.feature.cpconverter.shared.ConverterConstants.SLASH;
+
+/**
+ * Performs re-computation of the ContentPackagePath of the bundle entry (Sling Initial Content)
+ */
+class ContentPackageEntryPathComputer {
+
+    private final Set<SlingInitialContentBundleEntryMetaData> bundleEntries;
+    private final String contentPackageEntryPath;
+    private final VaultContentXMLContentCreator contentCreator;
+
+    ContentPackageEntryPathComputer(@NotNull Set<SlingInitialContentBundleEntryMetaData> bundleEntries,
+                                    @NotNull final String contentPackageEntryPath,
+                                    @NotNull VaultContentXMLContentCreator contentCreator) {
+        this.bundleEntries = bundleEntries;
+        this.contentPackageEntryPath = contentPackageEntryPath;
+        this.contentCreator = contentCreator;
+    }
+
+    @NotNull
+    String compute() {
+
+        String recomputedContentPackageEntryPath = FilenameUtils.removeExtension(contentPackageEntryPath);
+
+        // this covers the case of having a primary node name defined in the xml/json descriptor itself.
+        // if this is set, we need to use it in the path.
+        if (StringUtils.isNotBlank(contentCreator.getPrimaryNodeName())) {
+            //custom node name
+            recomputedContentPackageEntryPath = StringUtils.substringBeforeLast(recomputedContentPackageEntryPath, SLASH);
+            recomputedContentPackageEntryPath = recomputedContentPackageEntryPath + SLASH + contentCreator.getPrimaryNodeName();
+        }
+
+        final String checkIfRecomputedPathCandidate = StringUtils.removeStart(recomputedContentPackageEntryPath, "/jcr_root");
+        //  check if the resulting candidate matches one of the repositoryPaths in the bundle entries we have.
+        //  for example        /apps/testJsonFile.json.xml (descriptor entry)
+        //  will match         /apps/testJsonFile.json (file entry)
+        if (bundleEntries.stream().anyMatch(bundleEntry -> StringUtils.equals(checkIfRecomputedPathCandidate, bundleEntry.getRepositoryPath()))) {
+            //we are dealing with a file descriptor here
+            recomputedContentPackageEntryPath = recomputedContentPackageEntryPath + ".dir/" + DOT_CONTENT_XML;
+        } else {
+            //  in this case we are dealing with a folder descriptor. for example:
+            //  /apps/testJsonFolder.json
+            //  we want it to end up in the following format: /apps/testJsonFolder/.content.xml in our assembler.
+            recomputedContentPackageEntryPath = recomputedContentPackageEntryPath + SLASH + DOT_CONTENT_XML;
+        }
+
+        return recomputedContentPackageEntryPath;
+    }
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/ContentReaderProvider.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/ContentReaderProvider.java
new file mode 100644
index 0000000..6dfac17
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/ContentReaderProvider.java
@@ -0,0 +1,55 @@
+/*
+ * 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.slinginitialcontent;
+
+import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.readers.XMLReader;
+import org.apache.sling.jcr.contentloader.ContentReader;
+import org.apache.sling.jcr.contentloader.PathEntry;
+import org.apache.sling.jcr.contentloader.internal.readers.JsonReader;
+import org.apache.sling.jcr.contentloader.internal.readers.ZipReader;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+
+/**
+ * Holds The ContentReader instances and provides them for pathEntries.
+ */
+class ContentReaderProvider {
+
+    static final JsonReader jsonReader = new JsonReader();
+    static final XMLReader xmlReader = new XMLReader();
+    static final ZipReader zipReader = new ZipReader();
+
+    @Nullable
+    ContentReader getContentReaderForEntry(@NotNull File entry, @NotNull PathEntry pathEntry) {
+        String entryName = entry.getName();
+        if (entryName.endsWith(".json") && !pathEntry.isIgnoredImportProvider("json")) {
+            return jsonReader;
+        } else if (entryName.endsWith(".xml") && !pathEntry.isIgnoredImportProvider("xml")) {
+            return xmlReader;
+        } else if (
+                (entryName.endsWith(".zip") && !pathEntry.isIgnoredImportProvider("zip")) ||
+                        (entryName.endsWith(".jar") && !pathEntry.isIgnoredImportProvider("jar"))
+        ) {
+            return zipReader;
+        } else {
+            return null;
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/JcrNamespaceRegistryProvider.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/JcrNamespaceRegistryProvider.java
new file mode 100644
index 0000000..828a87e
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/JcrNamespaceRegistryProvider.java
@@ -0,0 +1,122 @@
+/*
+ * 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.slinginitialcontent;
+
+import org.apache.jackrabbit.commons.cnd.ParseException;
+import org.apache.sling.commons.osgi.ManifestHeader;
+import org.apache.sling.feature.cpconverter.vltpkg.JcrNamespaceRegistry;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.framework.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+/**
+ * Encapsulates the JcrNamespace registry provision logic for Sling Initial Content
+ */
+class JcrNamespaceRegistryProvider {
+
+    public static final String NODETYPES_BUNDLE_HEADER = "Sling-Nodetypes";
+
+    public static final String NAMESPACES_BUNDLE_HEADER = "Sling-Namespaces";
+
+    private static final Logger logger = LoggerFactory.getLogger(JcrNamespaceRegistryProvider.class);
+
+    private final Manifest manifest;
+    private final JarFile jarFile;
+    private final Map<String, String> predefinedNamespaceUriByPrefix;
+
+    JcrNamespaceRegistryProvider(@NotNull Manifest manifest,
+                                 @NotNull JarFile jarFile,
+                                 @NotNull Map<String, String> predefinedNamespaceUriByPrefix) {
+
+        this.manifest = manifest;
+        this.jarFile = jarFile;
+        this.predefinedNamespaceUriByPrefix = predefinedNamespaceUriByPrefix;
+    }
+
+    @NotNull
+    JcrNamespaceRegistry provideRegistryFromBundle() throws IOException {
+        try {
+            JcrNamespaceRegistry registry = new JcrNamespaceRegistry();
+            for (Map.Entry<String, String> entry : predefinedNamespaceUriByPrefix.entrySet()) {
+                registry.registerNamespace(entry.getKey(), entry.getValue());
+            }
+
+            // parse Sling-Namespaces header (https://github.com/apache/sling-org-apache-sling-jcr-base/blob/66be360910c265473799635fcac0e23895898913/src/main/java/org/apache/sling/jcr/base/internal/loader/Loader.java#L192)
+            String namespacesDefinitionHeader = manifest.getMainAttributes().getValue(NAMESPACES_BUNDLE_HEADER);
+            if (namespacesDefinitionHeader != null) {
+                registerNamespacesIntoRegistry(registry, namespacesDefinitionHeader);
+            }
+
+            // parse Sling-Nodetypes header
+            String typesHeader = manifest.getMainAttributes().getValue(NODETYPES_BUNDLE_HEADER);
+            if (typesHeader != null) {
+                registerCndIntoRegistry(registry, typesHeader);
+            }
+            return registry;
+        } catch (final RepositoryException | ParseException e) {
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+
+    private void registerNamespacesIntoRegistry(@NotNull final JcrNamespaceRegistry registry,
+                                                @NotNull final String namespacesDefinitionHeader) throws RepositoryException {
+        final StringTokenizer st = new StringTokenizer(namespacesDefinitionHeader, ",");
+
+        while (st.hasMoreTokens()) {
+            final String token = st.nextToken().trim();
+            int pos = token.indexOf('=');
+            if (pos == -1) {
+                logger.warn("createNamespaceRegistry: Bundle {} has an invalid namespace manifest header entry: {}",
+                        manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME), token);
+            } else {
+                String prefix = token.substring(0, pos).trim();
+                String namespace = token.substring(pos + 1).trim();
+                registry.registerNamespace(prefix, namespace);
+            }
+        }
+    }
+
+    private void registerCndIntoRegistry(@NotNull final JcrNamespaceRegistry registry,
+                                         @NotNull final String typesHeader) throws IOException, ParseException, RepositoryException {
+        for (ManifestHeader.Entry entry : ManifestHeader.parse(typesHeader).getEntries()) {
+            JarEntry jarEntry = jarFile.getJarEntry(entry.getValue());
+            if (jarEntry == null) {
+                logger.warn("createNamespaceRegistry: Bundle {} has referenced a non existing node type definition: {}",
+                        manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME), entry.getValue());
+            } else {
+                try (InputStream inputStream = jarFile.getInputStream(jarEntry);
+                     Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
+                    registry.registerCnd(reader, entry.getValue());
+                }
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/ParentFolderRepoInitHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/ParentFolderRepoInitHandler.java
new file mode 100644
index 0000000..b190137
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/ParentFolderRepoInitHandler.java
@@ -0,0 +1,120 @@
+/*
+ * 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.slinginitialcontent;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.feature.cpconverter.ConverterException;
+import org.apache.sling.feature.cpconverter.features.FeaturesManager;
+import org.apache.sling.feature.cpconverter.repoinit.createpath.CreatePathSegmentProcessor;
+import org.apache.sling.feature.cpconverter.shared.RepoPath;
+import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
+import org.apache.sling.repoinit.parser.operations.CreatePath;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Formatter;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import static org.apache.jackrabbit.vault.util.Constants.DOT_CONTENT_XML;
+
+/**
+ * Handles creating the parent folders for sling initial content entries from the bundle
+ */
+class ParentFolderRepoInitHandler {
+
+    private static final Logger logger = LoggerFactory.getLogger(ParentFolderRepoInitHandler.class);
+
+    private final Set<RepoPath> parentFolderPaths = new HashSet<>();
+
+    /**
+     * Handles creating the parent folders for sling initial content entries from the bundle
+     *
+     * @param contentPackageEntryRepositoryPath the actual repository path of the entry
+     *                                          eg: /jcr_root/apps/mysite/component/component-a/.content.xml
+     */
+    void addParentsForPath(@NotNull String contentPackageEntryRepositoryPath) {
+
+        String parentFolder = contentPackageEntryRepositoryPath;
+        if (StringUtils.endsWith(contentPackageEntryRepositoryPath, DOT_CONTENT_XML)) {
+            parentFolder = StringUtils.substringBeforeLast(parentFolder, "/" + DOT_CONTENT_XML);
+        }
+        parentFolder = StringUtils.substringBeforeLast(parentFolder, "/");
+        parentFolder = StringUtils.substringAfter(parentFolder, "/jcr_root");
+
+        parentFolderPaths.add(new RepoPath(parentFolder));
+    }
+
+    void reset() {
+        parentFolderPaths.clear();
+    }
+
+    void addRepoinitExtension(@NotNull List<VaultPackageAssembler> assemblers,
+                              @NotNull FeaturesManager featureManager) throws IOException, ConverterException {
+
+        try (Formatter formatter = new Formatter()) {
+            parentFolderPaths.stream()
+                    .filter(entry -> parentFolderPaths.stream()
+                            .noneMatch(other -> !other.equals(
+                                    entry) &&
+                                    other.startsWith(entry)
+                            )
+                    )
+                    .filter(entry ->
+                            !entry.isRepositoryPath()
+                    )
+                    .map(entry ->
+                            // we want to make sure of all our entries that are repositoryPaths, 
+                            // we create repoinit statements to create the parent folders with proper types.
+                            // if we don't do this we will end up with constraintViolationExceptions.
+                            getCreatePath(entry, assemblers)
+                    )
+                    .filter(Objects::nonNull)
+
+                    .forEach(
+                            createPath -> formatter.format("%s", createPath.asRepoInitString())
+                    );
+
+            String text = formatter.toString();
+
+            if (!text.isEmpty()) {
+                featureManager.addOrAppendRepoInitExtension("content-package", text, null);
+            }
+        }
+
+    }
+
+
+    @Nullable
+    CreatePath getCreatePath(@NotNull RepoPath path, @NotNull Collection<VaultPackageAssembler> packageAssemblers) {
+        if (path.getParent() == null) {
+            logger.debug("Omit create path statement for path '{}'", path);
+            return null;
+        }
+
+        CreatePath cp = new CreatePath("sling:Folder");
+        CreatePathSegmentProcessor.processSegments(path, packageAssemblers, cp);
+        return cp;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaData.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaData.java
new file mode 100644
index 0000000..d8b15be
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaData.java
@@ -0,0 +1,77 @@
+/*
+ * 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.slinginitialcontent;
+
+import org.apache.sling.jcr.contentloader.PathEntry;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.Objects;
+
+/**
+ * Holds BundleEntry MetaData.
+ */
+class SlingInitialContentBundleEntryMetaData {
+
+    private final File targetFile;
+    private final PathEntry pathEntry;
+    private final String repositoryPath;
+
+    SlingInitialContentBundleEntryMetaData(
+            @NotNull File targetFile,
+            @NotNull PathEntry pathEntry,
+            @NotNull String repositoryPath) {
+        this.targetFile = targetFile;
+        this.pathEntry = pathEntry;
+        this.repositoryPath = repositoryPath;
+    }
+
+    @NotNull
+    File getTargetFile() {
+        return targetFile;
+    }
+
+    @NotNull
+    PathEntry getPathEntry() {
+        return pathEntry;
+    }
+
+    @NotNull
+    String getRepositoryPath() {
+        return repositoryPath;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SlingInitialContentBundleEntryMetaData that = (SlingInitialContentBundleEntryMetaData) o;
+        return repositoryPath.equals(that.repositoryPath);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(targetFile, pathEntry, repositoryPath);
+    }
+
+    @Override
+    public String toString() {
+        return "SlingInitialContentBundleEntryMetaData{" +
+                "repositoryPath='" + repositoryPath + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaDataCollector.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaDataCollector.java
new file mode 100644
index 0000000..9b0bb8a
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaDataCollector.java
@@ -0,0 +1,204 @@
+/*
+ * 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.slinginitialcontent;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.apache.sling.jcr.contentloader.PathEntry;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import static org.apache.sling.feature.cpconverter.shared.ConverterConstants.SLASH;
+
+/**
+ * Handles collecting the metadata for each sling initial content entry, to be used for extraction in another loop
+ */
+class SlingInitialContentBundleEntryMetaDataCollector {
+
+    private static final double THRESHOLD_RATIO = 10;
+    private static final int BUFFER = 512;
+    private static final long TOOBIG = 0x6400000; // Max size of unzipped data, 100MB
+
+    private final BundleSlingInitialContentExtractContext context;
+    private final String basePath;
+    private final ContentPackage2FeatureModelConverter contentPackage2FeatureModelConverter;
+    private final Path newBundleFile;
+    private final Set<SlingInitialContentBundleEntryMetaData> collectedSlingInitialContentBundleEntries = new HashSet<>();
+    private final AtomicLong total = new AtomicLong(0);
+    private final JarFile jarFile;
+
+    SlingInitialContentBundleEntryMetaDataCollector(@NotNull BundleSlingInitialContentExtractContext context,
+                                                    @NotNull ContentPackage2FeatureModelConverter contentPackage2FeatureModelConverter,
+                                                    @NotNull Path newBundleFile) {
+        this.context = context;
+        this.basePath = contentPackage2FeatureModelConverter.getTempDirectory().getPath();
+        this.contentPackage2FeatureModelConverter = contentPackage2FeatureModelConverter;
+        this.newBundleFile = newBundleFile;
+        this.jarFile = context.getJarFile();
+    }
+
+    /**
+     * Collects all the MetaData from the context into a set
+     *
+     * @return
+     * @throws IOException
+     */
+    @SuppressWarnings("java:S5042") // we already addressed this
+    @NotNull
+    Set<SlingInitialContentBundleEntryMetaData> collectFromContextAndWriteTmpFiles() throws IOException {
+
+        final Manifest manifest = context.getManifest();
+
+        // create JAR file to prevent extracting it twice and for random access
+        try (OutputStream fileOutput = Files.newOutputStream(newBundleFile, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
+             JarOutputStream bundleOutput = new JarOutputStream(fileOutput, manifest)) {
+
+            Enumeration<? extends JarEntry> entries = jarFile.entries();
+
+            // first we collect all the entries into a set, collectedSlingInitialContentBundleEntries.
+            // we need it up front to be perform various checks in another loop later.
+            while (entries.hasMoreElements()) {
+                JarEntry jarEntry = entries.nextElement();
+
+                if (jarEntry.getName().equals(JarFile.MANIFEST_NAME)) {
+                    continue;
+                }
+
+                if (!jarEntry.isDirectory()) {
+                    extractFile(jarEntry, bundleOutput);
+                }
+
+                if (total.get() + BUFFER > TOOBIG) {
+                    throw new IllegalStateException("File being unzipped is too big.");
+                }
+            }
+        }
+
+        return collectedSlingInitialContentBundleEntries;
+    }
+
+    private void extractFile(JarEntry jarEntry, JarOutputStream bundleOutput) throws IOException {
+
+        byte[] data = new byte[BUFFER];
+        long compressedSize = jarEntry.getCompressedSize();
+
+        try (InputStream input = new BufferedInputStream(jarFile.getInputStream(jarEntry))) {
+            if (jarEntryIsSlingInitialContent(context, jarEntry)) {
+
+                File targetFile = new File(contentPackage2FeatureModelConverter.getTempDirectory(), jarEntry.getName());
+                String canonicalDestinationPath = targetFile.getCanonicalPath();
+
+
+                if (!checkIfPathStartsWithOrIsEqual(contentPackage2FeatureModelConverter.getTempDirectory().getCanonicalPath(), canonicalDestinationPath)) {
+                    throw new IOException("Entry is outside of the target directory");
+                }
+
+                targetFile.getParentFile().mkdirs();
+                if (!targetFile.exists() && !targetFile.createNewFile()) {
+                    throw new IOException("Could not create placeholder file!");
+                }
+
+                FileOutputStream fos = new FileOutputStream(targetFile);
+                safelyWriteOutputStream(compressedSize, data, input, fos, true);
+
+                SlingInitialContentBundleEntryMetaData bundleEntry = createSlingInitialContentBundleEntry(context, targetFile);
+                collectedSlingInitialContentBundleEntries.add(bundleEntry);
+            } else {
+                //write 'normal' content out to the normal bundle output
+                bundleOutput.putNextEntry(jarEntry);
+                safelyWriteOutputStream(compressedSize, data, input, bundleOutput, false);
+                IOUtils.copy(input, bundleOutput);
+                bundleOutput.closeEntry();
+            }
+        }
+    }
+
+    private void safelyWriteOutputStream(@NotNull long compressedSize,
+                                         @NotNull byte[] data,
+                                         @NotNull InputStream input,
+                                         @NotNull OutputStream fos,
+                                         boolean shouldClose) throws IOException {
+        int count;
+        BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);
+        while (total.get() + BUFFER <= TOOBIG && (count = input.read(data, 0, BUFFER)) != -1) {
+            dest.write(data, 0, count);
+            total.addAndGet(count);
+
+            double compressionRatio = (double) count / compressedSize;
+            if (compressionRatio > THRESHOLD_RATIO) {
+                // ratio between compressed and uncompressed data is highly suspicious, looks like a Zip Bomb Attack
+                break;
+            }
+        }
+        dest.flush();
+
+        if (shouldClose) {
+            dest.close();
+        }
+
+    }
+
+    private boolean jarEntryIsSlingInitialContent(@NotNull BundleSlingInitialContentExtractContext context, @NotNull JarEntry jarEntry) {
+        final String entryName = jarEntry.getName();
+        return context.getPathEntryList().stream().anyMatch(
+                pathEntry -> checkIfPathStartsWithOrIsEqual(pathEntry.getPath(), entryName)
+        );
+    }
+
+    @NotNull
+    private SlingInitialContentBundleEntryMetaData createSlingInitialContentBundleEntry(@NotNull BundleSlingInitialContentExtractContext context,
+                                                                                        @NotNull File targetFile) throws UnsupportedEncodingException {
+        final String entryName = StringUtils.substringAfter(targetFile.getPath(), basePath + "/");
+        final PathEntry pathEntryValue = context.getPathEntryList().stream().filter(
+                pathEntry -> checkIfPathStartsWithOrIsEqual(pathEntry.getPath(), entryName)
+        ).findFirst().orElseThrow(NullPointerException::new);
+        final String target = pathEntryValue.getTarget();
+        // https://sling.apache.org/documentation/bundles/content-loading-jcr-contentloader.html#file-name-escaping
+        String repositoryPath = (target != null ? target : "/") + URLDecoder.decode(entryName.substring(pathEntryValue.getPath().length()), "UTF-8");
+        return new SlingInitialContentBundleEntryMetaData(targetFile, pathEntryValue, repositoryPath);
+    }
+
+
+    private static boolean checkIfPathStartsWithOrIsEqual(String pathA, String pathB) {
+        String fixedPath = pathA;
+        if (!fixedPath.endsWith(SLASH)) {
+            fixedPath = pathA + SLASH;
+        }
+        return pathB.startsWith(fixedPath) || pathB.equals(pathA);
+    }
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/VaultContentXMLContentCreator.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/VaultContentXMLContentCreator.java
new file mode 100644
index 0000000..8c92dfd
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/VaultContentXMLContentCreator.java
@@ -0,0 +1,232 @@
+/*
+ * 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.slinginitialcontent;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.xmlbuffer.XMLNode;
+import org.apache.sling.feature.cpconverter.vltpkg.JcrNamespaceRegistry;
+import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
+import org.apache.sling.jcr.contentloader.ContentCreator;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.xml.stream.XMLStreamException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * ContentCreator substitute to create valid XML files to be packaged into a VaultPackage to be installed later
+ */
+public class VaultContentXMLContentCreator implements ContentCreator {
+
+    private static final String ACL_NOT_SUPPORTED_MSG = "Sling Initial Content - ACL statements are not supported yet . SLING issue: https://issues.apache.org/jira/browse/SLING-11060";
+
+    private final String repositoryPath;
+    private final OutputStream targetOutputStream;
+    private final VaultPackageAssembler packageAssembler;
+    private final LinkedList<XMLNode> parentNodePathStack = new LinkedList<>();
+    private final JcrNamespaceRegistry namespaceRegistry;
+
+    private final boolean isFileDescriptorEntry;
+
+    private boolean isFirstElement = true;
+    private boolean finished = false;
+    private boolean xmlProcessed = false;
+    private String primaryNodeName;
+    private XMLNode currentNode;
+
+    VaultContentXMLContentCreator(@NotNull String repositoryPath,
+                                  @NotNull OutputStream targetOutputStream,
+                                  @NotNull JcrNamespaceRegistry namespaceRegistry,
+                                  @NotNull VaultPackageAssembler packageAssembler,
+                                  boolean isFileDescriptorEntry) {
+        this.repositoryPath = repositoryPath;
+        this.targetOutputStream = targetOutputStream;
+        this.packageAssembler = packageAssembler;
+        this.namespaceRegistry = namespaceRegistry;
+        this.isFileDescriptorEntry = isFileDescriptorEntry;
+    }
+
+    void setIsXmlProcessed() {
+        this.xmlProcessed = true;
+    }
+
+    @Override
+    public void createNode(String name, String primaryNodeType, String[] mixinNodeTypes) throws RepositoryException {
+
+        final String elementName;
+        final String jcrNodeName;
+        if (xmlProcessed && isFirstElement) {
+            elementName = "jcr:root";
+            primaryNodeName = name;
+            jcrNodeName = name;
+            isFirstElement = false;
+        } else if (StringUtils.isNotBlank(name)) {
+            elementName = getValidElementName(name);
+            jcrNodeName = name;
+        } else {
+            elementName = "jcr:root";
+            jcrNodeName = null;
+        }
+
+
+        final String basePath;
+        if (parentNodePathStack.isEmpty()) {
+            basePath = repositoryPath;
+        } else {
+            StringBuilder basePathBuilder = new StringBuilder(repositoryPath);
+            for (Iterator<XMLNode> xmlNodeIterator = parentNodePathStack.descendingIterator(); xmlNodeIterator.hasNext(); ) {
+                XMLNode parent = xmlNodeIterator.next();
+                String parentJcrNodeName = parent.getJcrNodeName();
+                if (StringUtils.isNotBlank(parentJcrNodeName)) {
+                    basePathBuilder.append("/");
+                    basePathBuilder.append(parentJcrNodeName);
+                }
+            }
+            basePath = basePathBuilder.toString();
+        }
+
+        //if we are dealing with a descriptor file, we should use nt:file as default primaryType. 
+
+        String defaultNtType = isFileDescriptorEntry ? JcrConstants.NT_FILE : JcrConstants.NT_UNSTRUCTURED;
+        String toUsePrimaryNodeType = StringUtils.isNotBlank(primaryNodeType) ? primaryNodeType : defaultNtType;
+        XMLNode intermediateNode = new XMLNode(packageAssembler, basePath, elementName, jcrNodeName, toUsePrimaryNodeType, mixinNodeTypes);
+        //add the created node to the correct parent if present
+        if (currentNode != null) {
+            currentNode.addChildNode(elementName, intermediateNode);
+        }
+        //switch the current node 
+        currentNode = intermediateNode;
+
+        if (ArrayUtils.isNotEmpty(mixinNodeTypes)) {
+            currentNode.addProperty(JcrConstants.JCR_MIXINTYPES, "[" + String.join(",", mixinNodeTypes) + "]");
+        }
+
+        parentNodePathStack.push(currentNode);
+    }
+
+    @NotNull
+    private static String getValidElementName(@NotNull String name) {
+        if (StringUtils.isNumeric(name.substring(0, 1))) {
+            return "_" + name;
+        }
+        return name;
+    }
+
+    @Nullable
+    public String getPrimaryNodeName() {
+        return primaryNodeName;
+    }
+
+    @Override
+    public void finishNode() {
+        if (parentNodePathStack.size() > 1) {
+            this.parentNodePathStack.pop();
+        }
+        this.currentNode = this.parentNodePathStack.peek();
+    }
+
+    @Override
+    public void finish() throws RepositoryException {
+
+        if (finished) {
+            return;
+        }
+        try {
+            XMLNodeToXMLFileWriter writer = new XMLNodeToXMLFileWriter(currentNode, targetOutputStream, namespaceRegistry);
+            writer.write();
+            finished = true;
+        } catch (XMLStreamException e) {
+            throw new RepositoryException(e);
+        }
+    }
+
+
+    @Override
+    public void createProperty(String name, int propertyType, String value) throws RepositoryException {
+        currentNode.addProperty(name, propertyType, value);
+    }
+
+
+    @Override
+    public void createProperty(String name, int propertyType, String[] values) throws RepositoryException {
+        currentNode.addProperty(name, propertyType, values);
+    }
+
+
+    @Override
+    public void createProperty(String name, Object value) throws RepositoryException {
+        currentNode.addProperty(name, value);
+    }
+
+
+    @Override
+    public void createProperty(String name, Object[] values) throws RepositoryException {
+        currentNode.addProperty(name, values);
+    }
+
+    @Override
+    public void createFileAndResourceNode(String name, InputStream data, String mimeType, long lastModified) throws RepositoryException {
+        this.createNode(name, JcrConstants.NT_FILE, null);
+        this.createNode(JcrConstants.JCR_CONTENT, JcrConstants.NT_RESOURCE, null);
+
+        // ensure sensible last modification date
+        if (lastModified <= 0) {
+            lastModified = System.currentTimeMillis();
+        }
+        this.createProperty(JcrConstants.JCR_MIMETYPE, mimeType);
+        this.createProperty(JcrConstants.JCR_LASTMODIFIED, lastModified);
+        this.createProperty(JcrConstants.JCR_DATA, data);
+    }
+
+
+    @Override
+    public boolean switchCurrentNode(String subPath, String newNodeType) {
+        throw new UnsupportedOperationException(ACL_NOT_SUPPORTED_MSG);
+    }
+
+    @Override
+    public void createUser(String name, String password, Map<String, Object> extraProperties) {
+        throw new UnsupportedOperationException(ACL_NOT_SUPPORTED_MSG);
+    }
+
+    @Override
+    public void createGroup(String name, String[] members, Map<String, Object> extraProperties) {
+        throw new UnsupportedOperationException(ACL_NOT_SUPPORTED_MSG);
+    }
+
+    @Override
+    public void createAce(String principal, String[] grantedPrivileges, String[] deniedPrivileges, String order) {
+        throw new UnsupportedOperationException(ACL_NOT_SUPPORTED_MSG);
+    }
+
+    @Override
+    public void createAce(String principalId, String[] grantedPrivilegeNames, String[] deniedPrivilegeNames,
+                          String order, Map<String, Value> restrictions, Map<String, Value[]> mvRestrictions,
+                          Set<String> removedRestrictionNames) {
+        throw new UnsupportedOperationException(ACL_NOT_SUPPORTED_MSG);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/XMLNodeToXMLFileWriter.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/XMLNodeToXMLFileWriter.java
new file mode 100644
index 0000000..a218d60
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/XMLNodeToXMLFileWriter.java
@@ -0,0 +1,110 @@
+/*
+ * 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.slinginitialcontent;
+
+
+import javanet.staxutils.IndentingXMLEventWriter;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.xmlbuffer.XMLNode;
+import org.apache.sling.feature.cpconverter.vltpkg.JcrNamespaceRegistry;
+import org.jetbrains.annotations.NotNull;
+
+import javax.jcr.RepositoryException;
+import javax.xml.stream.XMLEventFactory;
+import javax.xml.stream.XMLEventWriter;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+import static org.apache.jackrabbit.vault.util.JcrConstants.JCR_MIXINTYPES;
+import static org.apache.jackrabbit.vault.util.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.vault.util.JcrConstants.NT_UNSTRUCTURED;
+
+/**
+ * Takes the buffered parent XMLNode and writes it to an actual XML file
+ */
+class XMLNodeToXMLFileWriter {
+
+    private final XMLNode parentNode;
+    private final XMLEventWriter eventWriter;
+    private final JcrNamespaceRegistry namespaceRegistry;
+    private final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
+
+    XMLNodeToXMLFileWriter(@NotNull XMLNode parentNode,
+                           @NotNull OutputStream targetOutputStream,
+                           @NotNull JcrNamespaceRegistry namespaceRegistry) throws XMLStreamException {
+        this.parentNode = parentNode;
+        XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(targetOutputStream, StandardCharsets.UTF_8.name());
+        this.eventWriter = new IndentingXMLEventWriter(
+                writer
+        );
+        this.namespaceRegistry = namespaceRegistry;
+        this.eventWriter.setNamespaceContext(this.namespaceRegistry);
+
+    }
+
+    void write() throws XMLStreamException, RepositoryException {
+
+        eventWriter.add(eventFactory.createStartDocument());
+        writeNode(parentNode, true);
+        eventWriter.add(eventFactory.createEndDocument());
+
+    }
+
+    void writeNode(@NotNull XMLNode xmlNode, boolean isFirstElement) throws RepositoryException, XMLStreamException {
+
+        eventWriter.add(eventFactory.createStartElement(StringUtils.EMPTY, StringUtils.EMPTY, xmlNode.getXmlElementName()));
+
+        if (isFirstElement) {
+            for (String prefix : namespaceRegistry.getPrefixes()) {
+                eventWriter.add(eventFactory.createNamespace(prefix, namespaceRegistry.getURI(prefix)));
+            }
+        }
+
+        String primaryNodeType = xmlNode.getPrimaryNodeType();
+        String[] mixinNodeTypes = xmlNode.getMixinNodeTypes();
+
+
+        eventWriter.add(eventFactory.createAttribute(JCR_PRIMARYTYPE, StringUtils.isNotBlank(primaryNodeType) ? primaryNodeType : NT_UNSTRUCTURED));
+
+        if (ArrayUtils.isNotEmpty(mixinNodeTypes)) {
+            eventWriter.add(eventFactory.createAttribute(JCR_MIXINTYPES, "[" + String.join(",", mixinNodeTypes) + "]"));
+        }
+
+        for (Map.Entry<String, String> entry : xmlNode.getVltXmlParsedProperties().entrySet()) {
+
+            if (entry.getKey().equals(JCR_PRIMARYTYPE) || entry.getKey().equals(JCR_MIXINTYPES)) {
+                continue;
+            }
+
+            eventWriter.add(eventFactory.createAttribute(entry.getKey(), entry.getValue()));
+        }
+
+        for (XMLNode node : xmlNode.getChildren().values()) {
+            writeNode(node, false);
+        }
+
+        eventWriter.add(eventFactory.createEndElement(StringUtils.EMPTY, StringUtils.EMPTY, xmlNode.getXmlElementName()));
+
+
+    }
+
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/PrimaryTypeParser.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/readers/XMLReader.java
similarity index 52%
copy from src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/PrimaryTypeParser.java
copy to src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/readers/XMLReader.java
index 49799aa..ac34617 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/PrimaryTypeParser.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/readers/XMLReader.java
@@ -14,30 +14,15 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.sling.feature.cpconverter.accesscontrol;
+package org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.readers;
 
-import org.apache.sling.feature.cpconverter.shared.AbstractJcrNodeParser;
-import org.xml.sax.Attributes;
+import org.apache.sling.jcr.contentloader.internal.readers.XmlReader;
 
-final class PrimaryTypeParser extends AbstractJcrNodeParser<String> {
-
-    public PrimaryTypeParser() {
-        super();
-    }
-
-    @Override
-    protected void onJcrRootNode(String uri, String localName, String qName, Attributes attributes, String primaryType) {
-        detectedPrimaryType = primaryType;
-    }
-
-    @Override
-    protected void onJcrRootElement(String uri, String localName, String qName, Attributes attributes) {
-        // not needed
-    }
-
-    @Override
-    protected String getParsingResult() {
-        return detectedPrimaryType;
+/**
+ * Extends the XMLReader to perform the activate method on construction, since it's protected.
+ */
+public class XMLReader extends XmlReader {
+    public XMLReader() {
+        this.activate();
     }
-
 }
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/xmlbuffer/XMLNode.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/xmlbuffer/XMLNode.java
new file mode 100644
index 0000000..dcad4b6
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/xmlbuffer/XMLNode.java
@@ -0,0 +1,158 @@
+/*
+ * 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.slinginitialcontent.xmlbuffer;
+
+import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Represents an XML node in the buffer to be written in a sling initial content package later
+ */
+public class XMLNode {
+
+    private static final String FORMAT_SINGLE_VALUE = "%s";
+    private static final String FORMAT_SINGLE_VALUE_TYPED = "{%s}%s";
+    private static final String FORMAT_MULTI_VALUE = "[%s]";
+    private static final String FORMAT_MULTI_VALUE_TYPED = "{%s}[%s]";
+
+    private final String basePath;
+    private final String xmlElementName;
+    private final String jcrNodeName;
+    private final String primaryNodeType;
+    private final String[] mixinNodeTypes;
+
+    private final VaultPackageAssembler packageAssembler;
+    private final Map<String, String> vltXmlParsedProperties = new HashMap<>();
+    private final Map<String, XMLNode> children = new LinkedHashMap<>();
+
+    public XMLNode(@NotNull VaultPackageAssembler packageAssembler,
+                   @NotNull String basePath,
+                   @NotNull String xmlElementName,
+                   @Nullable String jcrNodeName,
+                   @NotNull String primaryNodeType,
+                   @Nullable String[] mixinNodeTypes) {
+        this.packageAssembler = packageAssembler;
+        this.basePath = basePath;
+        this.xmlElementName = xmlElementName;
+        this.jcrNodeName = jcrNodeName;
+        this.primaryNodeType = primaryNodeType;
+        this.mixinNodeTypes = mixinNodeTypes;
+    }
+
+    public void addProperty(@NotNull String name, int propertyType, @NotNull String value) {
+        String propertyTypeName = PropertyType.nameFromValue(propertyType);
+
+        if (propertyType > 0) {
+            vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE_TYPED, propertyTypeName, value));
+        } else {
+            vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE, value));
+        }
+
+    }
+
+    public void addProperty(@NotNull String name, int propertyType, @NotNull String[] values) {
+        String propertyTypeName = PropertyType.nameFromValue(propertyType);
+
+        if (propertyType > 0) {
+            vltXmlParsedProperties.put(name, String.format(FORMAT_MULTI_VALUE_TYPED, propertyTypeName, String.join(",", values)));
+        } else {
+            vltXmlParsedProperties.put(name, String.format(FORMAT_MULTI_VALUE, String.join(",", values)));
+        }
+
+    }
+
+    public void addChildNode(@NotNull String name, @NotNull XMLNode xmlNode) {
+        this.children.put(name, xmlNode);
+    }
+
+    public void addProperty(@NotNull String name, @Nullable Object value) throws RepositoryException {
+        if (value == null) {
+            return;
+        }
+
+        if (value instanceof Long) {
+            vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE_TYPED, "Long", value.toString()));
+        } else if (value instanceof Date) {
+            vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE_TYPED, "Date", ((Date) value).toGMTString()));
+        } else if (value instanceof Calendar) {
+            vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE_TYPED, "Date", ((Calendar) value).getTime().toGMTString()));
+        } else if (value instanceof Double) {
+            vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE_TYPED, "Double", value));
+        } else if (value instanceof Boolean) {
+            boolean theBoolValue = Boolean.parseBoolean(value.toString());
+            vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE_TYPED, "Boolean", theBoolValue));
+        } else if (value instanceof InputStream) {
+            vltXmlParsedProperties.put(name, "{Binary}");
+            String path = "jcr_root/" + this.basePath;
+            try {
+                packageAssembler.addEntry(path, (InputStream) value);
+            } catch (IOException e) {
+                throw new RepositoryException(e);
+            }
+
+        } else {
+            vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE, value));
+        }
+
+    }
+
+    @NotNull
+    public String getPath() {
+        return this.basePath + "/" + this.jcrNodeName;
+    }
+
+    @NotNull
+    public String getXmlElementName() {
+        return xmlElementName;
+    }
+
+    @Nullable
+    public String getJcrNodeName() {
+        return jcrNodeName;
+    }
+
+    @NotNull
+    public String getPrimaryNodeType() {
+        return primaryNodeType;
+    }
+
+    @Nullable
+    public String[] getMixinNodeTypes() {
+        return mixinNodeTypes;
+    }
+
+    @NotNull
+    public Map<String, XMLNode> getChildren() {
+        return children;
+    }
+
+    @NotNull
+    public Map<String, String> getVltXmlParsedProperties() {
+        return vltXmlParsedProperties;
+    }
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/repoinit/createpath/CreatePathSegmentProcessor.java b/src/main/java/org/apache/sling/feature/cpconverter/repoinit/createpath/CreatePathSegmentProcessor.java
new file mode 100644
index 0000000..9e9185a
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/repoinit/createpath/CreatePathSegmentProcessor.java
@@ -0,0 +1,105 @@
+/*
+ * 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.repoinit.createpath;
+
+import org.apache.jackrabbit.vault.util.PlatformNameFormat;
+import org.apache.sling.feature.cpconverter.shared.RepoPath;
+import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
+import org.apache.sling.repoinit.parser.operations.CreatePath;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static org.apache.jackrabbit.vault.util.Constants.DOT_CONTENT_XML;
+
+public class CreatePathSegmentProcessor {
+
+    private CreatePathSegmentProcessor() {
+    }
+
+    /**
+     * Process segments of a repopath to createpath, checking packageassemblers for existing primaryType definitions.
+     *
+     * @param path
+     * @param packageAssemblers
+     * @param cp
+     * @return
+     */
+    public static boolean processSegments(@NotNull RepoPath path, @NotNull Collection<VaultPackageAssembler> packageAssemblers, @NotNull CreatePath cp) {
+        StringBuilder platformPath = new StringBuilder();
+        boolean foundType = false;
+        for (String part : path.getSegments()) {
+            String platformName = PlatformNameFormat.getPlatformName(part);
+            platformPath.append(platformPath.toString().isEmpty() ? platformName : "/" + platformName);
+
+            boolean segmentAdded = false;
+            //loop all package assemblers and check if .content.xml is defined
+            for (VaultPackageAssembler packageAssembler : packageAssemblers) {
+                File currentContent = packageAssembler.getEntry(platformPath + "/" + DOT_CONTENT_XML);
+                if (currentContent.exists() && currentContent.isFile()) {
+                    //add segment if jcr:primaryType is defined.
+                    segmentAdded = addSegment(cp, part, currentContent);
+                    if (segmentAdded) {
+                        foundType = true;
+                        break;
+                    }
+                }
+            }
+            if (!segmentAdded) {
+                //use sling:Folder (defined by repo-init runtime module)
+                cp.addSegment(part, null);
+            }
+        }
+        return foundType;
+    }
+
+    private static boolean addSegment(@NotNull CreatePath cp, @NotNull String part, @NotNull File currentContent) {
+        try (FileInputStream input = new FileInputStream(currentContent);
+             FileInputStream input2 = new FileInputStream(currentContent)) {
+            String primary = new PrimaryTypeParser().parse(input);
+            if (primary != null) {
+                List<String> mixins = new ArrayList<>();
+                String mixin = new MixinParser().parse(input2);
+                if (mixin != null) {
+                    mixin = mixin.trim();
+                    if (mixin.startsWith("[")) {
+                        mixin = mixin.substring(1, mixin.length() - 1);
+                    }
+                    for (String m : mixin.split(",")) {
+                        String mixinName = m.trim();
+                        if (!mixinName.isEmpty()) {
+                            mixins.add(mixinName);
+                        }
+                    }
+                }
+                cp.addSegment(part, primary, mixins);
+                return true;
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("A fatal error occurred while parsing the '"
+                    + currentContent
+                    + "' file, see nested exceptions: "
+                    + e);
+        }
+        return false;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/MixinParser.java b/src/main/java/org/apache/sling/feature/cpconverter/repoinit/createpath/MixinParser.java
similarity index 91%
rename from src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/MixinParser.java
rename to src/main/java/org/apache/sling/feature/cpconverter/repoinit/createpath/MixinParser.java
index 236e401..f6a9c36 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/MixinParser.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/repoinit/createpath/MixinParser.java
@@ -14,13 +14,13 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.sling.feature.cpconverter.accesscontrol;
+package org.apache.sling.feature.cpconverter.repoinit.createpath;
 
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.sling.feature.cpconverter.shared.AbstractJcrNodeParser;
 import org.xml.sax.Attributes;
 
-final class MixinParser extends AbstractJcrNodeParser<String> {
+public final class MixinParser extends AbstractJcrNodeParser<String> {
     private String mixins;
 
     public MixinParser() {
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/PrimaryTypeParser.java b/src/main/java/org/apache/sling/feature/cpconverter/repoinit/createpath/PrimaryTypeParser.java
similarity index 90%
rename from src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/PrimaryTypeParser.java
rename to src/main/java/org/apache/sling/feature/cpconverter/repoinit/createpath/PrimaryTypeParser.java
index 49799aa..22a2b44 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/PrimaryTypeParser.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/repoinit/createpath/PrimaryTypeParser.java
@@ -14,12 +14,12 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.sling.feature.cpconverter.accesscontrol;
+package org.apache.sling.feature.cpconverter.repoinit.createpath;
 
 import org.apache.sling.feature.cpconverter.shared.AbstractJcrNodeParser;
 import org.xml.sax.Attributes;
 
-final class PrimaryTypeParser extends AbstractJcrNodeParser<String> {
+public final class PrimaryTypeParser extends AbstractJcrNodeParser<String> {
 
     public PrimaryTypeParser() {
         super();
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/shared/ConverterConstants.java b/src/main/java/org/apache/sling/feature/cpconverter/shared/ConverterConstants.java
index a76f96a..94507fc 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/shared/ConverterConstants.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/shared/ConverterConstants.java
@@ -22,4 +22,5 @@ public final class ConverterConstants {
     
     public static final String SYSTEM_USER_REL_PATH_DEFAULT = "system";
     
+    public static final String SLASH = "/";
 }
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/DocViewSerializerContentHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/DocViewSerializerContentHandler.java
index 89f4a97..672650a 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/DocViewSerializerContentHandler.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/DocViewSerializerContentHandler.java
@@ -16,16 +16,7 @@
  */
 package org.apache.sling.feature.cpconverter.vltpkg;
 
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.AbstractMap;
-import java.util.Map;
-
-import javax.jcr.NamespaceException;
-import javax.jcr.RepositoryException;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-
+import org.apache.commons.lang3.StringUtils;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException;
 import org.apache.jackrabbit.spi.commons.conversion.NameParser;
@@ -35,6 +26,18 @@ import org.apache.jackrabbit.vault.fs.impl.io.DocViewSAXFormatter;
 import org.apache.jackrabbit.vault.fs.io.DocViewFormat;
 import org.apache.jackrabbit.vault.util.xml.serialize.FormattingXmlStreamWriter;
 import org.apache.sling.contentparser.api.ContentHandler;
+import org.jetbrains.annotations.NotNull;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.RepositoryException;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.AbstractMap;
+import java.util.Map;
+
+import static org.apache.commons.lang3.StringUtils.isNotBlank;
 
 /**
  * Similar to {@link DocViewSAXFormatter} but works outside a repository-based context on the input generated through {@link ContentHandler} callbacks.
@@ -52,7 +55,8 @@ public class DocViewSerializerContentHandler implements ContentHandler, AutoClos
         try {
             writer = FormattingXmlStreamWriter.create(outputStream, new DocViewFormat().getXmlOutputFormat());
             writer.writeStartDocument();
-            
+            writer.setNamespaceContext(nsRegistry);
+
         } catch (XMLStreamException e) {
             throw new DocViewSerializerContentHandlerException("Can not start document", e);
         }
@@ -74,7 +78,8 @@ public class DocViewSerializerContentHandler implements ContentHandler, AutoClos
         try {
             // now split by prefix and local name
             Map.Entry<String, Name> prefixAndQualifiedName = resolvePrefixedName(name);
-            writer.writeStartElement(prefixAndQualifiedName.getKey(), prefixAndQualifiedName.getValue().getNamespaceURI(), prefixAndQualifiedName.getValue().getLocalName());
+            writeElement(prefixAndQualifiedName);
+
             if (isFirstElement) {
                 for (String prefix : nsRegistry.getPrefixes()) {
                     writer.writeNamespace(prefix, nsRegistry.getURI(prefix));
@@ -84,7 +89,12 @@ public class DocViewSerializerContentHandler implements ContentHandler, AutoClos
             for (Map.Entry<String, Object> property : properties.entrySet()) {
                 // now split by prefix and local name
                 prefixAndQualifiedName = resolvePrefixedName(property.getKey());
-                writer.writeAttribute(prefixAndQualifiedName.getKey(), prefixAndQualifiedName.getValue().getNamespaceURI(), prefixAndQualifiedName.getValue().getLocalName(), ValueConverter.toString(property.getKey(), property.getValue()));
+                writer.writeAttribute(
+                        prefixAndQualifiedName.getKey(),
+                        prefixAndQualifiedName.getValue().getNamespaceURI(),
+                        prefixAndQualifiedName.getValue().getLocalName(),
+                        ValueConverter.toString(property.getKey(), property.getValue())
+                );
             }
         } catch (XMLStreamException e) {
             throw new DocViewSerializerContentHandlerException("Can not start element", e);
@@ -93,6 +103,41 @@ public class DocViewSerializerContentHandler implements ContentHandler, AutoClos
         }
     }
 
+    private boolean writeElement(Map.Entry<String, Name> prefixAndQualifiedName) throws XMLStreamException {
+
+        String key = prefixAndQualifiedName.getKey();
+        String namespaceURI = prefixAndQualifiedName.getValue().getNamespaceURI();
+        String localName = getValidLocalName(prefixAndQualifiedName);
+
+        if (isNotBlank(localName)) {
+            if (isNotBlank(namespaceURI) && isNotBlank(nsRegistry.getPrefix(namespaceURI)) && isNotBlank(key)) {
+                //uri already registered in context, this method will do
+                writer.writeStartElement(namespaceURI, localName);
+            } else if (isNotBlank(namespaceURI) && isNotBlank(key)) {
+                //uri not registered in context, so writing it out completely
+                writer.writeStartElement(key, namespaceURI, localName);
+            } else {
+                writer.writeStartElement(localName);
+            }
+        } else {
+            return false;
+        }
+
+        return true;
+    }
+
+    @NotNull
+    private String getValidLocalName(Map.Entry<String, Name> prefixAndQualifiedName) {
+        String localName = prefixAndQualifiedName.getValue().getLocalName();
+
+        String firstCharacter = localName.substring(0, 1);
+        if (StringUtils.isNumeric(firstCharacter)) {
+            //fix localName starting out with numbers as this is illegal.
+            localName = "_x003" + firstCharacter + "_" + StringUtils.substring(localName, 1);
+        }
+        return localName;
+    }
+
     public void closeParents(String stopAtParent) {
         try {
             while (!currentPath.equals(stopAtParent)) {
@@ -117,7 +162,7 @@ public class DocViewSerializerContentHandler implements ContentHandler, AutoClos
         try {
             String prefix = name.substring(0, posColon);
             return new AbstractMap.SimpleEntry<>(prefix, NameParser.parse(name, nsRegistry, NameFactoryImpl.getInstance()));
-        } catch (IllegalNameException|NamespaceException e) {
+        } catch (IllegalNameException | NamespaceException e) {
             throw new DocViewSerializerContentHandlerException("Could not resolve namespace URI for name " + name, e);
         }
     }
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 d35304b..e1ea0b5 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,30 +16,37 @@
  */
 package org.apache.sling.feature.cpconverter.vltpkg;
 
+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.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 {
+public class JcrNamespaceRegistry implements NamespaceRegistry, NamespaceResolver, NamespaceContext {
     
     private final Collection<String> registeredCndSystemIds = new ArrayList<>();
     private final NodeTypeManagerProvider ntManagerProvider = new NodeTypeManagerProvider();
     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");
@@ -83,14 +90,30 @@ public class JcrNamespaceRegistry implements NamespaceRegistry, NamespaceResolve
     }
 
     @Override
-    public String getPrefix(String uri) throws NamespaceException {
+    public String getNamespaceURI(String prefix) {
+        try {
+            return ntManagerProvider.getURI(prefix);
+        } catch (RepositoryException e) {
+            logger.info("Could not find prefix {} in registered namespaces", prefix);
+            return StringUtils.EMPTY;
+        }
+    }
+
+    @Override
+    public String getPrefix(String uri) {
         try {
             return ntManagerProvider.getPrefix(uri);
         } catch (RepositoryException e) {
-            throw new NamespaceException(e);
+            logger.info("Could not find uri {} in registered namespaces", uri);
+            return null;
         }
     }
 
+    @Override
+    public Iterator<String> getPrefixes(String namespaceURI) {
+        return Collections.singletonList(getPrefix(namespaceURI)).iterator();
+    }
+
     public @NotNull Collection<String> getRegisteredCndSystemIds() {
         return registeredCndSystemIds;
     }
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssembler.java b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssembler.java
index e3692e2..6553df3 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssembler.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssembler.java
@@ -60,6 +60,7 @@ import static org.apache.jackrabbit.vault.util.Constants.META_DIR;
 import static org.apache.jackrabbit.vault.util.Constants.PROPERTIES_XML;
 import static org.apache.jackrabbit.vault.util.Constants.ROOT_DIR;
 import static org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.PACKAGE_CLASSIFIER;
+import static org.apache.sling.feature.cpconverter.shared.ConverterConstants.SLASH;
 import static org.apache.sling.feature.cpconverter.vltpkg.VaultPackageUtils.getDependencies;
 import static org.apache.sling.feature.cpconverter.vltpkg.VaultPackageUtils.setDependencies;
 import static org.apache.sling.feature.cpconverter.vltpkg.VaultPackageUtils.toRepositoryPath;
@@ -233,8 +234,13 @@ public class VaultPackageAssembler {
     }
 
     public @NotNull File getEntry(@NotNull String path) {
-        if (!path.startsWith(ROOT_DIR)) {
-            path = ROOT_DIR + path;
+        if (!path.startsWith(ROOT_DIR + SLASH)) {
+            if(path.startsWith(SLASH)){
+                path = ROOT_DIR + path;
+            }else{
+                path = ROOT_DIR + SLASH + path;
+            }
+           
         }
 
         return new File(storingDirectory, path);
@@ -358,7 +364,7 @@ public class VaultPackageAssembler {
             }
         }
     }
-
+    
     private static final class RemoveInstallHooksPredicate implements Predicate<Map.Entry<Object, Object>> {
         @Override
         public boolean test(java.util.Map.Entry<Object, Object> entry) {
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 b828f59..0792378 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/AdjustedFilterTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/AdjustedFilterTest.java
@@ -26,6 +26,7 @@ import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager;
 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.shared.ConverterConstants;
 import org.apache.sling.feature.cpconverter.vltpkg.DefaultPackagesEventsEmitter;
 import org.jetbrains.annotations.NotNull;
@@ -50,7 +51,7 @@ public class AdjustedFilterTest extends AbstractConverterTest {
     private ContentPackage2FeatureModelConverter converter;
     private File outputDirectory;
     
-    private final EntryHandlersManager handlersManager = spy(new DefaultEntryHandlersManager(Collections.emptyMap(), false, ContentPackage2FeatureModelConverter.SlingInitialContentPolicy.KEEP, ConverterConstants.SYSTEM_USER_REL_PATH_DEFAULT));
+    private final EntryHandlersManager handlersManager = spy(new DefaultEntryHandlersManager(Collections.emptyMap(), false, ContentPackage2FeatureModelConverter.SlingInitialContentPolicy.KEEP,new BundleSlingInitialContentExtractor(),  ConverterConstants.SYSTEM_USER_REL_PATH_DEFAULT));
 
     @Before
     public void setUp() throws Exception {
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 f5f3843..4aea947 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sling.feature.cpconverter;
 
+import static org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.SlingInitialContentPolicy.EXTRACT_AND_REMOVE;
 import static org.apache.sling.feature.cpconverter.Util.normalize;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -34,7 +35,6 @@ import java.io.IOException;
 import java.io.Reader;
 import java.io.StringReader;
 import java.net.URL;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.util.*;
 import java.util.zip.ZipFile;
@@ -45,18 +45,14 @@ import javax.json.JsonObject;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.jackrabbit.vault.fs.spi.PrivilegeDefinitions;
-import org.apache.jackrabbit.vault.packaging.Dependency;
 import org.apache.jackrabbit.vault.packaging.PackageId;
 import org.apache.jackrabbit.vault.packaging.PackageProperties;
 import org.apache.jackrabbit.vault.packaging.PackageType;
 import org.apache.jackrabbit.vault.packaging.VaultPackage;
 import org.apache.jackrabbit.vault.packaging.impl.PackageManagerImpl;
-import org.apache.jackrabbit.vault.packaging.impl.ZipVaultPackage;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Artifacts;
-import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Configurations;
 import org.apache.sling.feature.Extension;
@@ -71,6 +67,8 @@ 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.EntryHandlersManager;
+import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.BundleSlingInitialContentExtractor;
+import org.apache.sling.feature.cpconverter.shared.ConverterConstants;
 import org.apache.sling.feature.cpconverter.vltpkg.DefaultPackagesEventsEmitter;
 import org.apache.sling.feature.io.json.FeatureJSONReader;
 import org.junit.After;
@@ -93,8 +91,6 @@ public class ContentPackage2FeatureModelConverterTest extends AbstractConverterT
                                                                 "test_b-1.0.zip",
                                                                 "test_e-1.0.zip" };
 
-    private static final String FORMAT = "%1$s/%2$s/%3$s/%2$s-%3$s-cp2fm-converted.zip";
-
     private ContentPackage2FeatureModelConverter converter;
     private EntryHandlersManager handlersManager;
         
@@ -103,6 +99,7 @@ public class ContentPackage2FeatureModelConverterTest extends AbstractConverterT
         handlersManager = new DefaultEntryHandlersManager();
         converter = new ContentPackage2FeatureModelConverter()
                     .setEntryHandlersManager(handlersManager)
+                    .setFeaturesManager(new DefaultFeaturesManager(new File("")))
                     .setAclManager(new DefaultAclManager());
     }
 
@@ -144,97 +141,6 @@ public class ContentPackage2FeatureModelConverterTest extends AbstractConverterT
     }
 
     @Test
-    public void checkIfGeneratedDependenciesAreProvided() throws Exception {
-        URL packageUrl = getClass().getResource("build_playground.ui.content-1.0-SNAPSHOT.zip");
-        File packageFile = FileUtils.toFile(packageUrl);
-
-        File outputDirectory = new File(System.getProperty("java.io.tmpdir"), getClass().getName() + '_' + System.currentTimeMillis());
-        File unreferencedOutputDir = new File(outputDirectory, "mutable-content-packages");
-        try {
-
-            converter = new ContentPackage2FeatureModelConverter(false, ContentPackage2FeatureModelConverter.SlingInitialContentPolicy.EXTRACT_AND_REMOVE)
-                    .setEntryHandlersManager(handlersManager)
-                    .setAclManager(new DefaultAclManager());
-            
-            converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, null, null, new HashMap<>(), new DefaultAclManager()))
-                    .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
-                    .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
-                    .setContentTypePackagePolicy(PackagePolicy.PUT_IN_DEDICATED_FOLDER)
-                    .setUnreferencedArtifactsDeployer(new LocalMavenRepositoryArtifactsDeployer(unreferencedOutputDir))
-                    .convert(packageFile);
-            
-             
-            File featureModel = new File(outputDirectory, "playground.ui.content.json");
-            Feature feature =  FeatureJSONReader.read(new FileReader(featureModel), "");
-            
-            List<PackageId> allPackageIds = new ArrayList<>();
-            List<Dependency> contentRefsDependencies = new ArrayList<>();
-                    
-            getContentPackagesFromFeatureModel(outputDirectory, feature,allPackageIds);
-            getContentPackagesFromRefs(outputDirectory,allPackageIds,contentRefsDependencies);
-            
-
-            for(Dependency dependency : contentRefsDependencies){
-                boolean foundDep = allPackageIds.stream().filter(Objects::nonNull).anyMatch(dependency::matches);
-                assertTrue("Dependency " + dependency.getName() + " is not present in the feature model or content refs", foundDep);
-            }
-            
-        } finally {
-            deleteDirTree(outputDirectory);
-        }
-    }
-
-    private void getContentPackagesFromFeatureModel(File outputDirectory, Feature feature, List<PackageId> packageIds) throws IOException {
-
-        Artifacts artifacts = feature.getExtensions().getByName("content-packages").getArtifacts();
-        for(Artifact artifact : artifacts){
-            ArtifactId id = artifact.getId();
-
-            String target = getPathTarget(id);
-            File file = new File(outputDirectory, target);
-            ZipVaultPackage vaultPackage = new ZipVaultPackage(file, true);
-            //read the artifact from disk and compute the artifactId from here
-            packageIds.add( vaultPackage.getProperties().getId() );
-        };
-
-    }
-
-    private void getContentPackagesFromRefs(File outputDirectory, List<PackageId> packageIds, List<Dependency> dependencies) throws IOException {
-        File contentPackagesCSV = new File(outputDirectory, "content-packages.csv");
-
-        List<String> contentPackages = IOUtils.readLines(new FileInputStream(contentPackagesCSV), StandardCharsets.UTF_8);
-
-        for(String contentPackageLine: contentPackages) {
-
-            if (contentPackageLine.startsWith("#")) {
-                continue;
-            }
-
-            String[] contentPackageLineSplit = contentPackageLine.split(",");
-
-            String artifactIdUnparsed = contentPackageLineSplit[1];
-            ArtifactId artifact = ArtifactId.fromMvnId(artifactIdUnparsed);
-
-            String target = "mutable-content-packages/" + getPathTarget(artifact);
-
-            File contentPackageFile = new File(outputDirectory, target);
-            ZipVaultPackage vaultPackage = new ZipVaultPackage(contentPackageFile, true);
-            packageIds.add(vaultPackage.getProperties().getId());
-
-
-            dependencies.addAll(Arrays.asList(vaultPackage.getProperties().getDependencies()));
-
-        }
-    }
-
-    private String getPathTarget(ArtifactId artifact) {
-        String groupIdPath = StringUtils.replace(artifact.getGroupId(), ".", "/");
-
-        return String.format(FORMAT, groupIdPath, artifact.getArtifactId(), artifact.getVersion());
-    }
-
-
-    @Test
     public void convertContentPackage() throws Exception {
         URL packageUrl = getClass().getResource("test-content-package.zip");
         File packageFile = FileUtils.toFile(packageUrl);
@@ -382,7 +288,7 @@ public class ContentPackage2FeatureModelConverterTest extends AbstractConverterT
             deleteDirTree(outputDirectory);
         }
     }
-
+    
     @Test
     public void convertContentPackagePutInDedicatedFolderContentTypePackagePolicy() throws Exception {
         URL packageUrl = getClass().getResource("test-content-package.zip");
@@ -1007,6 +913,4 @@ public class ContentPackage2FeatureModelConverterTest extends AbstractConverterT
 
         return loadedResources;
     }
-
-   
 }
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 fc272e0..6ff8aea 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/ConverterUserAndPermissionTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/ConverterUserAndPermissionTest.java
@@ -34,6 +34,7 @@ import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager;
 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.shared.ConverterConstants;
 import org.apache.sling.feature.cpconverter.vltpkg.DefaultPackagesEventsEmitter;
 import org.apache.sling.feature.io.json.FeatureJSONReader;
@@ -135,7 +136,7 @@ public class ConverterUserAndPermissionTest  extends AbstractConverterTest {
     public ConverterUserAndPermissionTest(@NotNull String systemUserRelPath, @Nullable String enforcePrincipalBasedSupportedPath, @NotNull String name) throws Exception {
         this.aclManager = new DefaultAclManager(enforcePrincipalBasedSupportedPath, systemUserRelPath);
         this.handlersManager = new DefaultEntryHandlersManager(Collections.emptyMap(), false, 
-                ContentPackage2FeatureModelConverter.SlingInitialContentPolicy.KEEP, systemUserRelPath);
+                ContentPackage2FeatureModelConverter.SlingInitialContentPolicy.KEEP, new BundleSlingInitialContentExtractor(), systemUserRelPath);
         this.enforcePrincipalBased = (enforcePrincipalBasedSupportedPath != null);
         this.withRelPath = (enforcePrincipalBased) ? enforcePrincipalBasedSupportedPath.substring(enforcePrincipalBasedSupportedPath.indexOf(systemUserRelPath)) : systemUserRelPath;
     }
@@ -250,7 +251,7 @@ public class ConverterUserAndPermissionTest  extends AbstractConverterTest {
                 assertEquals(normalize(
                         "create service user eventproxy-service with path system/eventproxy\n" +
                                 "    create path /content\n" +
-                                "    create path /var/eventproxy\n" +
+                                "    create path /var/eventproxy(nt:unstructured mixin rep:AccessControllable)\n" +
                                 "    set ACL for eventproxy-service\n" +
                                 "        allow jcr:read on /content\n" +
                                 "        allow jcr:read,rep:write on /var/eventproxy\n" +
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandleSlingInitialContentTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandleSlingInitialContentTest.java
index 1c72c82..6fa5143 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandleSlingInitialContentTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandleSlingInitialContentTest.java
@@ -16,19 +16,39 @@
  */
 package org.apache.sling.feature.cpconverter.handlers;
 
+
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.xmlunit.assertj.XmlAssert.assertThat;
 
 import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.lang.reflect.Type;
 import java.util.Collections;
 import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Properties;
+import java.util.Set;
 import java.util.jar.JarFile;
 
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
 import org.apache.jackrabbit.vault.packaging.PackageId;
@@ -39,8 +59,12 @@ import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.SlingInitialContentPolicy;
+import org.apache.sling.feature.cpconverter.accesscontrol.DefaultAclManager;
 import org.apache.sling.feature.cpconverter.artifacts.SimpleFolderArtifactsDeployer;
+import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.BundleSlingInitialContentExtractor;
+import org.apache.sling.feature.cpconverter.shared.ConverterConstants;
 import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
+import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -50,6 +74,14 @@ import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnitRunner;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xmlunit.builder.Input;
+import org.xmlunit.diff.ComparisonType;
+import org.xmlunit.diff.DOMDifferenceEngine;
+import org.xmlunit.diff.DifferenceEngine;
+
+import javax.xml.transform.Source;
 
 @RunWith(MockitoJUnitRunner.class)
 public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntryHandlerTest {
@@ -60,6 +92,9 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr
     @Captor
     ArgumentCaptor<Configuration> cfgCaptor;
 
+    @Captor
+    ArgumentCaptor<String> repoinitTextCaptor;
+    
     @Test
     public void testSlingInitialContent() throws Exception {
         setUpArchive("/jcr_root/apps/gav/install/io.wcm.handler.media-1.11.6.jar", "io.wcm.handler.media-1.11.6.jar");
@@ -79,10 +114,16 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr
         props.setProperty(PackageProperties.NAME_VERSION, "1.11.6");
         when(assembler.getPackageProperties()).thenReturn(props);
         converter.setMainPackageAssembler(assembler);
-
+        converter.setAclManager(new DefaultAclManager());
+        
+        BundleSlingInitialContentExtractor extractor = new BundleSlingInitialContentExtractor();
+        
+        handler.setBundleSlingInitialContentExtractor(extractor);
         handler.setSlingInitialContentPolicy(SlingInitialContentPolicy.EXTRACT_AND_REMOVE);
         handler.handle("/jcr_root/apps/gav/install/io.wcm.handler.media-1.11.6.jar", archive, entry, converter);
-
+        
+        extractor.addRepoInitExtension(converter.getAssemblers(), featuresManager);
+        
         converter.deployPackages();
         // verify generated bundle
         try (JarFile jarFile = new JarFile(new File(targetFolder, "io.wcm.handler.media-1.11.6-cp2fm-converted.jar"))) {
@@ -99,14 +140,38 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr
             archive.open(true);
             PackageId targetId = PackageId.fromString("io.wcm:io.wcm.handler.media-apps:1.11.6-cp2fm-converted");
             assertEquals(targetId, vaultPackage.getId());
-            Entry entry = archive.getEntry("jcr_root/apps/wcm-io/handler/media/components/global/include/responsiveImageSettings.xml");
+            Entry entry = archive.getEntry("jcr_root/apps/wcm-io/handler/media/components/global/include/responsiveImageSettings/.content.xml");
             assertNotNull("Archive does not contain expected item", entry);
+
+
+            Set<String> expectedCreatePathStatements = new HashSet<>();
+            expectedCreatePathStatements.add("create path (sling:Folder) /apps/wcm-io/handler/media/content");
+            expectedCreatePathStatements.add("create path (sling:Folder) /apps/wcm-io/handler/media/components/granite/form/mediaformatselect(cq:Component)");
+            expectedCreatePathStatements.add("create path (sling:Folder) /apps/wcm-io/handler/media/i18n");
+            expectedCreatePathStatements.add("create path (sling:Folder) /apps/wcm-io/handler/media/components/global/include");
+            expectedCreatePathStatements.add("create path (sling:Folder) /apps/wcm-io/handler/media/components/placeholder");
+            expectedCreatePathStatements.add("create path (sling:Folder) /apps/wcm-io/handler/media/clientlibs/authoring/dialog(cq:ClientLibraryFolder)/css");
+            expectedCreatePathStatements.add("create path (sling:Folder) /apps/wcm-io/handler/media/components/granite/form/fileupload(cq:Component)");
+            expectedCreatePathStatements.add("create path (sling:Folder) /apps/wcm-io/handler/media/clientlibs/authoring/dialog(cq:ClientLibraryFolder)/js");
+            expectedCreatePathStatements.add("create path (sling:Folder) /apps/wcm-io/handler/media/components/granite/datasources/mediaformats");
+            expectedCreatePathStatements.add("create path (sling:Folder) /apps/wcm-io/handler/media/docroot/resources/img");
+            expectedCreatePathStatements.add("create path (sling:Folder) /apps/wcm-io/handler/media/components/granite/global");
+            expectedCreatePathStatements.add("create path (sling:Folder) /apps/wcm-io/handler/media/components/granite/form/pathfield(cq:Component)");
+
+       
+           
+            verify(featuresManager, times(1)).addOrAppendRepoInitExtension(eq("content-package"), repoinitTextCaptor.capture(), Mockito.isNull());
+
+            for(String expectedCreatePathStatement: expectedCreatePathStatements){
+                assertTrue("Repoinit text does not contain desired create path statement!",StringUtils.contains(repoinitTextCaptor.getValue(), expectedCreatePathStatement));
+            }
+            
         }
         // verify nothing else has been deployed
         assertEquals(2, targetFolder.list().length);
         // verify changed id
         ArgumentCaptor<Artifact> captor = ArgumentCaptor.forClass(Artifact.class);
-        Mockito.verify(featuresManager).addArtifact(Mockito.isNull(), captor.capture(), Mockito.isNull());
+        verify(featuresManager).addArtifact(Mockito.isNull(), captor.capture(), Mockito.isNull());
         final Artifact result = captor.getValue();
         assertNotNull(result);
         assertEquals(ArtifactId.fromMvnId("io.wcm:io.wcm.handler.media:1.11.6-cp2fm-converted"), result.getId());
@@ -116,10 +181,310 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr
 
 
     @Test
+    public void testJsonI18nWithXMLFolderDescriptors() throws Exception {
+        setUpArchive("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar", "mysite.core-1.0.0-SNAPSHOT-i18n-xml-folderdescriptor.jar");
+        DefaultEntryHandlersManager handlersManager = new DefaultEntryHandlersManager(Collections.emptyMap(), false, SlingInitialContentPolicy.KEEP, new BundleSlingInitialContentExtractor(), ConverterConstants.SYSTEM_USER_REL_PATH_DEFAULT);
+        converter.setEntryHandlersManager(handlersManager);
+        Map<String, String> namespaceRegistry = new HashMap<>();
+
+        namespaceRegistry.put("cq","http://www.day.com/jcr/cq/1.0");
+        namespaceRegistry.put("granite", "http://www.adobe.com/jcr/granite/1.0");
+
+
+        when(featuresManager.getNamespaceUriByPrefix()).thenReturn(namespaceRegistry);
+
+        File targetFolder = tmpFolder.newFolder();
+        when(converter.getArtifactsDeployer()).thenReturn(new SimpleFolderArtifactsDeployer(targetFolder));
+        when(converter.isSubContentPackageIncluded("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar-APPLICATION")).thenReturn(true);
+
+        VaultPackageAssembler assembler = Mockito.mock(VaultPackageAssembler.class);
+        Properties props = new Properties();
+        props.setProperty(PackageProperties.NAME_GROUP, "com.mysite");
+        props.setProperty(PackageProperties.NAME_NAME, "mysite.core");
+        props.setProperty(PackageProperties.NAME_VERSION, "1.0.0-SNAPSHOT");
+        when(assembler.getPackageProperties()).thenReturn(props);
+        converter.setMainPackageAssembler(assembler);
+        converter.setAclManager(new DefaultAclManager());
+        BundleSlingInitialContentExtractor extractor = new BundleSlingInitialContentExtractor();
+
+        handler.setBundleSlingInitialContentExtractor(extractor);
+        handler.setSlingInitialContentPolicy(SlingInitialContentPolicy.EXTRACT_AND_REMOVE);
+        handler.handle("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar", archive, entry, converter);
+
+        converter.deployPackages();
+
+        Type typeOfHashMap = new TypeToken<Map<String, String>>() { }.getType();
+        // verify generated package
+        try (VaultPackage vaultPackage = new PackageManagerImpl().open(new File(targetFolder, "mysite.core-apps-1.0.0-SNAPSHOT-cp2fm-converted.zip"));
+             Archive archive = vaultPackage.getArchive()) {
+            archive.open(true);
+            PackageId targetId = PackageId.fromString("com.mysite:mysite.core-apps:1.0.0-SNAPSHOT-cp2fm-converted");
+            assertEquals(targetId, vaultPackage.getId());
+
+            Entry jsonFileEntry = archive.getEntry("/jcr_root/apps/myinitialcontentest/test/i18n/en.json");
+            assertNotNull(jsonFileEntry);
+            
+            //compare JSON
+            Reader actualJsonFileContents = new InputStreamReader(archive.getInputSource(jsonFileEntry).getByteStream(), UTF_8);
+            Reader expectedJsonFileContents = new InputStreamReader(getClass().getResourceAsStream("i18n-jsonfile-xml-descriptor-test/en.json"), UTF_8);
+            
+            Gson GSON =  new GsonBuilder().create();
+
+            Map<String,String> actualJson = GSON.fromJson(actualJsonFileContents, typeOfHashMap);
+            Map<String,String> expectedJson = GSON.fromJson(expectedJsonFileContents, typeOfHashMap);
+            
+            assertEquals(expectedJson, actualJson);
+            //compare XML
+            
+            Entry jsonFileDescriptorEntry = archive.getEntry("/jcr_root/apps/myinitialcontentest/test/i18n/en.json.dir/.content.xml");
+            assertNotNull(jsonFileDescriptorEntry);
+
+            String expectedXML = IOUtils.toString(getClass().getResourceAsStream("i18n-jsonfile-xml-descriptor-test/en.json.dir/.content.xml"), UTF_8);
+            String actualXML = IOUtils.toString(archive.getInputSource(jsonFileDescriptorEntry).getByteStream(), UTF_8);
+    
+            assertThat(actualXML).and(expectedXML).areSimilar();
+
+        }
+    }
+
+
+    @Test
+    public void testSlingInitialContentWithNodeTypeAndNoDefinedParent() throws Exception {
+        setUpArchive("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar", "mysite.core-1.0.0-SNAPSHOT-slinginitialcontent-test.jar");
+        
+        DefaultEntryHandlersManager handlersManager = new DefaultEntryHandlersManager(Collections.emptyMap(), false, SlingInitialContentPolicy.KEEP, new BundleSlingInitialContentExtractor(), ConverterConstants.SYSTEM_USER_REL_PATH_DEFAULT);
+        converter.setEntryHandlersManager(handlersManager);
+        Map<String, String> namespaceRegistry = new HashMap<>();
+
+        namespaceRegistry.put("cq","http://www.day.com/jcr/cq/1.0");
+        namespaceRegistry.put("granite", "http://www.adobe.com/jcr/granite/1.0");
+    
+
+        when(featuresManager.getNamespaceUriByPrefix()).thenReturn(namespaceRegistry);
+
+        File targetFolder = tmpFolder.newFolder();
+        when(converter.getArtifactsDeployer()).thenReturn(new SimpleFolderArtifactsDeployer(targetFolder));
+        when(converter.isSubContentPackageIncluded("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar-APPLICATION")).thenReturn(true);
+
+        VaultPackageAssembler assembler = Mockito.mock(VaultPackageAssembler.class);
+        Properties props = new Properties();
+        props.setProperty(PackageProperties.NAME_GROUP, "com.mysite");
+        props.setProperty(PackageProperties.NAME_NAME, "mysite.core");
+        props.setProperty(PackageProperties.NAME_VERSION, "1.0.0-SNAPSHOT");
+        when(assembler.getPackageProperties()).thenReturn(props);
+        converter.setMainPackageAssembler(assembler);
+        
+        DefaultAclManager aclManager = new DefaultAclManager();
+        converter.setAclManager(aclManager);
+        BundleSlingInitialContentExtractor extractor = new BundleSlingInitialContentExtractor();
+
+        handler.setBundleSlingInitialContentExtractor(extractor);
+        handler.setSlingInitialContentPolicy(SlingInitialContentPolicy.EXTRACT_AND_REMOVE);
+        handler.handle("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar", archive, entry, converter);
+
+        converter.deployPackages();
+        extractor.addRepoInitExtension(converter.getAssemblers(), featuresManager);
+
+        try (VaultPackage vaultPackage = new PackageManagerImpl().open(new File(targetFolder, "mysite.core-apps-1.0.0-SNAPSHOT-cp2fm-converted.zip"));
+             Archive archive = vaultPackage.getArchive()) {
+            archive.open(true);
+            PackageId targetId = PackageId.fromString("com.mysite:mysite.core-apps:1.0.0-SNAPSHOT-cp2fm-converted");
+            assertEquals(targetId, vaultPackage.getId());
+            
+            InputStream inputStream = archive.getInputSource(archive.getEntry("jcr_root/apps/myinitialcontentest/test/parent-with-definition/.content.xml")).getByteStream();
+            assertNotNull(inputStream);
+
+            //this needs to be defined by repoinit, not the package itself
+            Archive.Entry parentWithoutDefinitionEntry = archive.getEntry("jcr_root/apps/myinitialcontentest/test/parent-with-definition/parent-without-definition/.content.xml");
+            assertNull(parentWithoutDefinitionEntry);
+
+            InputStream someUnstructuredNode = archive.getInputSource(archive.getEntry("jcr_root/apps/myinitialcontentest/test/parent-with-definition/parent-without-definition/someUnstructuredNode/.content.xml")).getByteStream();
+            assertNotNull(someUnstructuredNode);
+            
+            String repoinitText = 
+                    "create path (sling:Folder) /content/test/myinitialcontentest2\n" +
+                    "create path (sling:Folder) /apps/myinitialcontentest/test/parent-with-definition(my:parent)/parent-without-definition\n";
+            
+            verify(featuresManager, times(1)).addOrAppendRepoInitExtension(eq("content-package"), eq(repoinitText), Mockito.isNull());
+            
+        }
+
+    }
+    
+
+    @Test
+    public void testSlingInitialContentWithNodeTypeAndPageJson() throws Exception {
+        setUpArchive("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar", "mysite.core-1.0.0-SNAPSHOT-pagejson.jar");
+        DefaultEntryHandlersManager handlersManager = new DefaultEntryHandlersManager(Collections.emptyMap(), false, SlingInitialContentPolicy.KEEP, new BundleSlingInitialContentExtractor(), ConverterConstants.SYSTEM_USER_REL_PATH_DEFAULT);
+        converter.setEntryHandlersManager(handlersManager);
+        Map<String, String> namespaceRegistry = new HashMap<>();
+
+        namespaceRegistry.put("cq","http://www.day.com/jcr/cq/1.0");
+        namespaceRegistry.put("granite", "http://www.adobe.com/jcr/granite/1.0");
+
+        
+        when(featuresManager.getNamespaceUriByPrefix()).thenReturn(namespaceRegistry);
+
+        File targetFolder = tmpFolder.newFolder();
+        when(converter.getArtifactsDeployer()).thenReturn(new SimpleFolderArtifactsDeployer(targetFolder));
+        when(converter.isSubContentPackageIncluded("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar-APPLICATION")).thenReturn(true);
+
+        VaultPackageAssembler assembler = Mockito.mock(VaultPackageAssembler.class);
+        Properties props = new Properties();
+        props.setProperty(PackageProperties.NAME_GROUP, "com.mysite");
+        props.setProperty(PackageProperties.NAME_NAME, "mysite.core");
+        props.setProperty(PackageProperties.NAME_VERSION, "1.0.0-SNAPSHOT");
+        when(assembler.getPackageProperties()).thenReturn(props);
+        converter.setMainPackageAssembler(assembler);
+        converter.setAclManager(new DefaultAclManager());
+        BundleSlingInitialContentExtractor extractor = new BundleSlingInitialContentExtractor();
+
+        handler.setBundleSlingInitialContentExtractor(extractor);        
+        handler.setSlingInitialContentPolicy(SlingInitialContentPolicy.EXTRACT_AND_REMOVE);
+        handler.handle("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar", archive, entry, converter);
+
+        converter.deployPackages();
+
+        // verify generated package
+        try (VaultPackage vaultPackage = new PackageManagerImpl().open(new File(targetFolder, "mysite.core-apps-1.0.0-SNAPSHOT-cp2fm-converted.zip"));
+             Archive archive = vaultPackage.getArchive()) {
+            archive.open(true);
+            PackageId targetId = PackageId.fromString("com.mysite:mysite.core-apps:1.0.0-SNAPSHOT-cp2fm-converted");
+            assertEquals(targetId, vaultPackage.getId());
+
+            assertPageStructureFromEntry(archive, "jcr_root/apps/mysite/components/global", "homepage");
+            assertPageStructureFromEntry(archive, "jcr_root/apps/mysite/components/global", "page", "body.html");
+            assertPageStructureFromEntry(archive, "jcr_root/apps/mysite/components/global", "xfpage","body.html");
+            
+            InputStream inputStream = archive.getInputSource(archive.getEntry("jcr_root/apps/mysite/components/global/homepage/.content.xml")).getByteStream();
+            
+            InputSource expectedXML = new InputSource(getClass().getResource("mysite-nodetype-and-page-json-xml-result.xml").openStream());
+            InputSource actualXML = new InputSource(inputStream);
+
+            
+            assertThat(expectedXML).and(actualXML).areSimilar();
+         
+        }
+
+    }
+
+    @Test
+    public void testSlingInitialContentWithSpecialCharacters() throws Exception {
+        setUpArchive("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar", "mysite.core-1.0.0-SNAPSHOT-specialchars-json-inputstream.jar");
+        DefaultEntryHandlersManager handlersManager = new DefaultEntryHandlersManager(Collections.emptyMap(), false, SlingInitialContentPolicy.KEEP, new BundleSlingInitialContentExtractor(), ConverterConstants.SYSTEM_USER_REL_PATH_DEFAULT);
+        converter.setEntryHandlersManager(handlersManager);
+        Map<String, String> namespaceRegistry = new HashMap<>();
+
+        namespaceRegistry.put("cq","http://www.day.com/jcr/cq/1.0");
+        namespaceRegistry.put("granite", "http://www.adobe.com/jcr/granite/1.0");
+
+
+        when(featuresManager.getNamespaceUriByPrefix()).thenReturn(namespaceRegistry);
+
+        File targetFolder = tmpFolder.newFolder();
+        when(converter.getArtifactsDeployer()).thenReturn(new SimpleFolderArtifactsDeployer(targetFolder));
+        when(converter.isSubContentPackageIncluded("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar-APPLICATION")).thenReturn(true);
+
+        VaultPackageAssembler assembler = Mockito.mock(VaultPackageAssembler.class);
+        Properties props = new Properties();
+        props.setProperty(PackageProperties.NAME_GROUP, "com.mysite");
+        props.setProperty(PackageProperties.NAME_NAME, "mysite.core");
+        props.setProperty(PackageProperties.NAME_VERSION, "1.0.0-SNAPSHOT");
+        when(assembler.getPackageProperties()).thenReturn(props);
+        converter.setMainPackageAssembler(assembler);
+        converter.setAclManager(new DefaultAclManager());
+        BundleSlingInitialContentExtractor extractor = new BundleSlingInitialContentExtractor();
+
+        handler.setBundleSlingInitialContentExtractor(extractor);        
+        handler.setSlingInitialContentPolicy(SlingInitialContentPolicy.EXTRACT_AND_REMOVE);
+        handler.handle("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar", archive, entry, converter);
+
+        converter.deployPackages();
+
+       
+                
+                
+        // verify generated package
+        try (VaultPackage vaultPackage = new PackageManagerImpl().open(new File(targetFolder, "mysite.core-apps-1.0.0-SNAPSHOT-cp2fm-converted.zip"));
+             Archive archive = vaultPackage.getArchive()) {
+            archive.open(true);
+            PackageId targetId = PackageId.fromString("com.mysite:mysite.core-apps:1.0.0-SNAPSHOT-cp2fm-converted");
+            assertEquals(targetId, vaultPackage.getId());
+
+            //json containing a property: "fancyCharacters": "<&\"'>"
+            assertPageStructureFromEntry(archive,"jcr_root/apps/mysite/components/global", "homepage" );
+            assertResultingEntry(archive, "homepage");
+            
+            //xml with custom name element: nodeName and containing a textfile
+            assertPageStructureFromEntry(archive,"jcr_root/apps/mysite/components/global", "nodeName", "testfile.txt" );
+            assertResultingEntry(archive, "nodeName");
+            
+            //xml with custom "name" element "xyz"
+            assertPageStructureFromEntry(archive, "jcr_root/apps/mysite/components/global", "xyz");
+            assertResultingEntry(archive, "xyz");
+            
+            //xml with custom name element: 11&quot;&quot;&gt;&lt;mumbojumbo
+            assertResultingEntry(archive, "11mumbojumbo");
+            assertPageStructureFromEntry(archive,"jcr_root/apps/mysite/components/global", "11mumbojumbo" );
+        }
+
+    }
+
+
+
+    @Test
+    public void testSlingInitialContentWithNumberedEntries() throws Exception {
+        setUpArchive("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar", "io.wcm.handler.link-1.7.02.jar");
+        DefaultEntryHandlersManager handlersManager = new DefaultEntryHandlersManager(Collections.emptyMap(), false, SlingInitialContentPolicy.KEEP, new BundleSlingInitialContentExtractor(), ConverterConstants.SYSTEM_USER_REL_PATH_DEFAULT);
+        converter.setEntryHandlersManager(handlersManager);
+        Map<String, String> namespaceRegistry = new HashMap<>();
+
+        namespaceRegistry.put("cq","http://www.day.com/jcr/cq/1.0");
+        namespaceRegistry.put("granite", "http://www.adobe.com/jcr/granite/1.0");
+
+
+        when(featuresManager.getNamespaceUriByPrefix()).thenReturn(namespaceRegistry);
+
+        File targetFolder = tmpFolder.newFolder();
+        when(converter.getArtifactsDeployer()).thenReturn(new SimpleFolderArtifactsDeployer(targetFolder));
+        when(converter.isSubContentPackageIncluded("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar-APPLICATION")).thenReturn(true);
+
+        VaultPackageAssembler assembler = Mockito.mock(VaultPackageAssembler.class);
+        Properties props = new Properties();
+        props.setProperty(PackageProperties.NAME_GROUP, "com.mysite");
+        props.setProperty(PackageProperties.NAME_NAME, "mysite.core");
+        props.setProperty(PackageProperties.NAME_VERSION, "1.0.0-SNAPSHOT");
+        when(assembler.getPackageProperties()).thenReturn(props);
+        converter.setMainPackageAssembler(assembler);
+        converter.setAclManager(new DefaultAclManager());
+        BundleSlingInitialContentExtractor extractor = new BundleSlingInitialContentExtractor();
+
+        handler.setBundleSlingInitialContentExtractor(extractor);
+        handler.setSlingInitialContentPolicy(SlingInitialContentPolicy.EXTRACT_AND_REMOVE);
+        handler.handle("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar", archive, entry, converter);
+
+        converter.deployPackages();
+        
+        // verify generated package
+        try (VaultPackage vaultPackage = new PackageManagerImpl().open(new File(targetFolder, "io.wcm.handler.link-apps-1.7.0-cp2fm-converted.zip"));
+             Archive archive = vaultPackage.getArchive()) {
+            archive.open(true);
+
+            InputStream xmlFile = archive.getInputSource(archive.getEntry("jcr_root/apps/wcm-io/handler/link/components/global/include/redirectStatus/.content.xml")).getByteStream();
+            String expectedXML = IOUtils.toString(getClass().getResourceAsStream("bundle-entry-xmls/include-redirectStatus.xml"), UTF_8);
+            String actualXML = IOUtils.toString(xmlFile, UTF_8);
+
+            assertThat(expectedXML).and(actualXML).areSimilar();
+        }
+
+    }
+
+    @Test
     public void testSlingInitialContentWithNodeType() throws Exception {
         setUpArchive("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar", "mysite-slinginitialcontent-nodetype-def.jar");
         DefaultEntryHandlersManager handlersManager = new DefaultEntryHandlersManager();
         converter.setEntryHandlersManager(handlersManager);
+        converter.setAclManager(new DefaultAclManager());
         Map<String, String> namespaceRegistry = Collections.singletonMap("granite", "http://www.adobe.com/jcr/granite/1.0");
         when(featuresManager.getNamespaceUriByPrefix()).thenReturn(namespaceRegistry);
 
@@ -134,7 +499,9 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr
         props.setProperty(PackageProperties.NAME_VERSION, "1.0.0-SNAPSHOT");
         when(assembler.getPackageProperties()).thenReturn(props);
         converter.setMainPackageAssembler(assembler);
+        BundleSlingInitialContentExtractor extractor = new BundleSlingInitialContentExtractor();
 
+        handler.setBundleSlingInitialContentExtractor(extractor);
         handler.setSlingInitialContentPolicy(SlingInitialContentPolicy.EXTRACT_AND_REMOVE);
         handler.handle("/jcr_root/apps/mysite/install/mysite-slinginitialcontent-nodetype-def.jar", archive, entry, converter);
 
@@ -154,14 +521,14 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr
             archive.open(true);
             PackageId targetId = PackageId.fromString("com.mysite:mysite.core-apps:1.0.0-SNAPSHOT-cp2fm-converted");
             assertEquals(targetId, vaultPackage.getId());
-            Entry entry = archive.getEntry("jcr_root/apps/myinitialcontentest/my-first-node.xml");
+            Entry entry = archive.getEntry("jcr_root/apps/myinitialcontentest/my-first-node/.content.xml");
             assertNotNull("Archive does not contain expected item", entry);
         }
         // verify nothing else has been deployed
         assertEquals(2, targetFolder.list().length);
         // verify changed id
         ArgumentCaptor<Artifact> captor = ArgumentCaptor.forClass(Artifact.class);
-        Mockito.verify(featuresManager).addArtifact(Mockito.isNull(), captor.capture(), Mockito.isNull());
+        verify(featuresManager).addArtifact(Mockito.isNull(), captor.capture(), Mockito.isNull());
         final Artifact result = captor.getValue();
         assertNotNull(result);
         assertEquals(ArtifactId.fromMvnId("com.mysite:mysite.core:1.0.0-SNAPSHOT-cp2fm-converted"), result.getId());
@@ -182,11 +549,15 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr
         props.setProperty(PackageProperties.NAME_VERSION, "1.11.6");
         when(assembler.getPackageProperties()).thenReturn(props);
         converter.setMainPackageAssembler(assembler);
+        converter.setAclManager(new DefaultAclManager());
+        BundleSlingInitialContentExtractor extractor = new BundleSlingInitialContentExtractor();
+
+        handler.setBundleSlingInitialContentExtractor(extractor);
         handler.setSlingInitialContentPolicy(SlingInitialContentPolicy.EXTRACT_AND_REMOVE);
         handler.handle("/jcr_root/apps/gav/install/composum-nodes-config-2.5.3.jar", archive, entry, converter);
         // modified bundle
         ArgumentCaptor<Artifact> captor = ArgumentCaptor.forClass(Artifact.class);
-        Mockito.verify(featuresManager).addArtifact(Mockito.isNull(), captor.capture(), Mockito.isNull());
+        verify(featuresManager).addArtifact(Mockito.isNull(), captor.capture(), Mockito.isNull());
         final Artifact result = captor.getValue();
         assertNotNull(result);
         assertEquals(ArtifactId.fromMvnId("com.composum.nodes:composum-nodes-config:2.5.3-cp2fm-converted"), result.getId());
@@ -194,7 +565,7 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr
         assertEquals("2.5.3", result.getMetadata().get(Constants.BUNDLE_VERSION));
 
         // need to use ArgumentCaptur to properly compare string arrays
-        Mockito.verify(featuresManager).addConfiguration(ArgumentMatchers.isNull(), cfgCaptor.capture(), ArgumentMatchers.eq("/jcr_root/libs/composum/nodes/install/org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment-composum_core_v2.config"), dictionaryCaptor.capture());
+        verify(featuresManager).addConfiguration(ArgumentMatchers.isNull(), cfgCaptor.capture(), eq("/jcr_root/libs/composum/nodes/install/org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment-composum_core_v2.config"), dictionaryCaptor.capture());
         assertEquals("composum_core", dictionaryCaptor.getValue().get("whitelist.name"));
         assertEquals("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment~composum_core_v2", cfgCaptor.getValue().getPid());
         assertArrayEquals(new String[] {
@@ -216,20 +587,24 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr
         props.setProperty(PackageProperties.NAME_VERSION, "1.11.6");
         when(assembler.getPackageProperties()).thenReturn(props);
         converter.setMainPackageAssembler(assembler);
+        converter.setAclManager(new DefaultAclManager());
+        BundleSlingInitialContentExtractor extractor = new BundleSlingInitialContentExtractor();
+
+        handler.setBundleSlingInitialContentExtractor(extractor);
         handler.setSlingInitialContentPolicy(SlingInitialContentPolicy.EXTRACT_AND_KEEP);
         handler.handle("/jcr_root/apps/gav/install/composum-nodes-config-2.5.3.jar", archive, entry, converter);
         // original bundle
         ArgumentCaptor<Artifact> captor = ArgumentCaptor.forClass(Artifact.class);
-        Mockito.verify(featuresManager).addArtifact(Mockito.isNull(), captor.capture(), Mockito.isNull());
+        verify(featuresManager).addArtifact(Mockito.isNull(), captor.capture(), Mockito.isNull());
         final Artifact result = captor.getValue();
         assertNotNull(result);
         assertEquals(ArtifactId.fromMvnId("com.composum.nodes:composum-nodes-config:2.5.3"), result.getId());
         assertEquals("com.composum.nodes.config", result.getMetadata().get(Constants.BUNDLE_SYMBOLICNAME));
         assertEquals("2.5.3", result.getMetadata().get(Constants.BUNDLE_VERSION));
         // need to use ArgumentCaptur to properly compare string arrays
-        Mockito.verify(featuresManager).addConfiguration(ArgumentMatchers.isNull(),
-             cfgCaptor.capture(),
-             ArgumentMatchers.eq("/jcr_root/libs/composum/nodes/install/org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment-composum_core_v2.config"), dictionaryCaptor.capture());
+        verify(featuresManager).addConfiguration(ArgumentMatchers.isNull(),
+                cfgCaptor.capture(),
+                eq("/jcr_root/libs/composum/nodes/install/org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment-composum_core_v2.config"), dictionaryCaptor.capture());
         assertEquals("composum_core", dictionaryCaptor.getValue().get("whitelist.name"));
         assertEquals("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment~composum_core_v2", cfgCaptor.getValue().getPid());
         assertArrayEquals(new String[] {
@@ -237,4 +612,44 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr
                 "com.composum.nodes.pckgmgr",
                 "com.composum.nodes.pckginstall" }, (String[])dictionaryCaptor.getValue().get("whitelist.bundles"));
     }
+
+    private void assertResultingEntry(Archive archive, String entryKey) throws IOException, SAXException {
+        InputStream xmlFile = archive.getInputSource(archive.getEntry("jcr_root/apps/mysite/components/global/" + entryKey  +"/.content.xml")).getByteStream();
+        InputStream expectedXmlFileStream = getClass().getResourceAsStream("bundle-entry-xmls/" + entryKey + ".xml");
+        String expectedXML = IOUtils.toString(expectedXmlFileStream, UTF_8);
+        String actualXML = IOUtils.toString(xmlFile, UTF_8);
+
+  
+        Source control = Input.fromString(expectedXML).build();
+        Source test = Input.fromString(actualXML).build();
+        
+        DifferenceEngine diff = new DOMDifferenceEngine();
+        diff.addDifferenceListener((comparison, outcome) -> {
+            
+            if(comparison.getType() == ComparisonType.CHILD_NODELIST_LENGTH){
+                //this comparison is buggy so we can't use it.
+                return;
+            }
+            
+            String actualString = comparison.getTestDetails().getValue().toString();
+            String expectedString = comparison.getControlDetails().getValue().toString();
+            if(!actualString.trim().equals(expectedString.trim())){
+                Assert.fail("difference found in XML: " + actualString + " vs " + expectedString);
+            }
+        });
+        diff.compare(control, test);
+    }
+
+    private void assertPageStructureFromEntry(Archive archive, String basePath, String pageName, String... files) throws IOException {
+        Entry contentXml = archive.getEntry( basePath + "/" + pageName + "/.content.xml");
+        assertNotNull(contentXml);
+        Entry pageXml = archive.getEntry( basePath + "/" + pageName + ".xml");
+        assertNull(pageXml);
+
+        for(String file: files){
+            Entry expectedEntry = archive.getEntry( basePath + "/" + pageName + "/" + file);
+            assertNotNull(expectedEntry);
+        }
+    }
+
 }
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandlerTest.java
index 6c254de..fbfc6b5 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandlerTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandlerTest.java
@@ -43,6 +43,7 @@ import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter
 import org.apache.sling.feature.cpconverter.artifacts.LocalMavenRepositoryArtifactsDeployer;
 import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager;
 import org.apache.sling.feature.cpconverter.features.FeaturesManager;
+import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.BundleSlingInitialContentExtractor;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/GavDeclarationsInBundleTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/GavDeclarationsInBundleTest.java
index ebd9afc..1ad5975 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/GavDeclarationsInBundleTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/GavDeclarationsInBundleTest.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals;
 import java.io.File;
 import java.util.jar.JarFile;
 
+import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.junit.Test;
 
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/vltpkg/JcrNamespaceRegistryTest.java b/src/test/java/org/apache/sling/feature/cpconverter/vltpkg/JcrNamespaceRegistryTest.java
index 751f1eb..ff04530 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/vltpkg/JcrNamespaceRegistryTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/vltpkg/JcrNamespaceRegistryTest.java
@@ -30,7 +30,7 @@ import static javax.jcr.NamespaceRegistry.PREFIX_XML;
 public class JcrNamespaceRegistryTest{
     
     
-    @Test(expected = NamespaceException.class)
+    @Test
     public void test_unregister() throws RepositoryException, ParseException, IOException {
         JcrNamespaceRegistry jcrNamespaceRegistry = new JcrNamespaceRegistry();
 
@@ -39,21 +39,23 @@ public class JcrNamespaceRegistryTest{
         Assert.assertEquals(8, jcrNamespaceRegistry.getURIs().length);
 
         //throws javax.jcr.NamespaceException: No URI for prefix 'xml' declared.
-        jcrNamespaceRegistry.getURI(PREFIX_XML);
+        Assert.assertThrows(NamespaceException.class, () -> jcrNamespaceRegistry.getURI(PREFIX_XML));
+       
     }
-    
-    @Test(expected = NamespaceException.class)
+
+    @Test
     public void test_get_prefix_exception() throws RepositoryException, ParseException, IOException {
 
         JcrNamespaceRegistry jcrNamespaceRegistry = new JcrNamespaceRegistry();
-        jcrNamespaceRegistry.getPrefix("_Asd");
+        Assert.assertNull(jcrNamespaceRegistry.getPrefix("_Asd"));
     }
 
-    @Test(expected = NamespaceException.class)
+    @Test
     public void test_get_uri_exception() throws RepositoryException, ParseException, IOException {
 
         JcrNamespaceRegistry jcrNamespaceRegistry = new JcrNamespaceRegistry();
-        jcrNamespaceRegistry.getURI("_Asd");
+        Assert.assertThrows(NamespaceException.class, () -> jcrNamespaceRegistry.getURI("_Asd"));
+        
     }
     
 }
\ No newline at end of file
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/build_playground.ui.content-1.0-SNAPSHOT.zip b/src/test/resources/org/apache/sling/feature/cpconverter/build_playground.ui.content-1.0-SNAPSHOT.zip
deleted file mode 100644
index 9c07c08..0000000
Binary files a/src/test/resources/org/apache/sling/feature/cpconverter/build_playground.ui.content-1.0-SNAPSHOT.zip and /dev/null differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/%3c%22&%3e.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/%3c%22&%3e.xml
new file mode 100644
index 0000000..db7494e
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/%3c%22&%3e.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace"
+          xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0"
+          xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0"
+          xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0"
+          xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="cq:Component"
+          sling:resourceSuperType="mysite-aem/core/components/global/page" fancyCharacters="&lt;&amp;&quot;'>"></jcr:root>
\ No newline at end of file
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/11mumbojumbo.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/11mumbojumbo.xml
new file mode 100644
index 0000000..de1f197
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/11mumbojumbo.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="cq:Component" prop=" [...]
+    <someOtherNode jcr:primaryType="nt:unstructured" prop="{String}property value as string">
+    </someOtherNode>
+</jcr:root>
\ No newline at end of file
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/homepage.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/homepage.xml
new file mode 100644
index 0000000..7bdf717
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/homepage.xml
@@ -0,0 +1,3 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="cq:Component" sling: [...]
+</jcr:root>
\ No newline at end of file
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/include-redirectStatus.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/include-redirectStatus.xml
new file mode 100755
index 0000000..a441d1c
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/include-redirectStatus.xml
@@ -0,0 +1,17 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="nt:unstructured" mar [...]
+  <items jcr:primaryType="nt:unstructured">
+    <column jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container">
+      <items jcr:primaryType="nt:unstructured">
+        <redirectStatus jcr:primaryType="nt:unstructured" defaultValue="301" fieldLabel="io.wcm.handler.link.components.global.include.linkRefContainer.redirectStatus.redirectStatus.fieldLabel" name="./redirectStatus" sling:resourceType="granite/ui/components/coral/foundation/form/select" fieldDescription="io.wcm.handler.link.components.global.include.linkRefContainer.redirectStatus.redirectStatus.fieldDescription">
+          <items jcr:primaryType="nt:unstructured">
+            <_301 jcr:primaryType="nt:unstructured" text="io.wcm.handler.link.components.global.include.linkRefContainer.redirectStatus.redirectStatus.301" value="301">
+            </_301>
+            <_302 jcr:primaryType="nt:unstructured" text="io.wcm.handler.link.components.global.include.linkRefContainer.redirectStatus.redirectStatus.302" value="302">
+            </_302>
+          </items>
+        </redirectStatus>
+      </items>
+    </column>
+  </items>
+</jcr:root>
\ No newline at end of file
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/nodeName.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/nodeName.xml
new file mode 100644
index 0000000..c11e395
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/nodeName.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="nt:unstructured" pro [...]
+    <testfile.txt jcr:primaryType="nt:file">
+        <jcr:content jcr:primaryType="nt:resource" jcr:mimeType="application/test" jcr:data="{Binary}" jcr:lastModified="{Long}233992800000">
+        </jcr:content>
+    </testfile.txt>
+</jcr:root>
\ No newline at end of file
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/someprop.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/someprop.xml
new file mode 100644
index 0000000..c442151
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/someprop.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace"
+          xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0"
+          xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0"
+          xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0"
+          xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="cq:Component" prop="{String}aa"></jcr:root>
\ No newline at end of file
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/xyz.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/xyz.xml
new file mode 100644
index 0000000..78dfbe0
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/xyz.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="nt:unstructured" jcr [...]
+    <someOtherNode jcr:primaryType="nt:unstructured" prop="{String}property value as string">
+    </someOtherNode>
+</jcr:root>
\ No newline at end of file
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/i18n-jsonfile-xml-descriptor-test/en.json b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/i18n-jsonfile-xml-descriptor-test/en.json
new file mode 100755
index 0000000..ee1613a
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/i18n-jsonfile-xml-descriptor-test/en.json
@@ -0,0 +1,10 @@
+{
+  "com.mysite.components.templating.navigation.title": "Navigation",
+  "com.mysite.components.templating.navigation.overview": "Overview",
+  "com.mysite.components.templating.navigation.open": "Open navigation",
+  "com.mysite.components.templating.navigation.close": "Close navigation",
+  "com.mysite.components.templating.navigation.go-back": "Go back",
+  "com.mysite.components.search": "Search",
+  "com.mysite.components.search.results": "results found",
+  "com.mysite.components.search.showmore": "Show more"
+}
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/i18n-jsonfile-xml-descriptor-test/en.json.dir/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/i18n-jsonfile-xml-descriptor-test/en.json.dir/.content.xml
new file mode 100755
index 0000000..79aadfd
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/i18n-jsonfile-xml-descriptor-test/en.json.dir/.content.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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:mix="http://www.jcp.org/jcr/mix/1.0"
+          xmlns:my="http://namespace.com/my"
+          jcr:language="{String}en"
+          jcr:mixinTypes="[mix:language]"
+          jcr:primaryType="nt:file">
+</jcr:root>
\ No newline at end of file
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/io.wcm.handler.link-1.7.02.jar b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/io.wcm.handler.link-1.7.02.jar
new file mode 100755
index 0000000..372a19b
Binary files /dev/null and b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/io.wcm.handler.link-1.7.02.jar differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite-nodetype-and-page-json-xml-result.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite-nodetype-and-page-json-xml-result.xml
new file mode 100644
index 0000000..6c4ca35
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite-nodetype-and-page-json-xml-result.xml
@@ -0,0 +1,44 @@
+<?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="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="cq:Component" sling: [...]
+    <cq:dialog jcr:primaryType="nt:unstructured">
+        <content jcr:primaryType="nt:unstructured">
+            <items jcr:primaryType="nt:unstructured">
+                <tabs jcr:primaryType="nt:unstructured">
+                    <items jcr:primaryType="nt:unstructured">
+                        <basic jcr:primaryType="nt:unstructured">
+                            <items jcr:primaryType="nt:unstructured">
+                                <column jcr:primaryType="nt:unstructured">
+                                    <items jcr:primaryType="nt:unstructured">
+                                        <errorhandling jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/fieldset" jcr:title="Error Handling">
+                                            <items jcr:primaryType="nt:unstructured">
+                                                <errorpages jcr:primaryType="nt:unstructured" fieldLabel="Error Pages" name="./errorPages" sling:resourceType="wcm-io/wcm/ui/granite/components/form/pathfield" fieldDescription="Error pages for this content tree" rootPath="/content">
+                                                    <granite:data jcr:primaryType="nt:unstructured" cq-msm-lockable="errorPages"/>
+                                                </errorpages>
+                                            </items>
+                                        </errorhandling>
+                                    </items>
+                                </column>
+                            </items>
+                        </basic>
+                    </items>
+                </tabs>
+            </items>
+        </content>
+    </cq:dialog>
+</jcr:root>
\ No newline at end of file
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite.core-1.0.0-SNAPSHOT-i18n-xml-folderdescriptor.jar b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite.core-1.0.0-SNAPSHOT-i18n-xml-folderdescriptor.jar
new file mode 100644
index 0000000..06be679
Binary files /dev/null and b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite.core-1.0.0-SNAPSHOT-i18n-xml-folderdescriptor.jar differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite.core-1.0.0-SNAPSHOT-pagejson.jar b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite.core-1.0.0-SNAPSHOT-pagejson.jar
new file mode 100644
index 0000000..4773cbe
Binary files /dev/null and b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite.core-1.0.0-SNAPSHOT-pagejson.jar differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite.core-1.0.0-SNAPSHOT-slinginitialcontent-test.jar b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite.core-1.0.0-SNAPSHOT-slinginitialcontent-test.jar
new file mode 100644
index 0000000..07428f7
Binary files /dev/null and b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite.core-1.0.0-SNAPSHOT-slinginitialcontent-test.jar differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite.core-1.0.0-SNAPSHOT-specialchars-json-inputstream.jar b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite.core-1.0.0-SNAPSHOT-specialchars-json-inputstream.jar
new file mode 100644
index 0000000..74ba2ef
Binary files /dev/null and b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/mysite.core-1.0.0-SNAPSHOT-specialchars-json-inputstream.jar differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/mysite.all-1.0.0-SNAPSHOT2.zip b/src/test/resources/org/apache/sling/feature/cpconverter/mysite.all-1.0.0-SNAPSHOT2.zip
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/seed.json b/src/test/resources/org/apache/sling/feature/cpconverter/seed.json
new file mode 100644
index 0000000..be10416
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/seed.json
@@ -0,0 +1,9 @@
+{
+  "id":"foo:bar:slingosgifeature:1.0.0",
+  "repoinit:TEXT|true":[
+    "register namespace (granite) http://www.adobe.com/jcr/granite/1.0",
+    "register namespace (wcmio) http://wcm.io/ns",
+    "register namespace (crx) http://www.day.com/crx/1.0",
+    "register namespace (cq) http://www.day.com/jcr/cq/1.0"
+  ]
+}
\ No newline at end of file