Merge pull request #73 from apache/SLING-10286

SLING-10286 : Converter ignores disabled status of system user
diff --git a/README.md b/README.md
index 66adecf..71907f2 100644
--- a/README.md
+++ b/README.md
@@ -6,11 +6,11 @@
 
 ## Introduction
 
-`content-package`s are zipped archives containing OSGi bundles, OSGi configurations and resources (and nested `content-package`s as well), aside metadata, that can be used to install content into a _JCR_ repository using the [Apache Jackrabbit FileVault](http://jackrabbit.apache.org/filevault/) packaging runtime.
+`content-package`s are zipped archives containing OSGi bundles, OSGi configurations, JCR nodes/properties and nested `content-package`s as well, aside [metadata](http://jackrabbit.apache.org/filevault/metadata.html), that can be used to install content into a _JCR_ repository using the [Apache Jackrabbit FileVault](http://jackrabbit.apache.org/filevault/) packaging runtime.
 
 OTOH, [Apache Sling Feature](https://github.com/apache/sling-org-apache-sling-feature) allows users to describe an entire OSGi-based application based on reusable components and includes everything related to this application, including bundles, configuration, framework properties, capabilities, requirements and custom artifacts.
 
-The _Apache Sling Content Package to Feature Model converter_ (referred as _cp2fm_) is a tool able to extract OSGI bundles, OSGi configurations, resources and iteratively scan nested `content-package`s from an input `content-package` and create one (or more) _Apache Sling Feature_ model files and deploy the extracted OSGi bundles in a directory which structure is compliant the _Apache Maven_ repository conventions.
+The _Apache Sling Content Package to Feature Model converter_ (referred as _cp2fm_) is a tool able to extract OSGI bundles, OSGi configurations and iteratively scan nested `content-package`s from an input `content-package` and create one (or more) _Apache Sling Feature_ model files and deploy the extracted OSGi bundles in a directory which structure is compliant the _Apache Maven_ repository conventions. The remaining JCR nodes/properties are kept in content packages which are either referenced in the [Sling Feature Model Content Deployment Extension](https://github.com/apache/sling-org-apache-sling-feature-extension-content) section or completely separate from the generated feature model.
 
 ## Understanding the Input
 
@@ -147,9 +147,14 @@
     39481                     22 files
 ```
 
+### Package Types
+
+`content-package`s have one of [four package types](http://jackrabbit.apache.org/filevault/packagetypes.html).
+By default type `content` is never referenced inside the feature model (to work with Oak Composite Node Stores), while `application` and `mixed` type's are referenced in the generated feature models. `container` packages are dissolved and all sub packages are flattened (i.e. extracted as individual packages).
+
 ## Mapping and the Output
 
-All metadata are mainly collected inside one or more, depending by declared run modes in the installation and configuration paths, _Feature_ model files:
+All metadata are mainly collected inside one or more _Feature_ model files, depending on declared run modes in the installation and configuration paths:
 
 ```json
 $ cat asd.retail.all.json 
@@ -201,7 +206,9 @@
 }
 ```
 
-### Binaries
+### OSGi Bundles and Content Packages
+
+All nodes and properties which are not OSGi configurations or bundles are kept in (stripped) content packages. Usually those content packages are referenced inside the Feature Model (evaluated by [Content Handler](https://github.com/apache/sling-org-apache-sling-feature-extension-content/blob/master/src/main/java/org/apache/sling/feature/extension/content/ContentHandler.java)). For packages of type `content` the user can define what to do with those packages (reference in feature model, store in dedicated folder or completely drop). This is useful in more complex deployment scenarios where parts of the repository are swapped via Oak Composite Node Store.
 
 All detected bundles are collected in an _Apache Maven repository_ compliant directory, all other resources are collected in a new `content-package`, usually classified as `cp2fm-converted-feature`, created while scanning the packages, which contains _content only_.
 
@@ -232,7 +239,7 @@
 12 directories, 8 files
 ```
 
-### Supported configurations
+### OSGi Configurations
 
 All OSGi configuration formats are supported:
 
@@ -316,7 +323,7 @@
 
 The [org.apache.sling.feature.cpconverter.artifacts.ArtifactsDeployer](./src/main/java/org/apache/sling/feature/cpconverter/artifacts/ArtifactsDeployer.java) service is designed to let the conversion tool be integrated in external services, i.e. _Apache Maven_.
 
-The [default implementation](./src/main/java/org/apache/sling/feature/cpconverter/artifacts/DefaultArtifactsDeployer.java) just copies bundles in the target output directory, according to the _Apache Maven_ repository layout.
+The [default implementation](./src/main/java/org/apache/sling/feature/cpconverter/artifacts/LocalMavenRepositoryArtifactsDeployer.java) just copies bundles in the target output directory, according to the _Apache Maven_ repository layout.
 
 Bundles are collected in an _Apache Maven repository_ compliant directory, all other resources are collected in a new `content-package` created while scanning the packages:
 
@@ -421,7 +428,7 @@
 
 ## The CLI Tool
 
-The tool is distributed with a commodity package containing all is needed in order to launch the `ContentPackage2FeatureModelConverter` form the shell:
+The tool is distributed with a commodity package containing all is needed in order to launch the `ContentPackage2FeatureModelConverter` from the shell:
 
 ```bash
 $ unzip -l org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT.zip 
@@ -484,13 +491,52 @@
 
 ```bash
 $ ./bin/cp2fm -h
-Usage: cp2fm [-hmqsvX] -a=<artifactsOutputDirectory> [-b=<bundlesStartOrder>]
-             [-i=<artifactIdOverride>] -o=<featureModelsOutputDirectory>
-             [-p=<fmPrefix>] [-D=<String=String>]...
-             [-f=<filteringPatterns>]... [-r=<apiRegions>]...
-             content-packages...
+Usage: cp2fm [-hmqsvXZ] [--disable-installer-policy]
+             [--enforce-servicemapping-by-principal] [--remove-install-hooks]
+             [--content-type-package-policy=<contentTypePackagePolicy>]
+             [--enforce-principal-based-supported-path=<enforcePrincipalBasedSup
+             portedPath>] [--seed-feature=<seedFeature>]
+             [--system-user-rel-path=<systemUserRelPath>]
+             -a=<artifactsOutputDirectory> [-b=<bundlesStartOrder>]
+             [-e=<exportsToRegion>] [-i=<artifactIdOverride>]
+             -o=<featureModelsOutputDirectory> [-p=<fmPrefix>]
+             -u=<unreferencedArtifactsOutputDirectory>
+             [--entry-handler-config=<entryHandlerConfigs>]...
+             [-D=<String=String>]... [-f=<filteringPatterns>]...
+             [-r=<apiRegions>]... content-packages...
 Apache Sling Content Package to Sling Feature converter
       content-packages...   The content-package input file(s).
+      --content-type-package-policy=<contentTypePackagePolicy>
+                            Determines what to do with converted packages of type
+                              'content'. Valid values: REFERENCE, DROP,
+                              PUT_IN_DEDICATED_FOLDER.
+                              Default: DROP
+      --disable-installer-policy
+                            Disables enforcing that OSGi configurations are only
+                              allowed below a folder called 'config' and OSGi
+                              bundles are only allowed below a folder called
+                              'install'. Instead both are detected below either
+                              'install' or 'config'.
+      --enforce-principal-based-supported-path=<enforcePrincipalBasedSupportedPath>
+                            Converts service user access control entries to
+                              principal-based setup using the given supported path.
+      --enforce-servicemapping-by-principal
+                            Converts service user mappings with the form 'service:
+                              sub=userID' to 'service:sub=[principalname]'. Note,
+                              this may result in group membership no longer being
+                              resolved upon service login.
+      --entry-handler-config=<entryHandlerConfigs>
+                            Config for entry handlers that support it (classname:
+                              <config-string>
+      --remove-install-hooks
+                            Removes both internal and external hooks from processed
+                              packages
+      --seed-feature=<seedFeature>
+                            A url pointing to a feature that can be assumed to be
+                              around when the conversion result will be used
+      --system-user-rel-path=<systemUserRelPath>
+                            Relative path for system user as configured with Apache
+                              Jackrabbit Oak
   -a, --artifacts-output-directory=<artifactsOutputDirectory>
                             The output directory where the artifacts will be
                               deployed.
@@ -498,6 +544,9 @@
                             The order to start detected bundles.
   -D, --define=<String=String>
                             Define a system property
+  -e, --exports-to-region=<exportsToRegion>
+                            Packages exported by bundles in the content packages are
+                              exported in the named region
   -f, --filtering-patterns=<filteringPatterns>
                             Regex based pattern(s) to reject content-package archive
                               entries.
@@ -518,15 +567,16 @@
                             The API Regions assigned to the generated features
   -s, --strict-validation   Flag to mark the content-package input file being strict
                               validated.
+  -u, --unreferenced-artifacts-output-directory=<unreferencedArtifactsOutputDirectory
+        >
+                            The output directory where unreferenced artifacts will
+                              be deployed.
   -v, --version             Display version information.
   -X, --verbose             Produce execution debug output.
   -Z, --fail-on-mixed-packages
                             Fail the conversion if the resulting attached
-                              content-package is MIXED type.
-  --enforce-principal-based-supported-path=<path>
-                            Converts service user access control entries to principal-based 
-                              setup using the given supported path.
-Copyright(c) 2019 The Apache Software Foundation.
+                              content-package is MIXED type
+Copyright(c) 2019-2021 The Apache Software Foundation.
 ```
 
 to see all the available options; a sample execution could look like:
@@ -551,7 +601,7 @@
 
 ```bash
 > ./bin/cp2fm @argfile
-````
+```
 
 ## Failures and Restrictions
 
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 84bc7ac..49cb21e 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
@@ -23,6 +23,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -84,15 +85,28 @@
 
     private ArtifactsDeployer artifactsDeployer;
 
+    private ArtifactsDeployer unreferencedArtifactsDeployer;
+
     private VaultPackageAssembler mainPackageAssembler;
 
     private final RecollectorVaultPackageScanner recollectorVaultPackageScanner;
 
-    private PackagesEventsEmitter emitter;
+    private List<PackagesEventsEmitter> emitters = new ArrayList<>();
 
     private boolean failOnMixedPackages = false;
 
-    private boolean dropContent = false;
+    public enum PackagePolicy {
+        /** References the content package in the feature model and deploys via the {@link ContentPackage2FeatureModelConverter#artifactsDeployer} */
+        REFERENCE, 
+        /** Drops the content package completely (i.e. neither reference it in the feature model nor deploy anywhere)
+          * @deprecated
+          */
+        DROP,
+        /** Deploys the content package via the {@link ContentPackage2FeatureModelConverter#unreferencedArtifactsDeployer} */
+        PUT_IN_DEDICATED_FOLDER;
+    }
+
+    private PackagePolicy contentTypePackagePolicy = PackagePolicy.REFERENCE;
 
     private boolean removeInstallHooks = false;
 
@@ -123,6 +137,9 @@
 
     public @NotNull ContentPackage2FeatureModelConverter setFeaturesManager(@Nullable FeaturesManager featuresManager) {
         this.featuresManager = featuresManager;
+        if ( featuresManager instanceof PackagesEventsEmitter ) {
+            this.emitters.add((PackagesEventsEmitter)featuresManager);
+        }
         return this;
     }
 
@@ -140,6 +157,11 @@
         return this;
     }
 
+    public @NotNull ContentPackage2FeatureModelConverter setUnreferencedArtifactsDeployer(@Nullable ArtifactsDeployer unreferencedArtifactsDeployer) {
+        this.unreferencedArtifactsDeployer = unreferencedArtifactsDeployer;
+        return this;
+    }
+
     public @Nullable AclManager getAclManager() {
         return aclManager;
     }
@@ -154,12 +176,12 @@
     }
 
     public @NotNull ContentPackage2FeatureModelConverter setEmitter(@Nullable PackagesEventsEmitter emitter) {
-        this.emitter = emitter;
+        this.emitters.add(emitter);
         return this;
     }
     
-    public @NotNull ContentPackage2FeatureModelConverter setDropContent(boolean dropContent) {
-        this.dropContent = dropContent;
+    public @NotNull ContentPackage2FeatureModelConverter setContentTypePackagePolicy(PackagePolicy contentTypePackagePolicy) {
+        this.contentTypePackagePolicy = contentTypePackagePolicy;
         return this;
     }
 
@@ -229,11 +251,11 @@
     }
 
     protected void secondPass(@NotNull Collection<VaultPackage> orderedContentPackages) throws Exception {
-        emitter.start();
+        emitters.stream().forEach(e -> e.start());
 
         for (VaultPackage vaultPackage : orderedContentPackages) {
             try {
-                emitter.startPackage(vaultPackage);
+                emitters.stream().forEach(e -> e.startPackage(vaultPackage));
                 mainPackageAssembler = VaultPackageAssembler.create(this.getTempDirectory(), vaultPackage, removeInstallHooks);
                 assemblers.add(mainPackageAssembler);
 
@@ -264,7 +286,7 @@
                 logger.info("Conversion complete!");
 
                 featuresManager.serialize();
-                emitter.endPackage();
+                emitters.stream().forEach(e -> e.endPackage());
             } finally {
                 aclManager.reset();
                 assemblers.clear();
@@ -279,7 +301,7 @@
 
         mutableContentsIds.clear();
 
-        emitter.end();
+        emitters.stream().forEach(e -> e.end());
     }
 
     private void orderDependencies(@NotNull Map<PackageId, VaultPackage> idFileMap,
@@ -312,7 +334,7 @@
             return;
         }
 
-        emitter.startSubPackage(path, vaultPackage);
+        emitters.stream().forEach(e -> e.startSubPackage(path, vaultPackage));
 
         PackageId originalPackageId = vaultPackage.getId();
         ArtifactId mvnPackageId = toArtifactId(vaultPackage);
@@ -347,7 +369,7 @@
         // restore the previous assembler
         mainPackageAssembler = handler;
 
-        emitter.endSubPackage();
+        emitters.stream().forEach(e -> e.endSubPackage());
     }
 
     private void processContentPackageArchive(@NotNull File contentPackageArchive,
@@ -366,15 +388,33 @@
                                     + " is of MIXED type");
             }
 
-            // don't deploy & add content-packages of type content to featuremodel if dropContent is set
-            if (PackageType.CONTENT != packageType || !dropContent) {
-                // deploy the new content-package to the local mvn bundles dir and attach it to the feature
+            
+            // special handling for converted packages of type content
+            if (PackageType.CONTENT == packageType) {
+                switch (contentTypePackagePolicy) {
+                    case DROP:
+                        mutableContentsIds.put(originalPackageId, getDependencies(vaultPackage));
+                        logger.info("Dropping package of PackageType.CONTENT {} (content-package id: {})",
+                                    mvnPackageId.getArtifactId(), originalPackageId);
+                        break;
+                    case PUT_IN_DEDICATED_FOLDER:
+                        mutableContentsIds.put(originalPackageId, getDependencies(vaultPackage));
+                        // deploy the new content-package to the unreferenced artifacts deployer
+                        if (unreferencedArtifactsDeployer == null) {
+                            throw new IllegalStateException("ContentTypePackagePolicy PUT_IN_DEDICATED_FOLDER requires a valid deployer ");
+                        }
+                        unreferencedArtifactsDeployer.deploy(new FileArtifactWriter(contentPackageArchive), mvnPackageId);
+                        logger.info("Put converted package of PackageType.CONTENT {} (content-package id: {}) in {} (not referenced in feature model)",
+                                    mvnPackageId.getArtifactId(), originalPackageId, unreferencedArtifactsDeployer.getBaseDirectory());
+                        break;
+                    case REFERENCE:
+                        artifactsDeployer.deploy(new FileArtifactWriter(contentPackageArchive), mvnPackageId);
+                        featuresManager.addArtifact(runMode, mvnPackageId);
+                }
+            } else {
+                // deploy the new content-package to the local mvn bundles dir
                 artifactsDeployer.deploy(new FileArtifactWriter(contentPackageArchive), mvnPackageId);
                 featuresManager.addArtifact(runMode, mvnPackageId);
-            } else {
-                mutableContentsIds.put(originalPackageId, getDependencies(vaultPackage));
-                logger.info("Dropping package of PackageType.CONTENT {} (content-package id: {})",
-                            mvnPackageId.getArtifactId(), originalPackageId);
             }
         }
     }
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/artifacts/ArtifactsDeployer.java b/src/main/java/org/apache/sling/feature/cpconverter/artifacts/ArtifactsDeployer.java
index c94dfe9..b498945 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/artifacts/ArtifactsDeployer.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/artifacts/ArtifactsDeployer.java
@@ -24,7 +24,7 @@
 
 public interface ArtifactsDeployer {
 
-    @NotNull File getBundlesDirectory();
+    @NotNull File getBaseDirectory();
 
     void deploy(@NotNull ArtifactWriter artifactWriter, @NotNull ArtifactId id) throws IOException;
 
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/artifacts/DefaultArtifactsDeployer.java b/src/main/java/org/apache/sling/feature/cpconverter/artifacts/LocalMavenRepositoryArtifactsDeployer.java
similarity index 82%
rename from src/main/java/org/apache/sling/feature/cpconverter/artifacts/DefaultArtifactsDeployer.java
rename to src/main/java/org/apache/sling/feature/cpconverter/artifacts/LocalMavenRepositoryArtifactsDeployer.java
index eaec649..3ddd1d9 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/artifacts/DefaultArtifactsDeployer.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/artifacts/LocalMavenRepositoryArtifactsDeployer.java
@@ -28,13 +28,16 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public final class DefaultArtifactsDeployer implements ArtifactsDeployer {
+/**
+ * Stores the deployed artifacts in a <a href="https://cwiki.apache.org/confluence/display/MAVENOLD/Repository+Layout+-+Final">local Maven repository layout</a>.
+ */
+public final class LocalMavenRepositoryArtifactsDeployer implements ArtifactsDeployer {
 
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
     private final File artifactsDirectory;
 
-    public DefaultArtifactsDeployer(@NotNull File outputDirectory) {
+    public LocalMavenRepositoryArtifactsDeployer(@NotNull File outputDirectory) {
         artifactsDirectory = outputDirectory;
         if (!artifactsDirectory.exists()) {
             artifactsDirectory.mkdirs();
@@ -42,7 +45,7 @@
     }
 
     @Override
-    public @NotNull File getBundlesDirectory() {
+    public @NotNull File getBaseDirectory() {
         return artifactsDirectory;
     }
 
@@ -65,18 +68,8 @@
 
         // deploy the main artifact
 
-        StringBuilder nameBuilder = new StringBuilder()
-                                    .append(id.getArtifactId())
-                                    .append('-')
-                                    .append(id.getVersion());
 
-        if (id.getClassifier() != null) {
-            nameBuilder.append('-').append(id.getClassifier());
-        }
-
-        nameBuilder.append('.').append(id.getType());
-
-        File targetFile = new File(targetDir, nameBuilder.toString());
+        File targetFile = new File(targetDir, id.toMvnName());
 
         logger.info("Writing data to {}...", targetFile);
 
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/artifacts/SimpleFolderArtifactsDeployer.java b/src/main/java/org/apache/sling/feature/cpconverter/artifacts/SimpleFolderArtifactsDeployer.java
new file mode 100644
index 0000000..4afb626
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/artifacts/SimpleFolderArtifactsDeployer.java
@@ -0,0 +1,61 @@
+/*
+ * 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.artifacts;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.sling.feature.ArtifactId;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Stores the deployed artifacts as files in a flat folder structure.
+ * All deployed artifact ids must be unique among all group ids to prevent overwriting files.
+ */
+public class SimpleFolderArtifactsDeployer implements ArtifactsDeployer {
+
+    private final File artifactsDirectory;
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    public SimpleFolderArtifactsDeployer(@NotNull File outputDirectory) {
+        artifactsDirectory = outputDirectory;
+        if (!artifactsDirectory.exists()) {
+            artifactsDirectory.mkdirs();
+        }
+    }
+
+    @Override
+    public @NotNull File getBaseDirectory() {
+        return artifactsDirectory;
+    }
+
+    @Override
+    public void deploy(@NotNull ArtifactWriter artifactWriter, @NotNull ArtifactId id) throws IOException {
+        File targetFile = new File(artifactsDirectory, id.toMvnName());
+        logger.info("Writing data to {}...", targetFile);
+
+        try (FileOutputStream targetStream = new FileOutputStream(targetFile)) {
+            artifactWriter.write(targetStream);
+        }
+
+        logger.info("Data successfully written to {}.", targetFile);
+    }
+
+}
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 6fe8ca0..2503076 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
@@ -31,7 +31,8 @@
 import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
 import org.apache.sling.feature.cpconverter.accesscontrol.AclManager;
 import org.apache.sling.feature.cpconverter.accesscontrol.DefaultAclManager;
-import org.apache.sling.feature.cpconverter.artifacts.DefaultArtifactsDeployer;
+import org.apache.sling.feature.cpconverter.artifacts.LocalMavenRepositoryArtifactsDeployer;
+import org.apache.sling.feature.cpconverter.artifacts.SimpleFolderArtifactsDeployer;
 import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager;
 import org.apache.sling.feature.cpconverter.filtering.RegexBasedResourceFilter;
 import org.apache.sling.feature.cpconverter.handlers.DefaultEntryHandlersManager;
@@ -43,13 +44,14 @@
 
 import picocli.CommandLine;
 import picocli.CommandLine.Command;
+import picocli.CommandLine.Help.Visibility;
 import picocli.CommandLine.Option;
 import picocli.CommandLine.Parameters;
 
 @Command(
     name = "cp2fm",
     description = "Apache Sling Content Package to Sling Feature converter",
-    footer = "Copyright(c) 2019 The Apache Software Foundation."
+    footer = "Copyright(c) 2019-2021 The Apache Software Foundation."
 )
 public final class ContentPackage2FeatureModelConverterLauncher implements Runnable {
 
@@ -125,6 +127,12 @@
     @Option(names = { "--disable-installer-policy" }, description = "Disables enforcing that OSGi configurations are only allowed below a folder called 'config' and OSGi bundles are only allowed below a folder called 'install'. Instead both are detected below either 'install' or 'config'.", required = false)
     private boolean disableInstallerPolicy = false;
 
+    @Option(names = { "--content-type-package-policy" }, description = "Determines what to do with converted packages of type 'content'. Valid values: ${COMPLETION-CANDIDATES}.", required = false, showDefaultValue = Visibility.ALWAYS)
+    private ContentPackage2FeatureModelConverter.PackagePolicy contentTypePackagePolicy = ContentPackage2FeatureModelConverter.PackagePolicy.DROP;
+
+    @Option(names = { "-u", "--unreferenced-artifacts-output-directory" }, description = "The output directory where unreferenced artifacts will be deployed.", required = true)
+    private File unreferencedArtifactsOutputDirectory;
+
     @Override
     public void run() {
         if (quiet) {
@@ -195,13 +203,18 @@
 
                 ContentPackage2FeatureModelConverter converter = new ContentPackage2FeatureModelConverter(strictValidation)
                                                                 .setFeaturesManager(featuresManager)
-                                                                .setBundlesDeployer(new DefaultArtifactsDeployer(artifactsOutputDirectory))
+                                                                .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(artifactsOutputDirectory))
                                                                 .setEntryHandlersManager(new DefaultEntryHandlersManager(entryHandlerConfigsMap, !disableInstallerPolicy))
                                                                 .setAclManager(aclManager)
                                                                 .setEmitter(DefaultPackagesEventsEmitter.open(featureModelsOutputDirectory))
                                                                 .setFailOnMixedPackages(failOnMixedPackages)
-                                                                .setDropContent(true);
-
+                                                                .setContentTypePackagePolicy(contentTypePackagePolicy);
+                                                                
+                if (unreferencedArtifactsOutputDirectory != null) {
+                    converter.setUnreferencedArtifactsDeployer(new SimpleFolderArtifactsDeployer(unreferencedArtifactsOutputDirectory));
+                } else if (contentTypePackagePolicy == ContentPackage2FeatureModelConverter.PackagePolicy.PUT_IN_DEDICATED_FOLDER) {
+                    throw new IllegalStateException("Argument '--content-type-package-policy PUT_IN_DEDICATED_FOLDER' requires argument '--unreferenced-artifacts-output-directory' as well!");
+                }
                 try {
                     if (filteringPatterns != null && filteringPatterns.length > 0) {
                         RegexBasedResourceFilter filter = new RegexBasedResourceFilter();
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/features/DefaultFeaturesManager.java b/src/main/java/org/apache/sling/feature/cpconverter/features/DefaultFeaturesManager.java
index d0df8c5..b16425c 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/features/DefaultFeaturesManager.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/features/DefaultFeaturesManager.java
@@ -25,6 +25,7 @@
 import java.util.*;
 import java.util.Map.Entry;
 
+import org.apache.jackrabbit.vault.packaging.VaultPackage;
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Artifacts;
@@ -38,6 +39,7 @@
 import org.apache.sling.feature.cpconverter.accesscontrol.Mapping;
 import org.apache.sling.feature.cpconverter.interpolator.SimpleVariablesInterpolator;
 import org.apache.sling.feature.cpconverter.interpolator.VariablesInterpolator;
+import org.apache.sling.feature.cpconverter.vltpkg.PackagesEventsEmitter;
 import org.apache.sling.feature.extension.apiregions.api.ApiExport;
 import org.apache.sling.feature.extension.apiregions.api.ApiRegion;
 import org.apache.sling.feature.extension.apiregions.api.ApiRegions;
@@ -49,7 +51,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class DefaultFeaturesManager implements FeaturesManager {
+public class DefaultFeaturesManager implements FeaturesManager, PackagesEventsEmitter {
 
     public enum ConfigurationHandling {
         ORDERED,
@@ -63,6 +65,9 @@
 
     private static final String JSON_FILE_EXTENSION = ".json";
 
+    private static final String BUNDLE_ORIGINS = "content-package-origins";
+    private static final String CONFIGURATION_ORIGINS = Configuration.CONFIGURATOR_PREFIX.concat(BUNDLE_ORIGINS);
+
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
     private final Map<String, Feature> runModes = new HashMap<>();
@@ -93,6 +98,8 @@
 
     private final Map<String, String> pidToPathMapping = new HashMap<>();
 
+    private final Stack<String> packageIds = new Stack<>();
+    
     DefaultFeaturesManager() {
         this(new File(""));
     }
@@ -183,9 +190,15 @@
 
             artifacts = extension.getArtifacts();
         } else {
-            int startOrderForBundle = startOrder != null ? startOrder.intValue() : bundlesStartOrder;
+            // set start order
+            final int startOrderForBundle = startOrder != null ? startOrder.intValue() : bundlesStartOrder;
             artifact.setStartOrder(startOrderForBundle);
-            artifacts = targetFeature.getBundles();
+            // set origins
+            if ( !this.packageIds.isEmpty() ) {
+                artifact.getMetadata().put(BUNDLE_ORIGINS, String.join("|", this.packageIds));
+            }
+
+            artifacts = targetFeature.getBundles();        
         }
 
         artifacts.add(artifact);
@@ -288,7 +301,6 @@
         @NotNull String path,
         @NotNull Dictionary<String, Object> configurationProperties) {
         String factoryPid = null;
-        String id;
         int n = pid.indexOf('~');
         if (n > 0) {
             factoryPid = pid.substring(0, n);
@@ -339,6 +351,17 @@
         configuration.getProperties().remove(Constants.SERVICE_PID);
         configuration.getProperties().remove("service.bundleLocation");
         configuration.getProperties().remove("service.factoryPid");
+
+        // set origins
+        if ( !this.packageIds.isEmpty() ) {
+            final List<String> origins = new ArrayList<>();
+            final Object val = configuration.getProperties().get(CONFIGURATION_ORIGINS);
+            if ( val != null ) {
+                origins.addAll(Arrays.asList(val.toString().split(",")));
+            }    
+            origins.add(String.join("|", this.packageIds));
+            configuration.getProperties().put(CONFIGURATION_ORIGINS, String.join(",", origins));
+        }
     }
 
     private void addAPIRegions(@NotNull Feature feature, @Nullable List<String> exportedPackages) throws IOException {
@@ -462,4 +485,49 @@
             }
         }
     }
+
+    /**
+     * Package converter starts
+     */
+    public void start() {
+        // nothing to do
+    }
+
+    /** 
+     * Package converter ends
+     */
+    public void end() {
+        // nothing to do
+    }
+
+    /**
+     * Package starts
+     * @param vaultPackage the package
+     */
+    public void startPackage(final @NotNull VaultPackage vaultPackage) {
+        packageIds.push(vaultPackage.getId().toString());
+    }
+
+    /**
+     * Package ends
+     */
+    public void endPackage() {
+        packageIds.pop();
+    }
+
+    /**
+     * Sub package starts
+     * @param path The path
+     * @param vaultPackage the package
+     */
+    public void startSubPackage(final @NotNull String path, final @NotNull VaultPackage vaultPackage) {
+        packageIds.push(vaultPackage.getId().toString());
+    }
+
+    /**
+     * Sub package ends
+     */
+    public void endSubPackage() {
+        packageIds.pop();
+    }
 }
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 5cc4f2d..2fb623e 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
@@ -55,8 +55,10 @@
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.ExtensionType;
 import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.PackagePolicy;
 import org.apache.sling.feature.cpconverter.accesscontrol.DefaultAclManager;
-import org.apache.sling.feature.cpconverter.artifacts.DefaultArtifactsDeployer;
+import org.apache.sling.feature.cpconverter.artifacts.LocalMavenRepositoryArtifactsDeployer;
+import org.apache.sling.feature.cpconverter.artifacts.SimpleFolderArtifactsDeployer;
 import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager;
 import org.apache.sling.feature.cpconverter.filtering.RegexBasedResourceFilter;
 import org.apache.sling.feature.cpconverter.handlers.DefaultEntryHandlersManager;
@@ -141,7 +143,7 @@
         try {
 
             converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, null, null, null, new DefaultAclManager()))
-                    .setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
+                    .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
                     .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
                     .convert(packageFile);
 
@@ -212,7 +214,7 @@
     }
 
     @Test
-    public void convertContentPackageDropContent() throws Exception {
+    public void convertContentPackageDropContentTypePackagePolicy() throws Exception {
         URL packageUrl = getClass().getResource("test-content-package.zip");
         File packageFile = FileUtils.toFile(packageUrl);
 
@@ -221,9 +223,9 @@
         try {
 
             converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, null, null, null, new DefaultAclManager()))
-                    .setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
+                    .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
                     .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
-                    .setDropContent(true)
+                    .setContentTypePackagePolicy(PackagePolicy.DROP)
                     .convert(packageFile);
 
             verifyFeatureFile(outputDirectory,
@@ -282,6 +284,89 @@
     }
 
     @Test
+    public void convertContentPackagePutInDedicatedFolderContentTypePackagePolicy() throws Exception {
+        URL packageUrl = getClass().getResource("test-content-package.zip");
+        File packageFile = FileUtils.toFile(packageUrl);
+
+        File outputDirectory = new File(System.getProperty("java.io.tmpdir"), getClass().getName() + '_' + System.currentTimeMillis());
+        File outputDirectoryUnreferencedArtifacts = new File(System.getProperty("java.io.tmpdir"), getClass().getName() + "_unreferenced_" + System.currentTimeMillis());
+
+        try {
+
+            converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, null, null, null, new DefaultAclManager()))
+                    .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
+                    .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
+                    .setContentTypePackagePolicy(PackagePolicy.PUT_IN_DEDICATED_FOLDER)
+                    .setUnreferencedArtifactsDeployer(new SimpleFolderArtifactsDeployer(outputDirectoryUnreferencedArtifacts))
+                    .convert(packageFile);
+
+            verifyFeatureFile(outputDirectory,
+                            "asd.retail.all.json",
+                            "asd.sample:asd.retail.all:slingosgifeature:0.0.1",
+                            Arrays.asList("org.apache.felix:org.apache.felix.framework:6.0.1"),
+                            Arrays.asList("org.apache.sling.commons.log.LogManager.factory.config~asd-retail"),
+                            Arrays.asList("asd.sample:asd.retail.apps:zip:cp2fm-converted:0.0.1",
+                                    "asd:Asd.Retail.config:zip:cp2fm-converted:0.0.1"));
+            verifyFeatureFile(outputDirectory,
+                            "asd.retail.all-author.json",
+                            "asd.sample:asd.retail.all:slingosgifeature:author:0.0.1",
+                            Arrays.asList("org.apache.sling:org.apache.sling.api:2.20.0"),
+                            Collections.emptyList(),
+                            Collections.emptyList());
+            verifyFeatureFile(outputDirectory,
+                            "asd.retail.all-publish.json",
+                            "asd.sample:asd.retail.all:slingosgifeature:publish:0.0.1",
+                            Arrays.asList("org.apache.sling:org.apache.sling.models.api:1.3.8"),
+                            Arrays.asList("org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~asd-retail"),
+                            Collections.emptyList());
+
+            // verify the runmode.mapper integrity
+            File runmodeMapperFile = new File(outputDirectory, "runmode.mapping");
+            assertTrue(runmodeMapperFile.exists());
+            assertTrue(runmodeMapperFile.isFile());
+            Properties runModes = new Properties();
+            try (FileInputStream input = new FileInputStream(runmodeMapperFile)) {
+                runModes.load(input);
+            }
+            assertFalse(runModes.isEmpty());
+            assertTrue(runModes.containsKey("(default)"));
+            assertEquals("asd.retail.all.json", runModes.getProperty("(default)"));
+            assertEquals("asd.retail.all-author.json", runModes.getProperty("author"));
+            assertEquals("asd.retail.all-publish.json", runModes.getProperty("publish"));
+
+            verifyContentPackage(new File(outputDirectory, "asd/Asd.Retail.config/0.0.1/Asd.Retail.config-0.0.1-cp2fm-converted.zip"),
+                    "META-INF/vault/settings.xml",
+                    "META-INF/vault/properties.xml",
+                    "META-INF/vault/config.xml",
+                    "META-INF/vault/filter.xml",
+                    "jcr_root/apps/.content.xml");
+            verifyContentPackage(new File(outputDirectoryUnreferencedArtifacts, "Asd.Retail.ui.content-0.0.1-cp2fm-converted.zip"),
+                    "META-INF/vault/settings.xml",
+                    "META-INF/vault/properties.xml",
+                    "META-INF/vault/config.xml",
+                    "META-INF/vault/filter.xml",
+                    "META-INF/vault/filter-plugin-generated.xml",
+                    "jcr_root/content/asd/.content.xml",
+                    "jcr_root/content/asd/resources.xml");
+            verifyContentPackage(new File(outputDirectory, "asd/sample/asd.retail.apps/0.0.1/asd.retail.apps-0.0.1-cp2fm-converted.zip"),
+                    "META-INF/vault/settings.xml",
+                    "META-INF/vault/properties.xml",
+                    "META-INF/vault/config.xml",
+                    "META-INF/vault/filter.xml",
+                    "META-INF/vault/filter-plugin-generated.xml");
+            verifyContentPackage(new File(outputDirectoryUnreferencedArtifacts, "asd.retail.all-0.0.1-cp2fm-converted.zip"),
+                    "META-INF/vault/settings.xml",
+                    "META-INF/vault/properties.xml",
+                    "META-INF/vault/config.xml",
+                    "META-INF/vault/filter.xml");
+
+        } finally {
+            deleteDirTree(outputDirectory);
+            deleteDirTree(outputDirectoryUnreferencedArtifacts);
+        }
+    }
+
+    @Test
     public void convertContentPackageRemoveInstallHooks() throws Exception {
         URL packageUrl = getClass().getResource("test-with-install-hooks.zip");
         File packageFile = FileUtils.toFile(packageUrl);
@@ -290,7 +375,7 @@
 
         try {
             converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, null, null, null, new DefaultAclManager()))
-                    .setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
+                    .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
                     .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
                     .setRemoveInstallHooks(true)
                     .convert(packageFile);
@@ -318,7 +403,7 @@
 
         try {
             converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, null, null, null, new DefaultAclManager()))
-                    .setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
+                    .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
                     .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
                     .convert(packageFile);
 
@@ -347,7 +432,7 @@
             DefaultFeaturesManager fm = new DefaultFeaturesManager(true, 5, outDir, null, null, null, new DefaultAclManager());
             fm.setAPIRegions(Arrays.asList("global", "foo.bar"));
             converter.setFeaturesManager(fm)
-                     .setBundlesDeployer(new DefaultArtifactsDeployer(outDir))
+                     .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outDir))
                      .setEmitter(DefaultPackagesEventsEmitter.open(outDir))
                      .convert(cpFile);
 
@@ -510,7 +595,7 @@
         try {
 
             converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, null, null, null, new DefaultAclManager()))
-                    .setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
+                    .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
                     .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
                     .convert(packageFile);
         } finally {
@@ -532,7 +617,7 @@
         try {
 
             converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, null, null, null, new DefaultAclManager()))
-                    .setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
+                    .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
                     .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
                     .convert(packageFile);
             
@@ -563,7 +648,7 @@
             URL packageUrl = getClass().getResource("test-content-package.zip");
             File packageFile = FileUtils.toFile(packageUrl);
     
-            converter.setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
+            converter.setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
                      .setFeaturesManager(new DefaultFeaturesManager(DefaultFeaturesManager.ConfigurationHandling.STRICT, 5, outputDirectory, null, null, null, new DefaultAclManager()))
                      .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
                      .convert(packageFile);
@@ -584,7 +669,7 @@
             URL packageUrl = getClass().getResource("test-content-package.zip");
             File packageFile = FileUtils.toFile(packageUrl);
     
-            converter.setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
+            converter.setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
                      .setFeaturesManager(new DefaultFeaturesManager(false, 5, outputDirectory, null, null, null, new DefaultAclManager()))
                      .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
                      .convert(packageFile);
@@ -608,7 +693,7 @@
             URL packageUrl = getClass().getResource("test-content-package.zip");
             File packageFile = FileUtils.toFile(packageUrl);
     
-            converter.setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
+            converter.setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
                      .setFeaturesManager(new DefaultFeaturesManager(false, 5, outputDirectory, null, null, null, new DefaultAclManager()))
                      .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
                      .convert(packageFile);
@@ -633,7 +718,7 @@
         File outDir = Files.createTempDirectory(getClass().getSimpleName()).toFile();
 
         try {
-            converter.setBundlesDeployer(new DefaultArtifactsDeployer(outDir))
+            converter.setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outDir))
             .setFeaturesManager(new DefaultFeaturesManager(false, 5, outDir, null, null, null, new DefaultAclManager())
                     .setAPIRegions(Arrays.asList("a.b.c")))
             .setEmitter(DefaultPackagesEventsEmitter.open(outDir))
@@ -676,7 +761,7 @@
 
             String overrideId = "${project.groupId}:${project.artifactId}:slingosgifeature:asd.test.all-1.0.0:${project.version}";
             converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, overrideId, null, null, new DefaultAclManager()))
-                    .setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
+                    .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
                     .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
                     .convert(packageFile);
 
@@ -749,7 +834,7 @@
         try {
 
             converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, null, null, null, new DefaultAclManager()))
-                    .setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
+                    .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
                     .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
                     .convert(contentPackages[0]);
 
@@ -794,8 +879,8 @@
         File outputDirectory = new File(System.getProperty("java.io.tmpdir"), getClass().getName() + '_' + System.currentTimeMillis());
         try {
             converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, null, null, null, new DefaultAclManager()))
-                    .setDropContent(true)
-                    .setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
+                    .setContentTypePackagePolicy(PackagePolicy.DROP)
+                    .setBundlesDeployer(new LocalMavenRepositoryArtifactsDeployer(outputDirectory))
                     .setEmitter(DefaultPackagesEventsEmitter.open(outputDirectory))
                     .convert(contentPackages);
 
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/artifacts/DefaultBundlesDeployerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/artifacts/DefaultBundlesDeployerTest.java
index a7505be..0fba49c 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/artifacts/DefaultBundlesDeployerTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/artifacts/DefaultBundlesDeployerTest.java
@@ -39,7 +39,7 @@
     @Before
     public void setUp() {
         outputDirectory = new File(System.getProperty("java.io.tmpdir"), getClass().getName() + '_' + System.currentTimeMillis());
-        artifactDeployer = new DefaultArtifactsDeployer(outputDirectory);
+        artifactDeployer = new LocalMavenRepositoryArtifactsDeployer(outputDirectory);
     }
 
     @After
@@ -57,7 +57,7 @@
 
     @Test
     public void verifyBundlesDirectory() {
-        File bundlesDirectory = artifactDeployer.getBundlesDirectory();
+        File bundlesDirectory = artifactDeployer.getBaseDirectory();
         assertNotNull(bundlesDirectory);
         assertTrue(bundlesDirectory.exists());
         assertTrue(bundlesDirectory.isDirectory());
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 28d1e86..1174fe5 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
@@ -40,7 +40,7 @@
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
-import org.apache.sling.feature.cpconverter.artifacts.DefaultArtifactsDeployer;
+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.junit.Test;
@@ -115,7 +115,7 @@
         File testDirectory = new File(System.getProperty("java.io.tmpdir"), getClass().getName() + '_' + System.currentTimeMillis());
         try {
 
-            when(converter.getArtifactsDeployer()).thenReturn(new DefaultArtifactsDeployer(testDirectory));
+            when(converter.getArtifactsDeployer()).thenReturn(new LocalMavenRepositoryArtifactsDeployer(testDirectory));
             when(converter.getFeaturesManager()).thenReturn(featuresManager);
 
             bundleEntryHandler.handle(bundleLocation, archive, entry, converter);