You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by si...@apache.org on 2019/05/26 21:38:40 UTC

[sling-org-apache-sling-feature-cpconverter] 01/01: initial checkin of experimental branch where spaghetti code is replaced by DI with Google Guice

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

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

commit 6e197d2645cc74d9516ed904e87bd5e4e9360f79
Author: Simo Tripodi <st...@adobe.com>
AuthorDate: Sun May 26 23:38:26 2019 +0200

    initial checkin of experimental branch where spaghetti code is replaced
    by DI with Google Guice
---
 pom.xml                                            |  10 +-
 .../ContentPackage2FeatureModelConverter.java      |  75 +++------
 .../artifacts/DefaultArtifactsDeployer.java        |   6 +-
 ...ntentPackage2FeatureModelConverterLauncher.java |  56 +++----
 .../features/DefaultFeaturesManager.java           |  44 +++---
 .../filtering/RegexBasedResourceFilter.java        |  14 ++
 .../AbstractConfigurationEntryHandler.java         |  13 +-
 .../handlers/AbstractContentPackageHandler.java    |  33 ++--
 .../cpconverter/handlers/BundleEntryHandler.java   |  39 +++--
 .../handlers/ContentPackageEntryHandler.java       |  16 +-
 .../handlers/DefaultEntryHandlersManager.java      |  40 -----
 .../feature/cpconverter/handlers/EntryHandler.java |   3 +-
 .../handlers/RepPolicyEntryHandler.java            |  11 +-
 .../handlers/SystemUsersEntryHandler.java          |  20 ++-
 .../VersionResolverContentPackageEntryHandler.java |  19 ++-
 ...ContentPackage2FeatureModelConverterModule.java |  85 ++++++++++
 .../package-info.java}                             |   8 +-
 .../vltpkg/BaseVaultPackageScanner.java            |  22 ++-
 .../vltpkg/RecollectorVaultPackageScanner.java     |  23 +--
 .../cpconverter/vltpkg/VaultPackageAssembler.java  |   4 +-
 ...sling.feature.cpconverter.handlers.EntryHandler |   8 -
 .../ContentPackage2FeatureModelConverterTest.java  | 172 ++++-----------------
 .../sling/feature/cpconverter/IdOverrideTest.java  | 105 +++++++++++++
 .../handlers/BundleEntryHandlerTest.java           |  56 ++++---
 .../handlers/ConfigEntryHandlerTest.java           |  28 ++--
 .../handlers/ConfigurationEntryHandlerTest.java    |  61 ++++----
 .../handlers/ContentPackageEntryHandlerTest.java   |   4 +-
 .../JsonConfigurationEntryHandlerTest.java         |   5 +-
 .../handlers/RepPolicyEntryHandlerTest.java        |  43 ++----
 .../handlers/SystemUsersEntryHandlerTest.java      |  40 ++---
 ...ctContentPackage2FeatureModelConverterTest.java | 117 ++++++++++++++
 31 files changed, 656 insertions(+), 524 deletions(-)

diff --git a/pom.xml b/pom.xml
index fda28c6..1a88303 100644
--- a/pom.xml
+++ b/pom.xml
@@ -53,6 +53,15 @@
     </dependency>
 
     <!--
+     | Dependency injection
+    -->
+    <dependency>
+      <groupId>com.google.inject</groupId>
+      <artifactId>guice</artifactId>
+      <version>4.2.2</version>
+    </dependency>
+
+    <!--
      | CLI
     -->
     <dependency>
@@ -226,7 +235,6 @@
         <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>
           </excludes>
         </configuration>
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 7dff3a7..9ea2e95 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
@@ -20,7 +20,6 @@ import static java.util.Objects.requireNonNull;
 
 import java.io.File;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -40,11 +39,12 @@ import org.apache.sling.feature.cpconverter.artifacts.FileArtifactWriter;
 import org.apache.sling.feature.cpconverter.features.FeaturesManager;
 import org.apache.sling.feature.cpconverter.filtering.ResourceFilter;
 import org.apache.sling.feature.cpconverter.handlers.EntryHandler;
-import org.apache.sling.feature.cpconverter.handlers.EntryHandlersManager;
 import org.apache.sling.feature.cpconverter.vltpkg.BaseVaultPackageScanner;
 import org.apache.sling.feature.cpconverter.vltpkg.RecollectorVaultPackageScanner;
 import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
 
+import com.google.inject.Inject;
+
 public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanner {
 
     public static final String ZIP_TYPE = "zip";
@@ -53,67 +53,28 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
 
     private static final String DEFEAULT_VERSION = "0.0.0";
 
-    private final Map<PackageId, String> subContentPackages = new HashMap<>();
-
-    private EntryHandlersManager handlersManager;
+    @Inject
+    private Map<PackageId, String> subContentPackages;
 
+    @Inject
     private AclManager aclManager;
 
+    @Inject
     private FeaturesManager featuresManager;
 
+    @Inject
     private ResourceFilter resourceFilter;
 
+    @Inject
     private ArtifactsDeployer artifactsDeployer;
 
-    private VaultPackageAssembler mainPackageAssembler = null;
+    @Inject
+    private Set<EntryHandler> entryHandlers;
 
+    @Inject
     private RecollectorVaultPackageScanner recollectorVaultPackageScanner;
 
-    public ContentPackage2FeatureModelConverter() {
-        this(false);
-    }
-
-    public ContentPackage2FeatureModelConverter(boolean strictValidation) {
-        super(strictValidation);
-        this.recollectorVaultPackageScanner = new RecollectorVaultPackageScanner(this, this.packageManager, strictValidation, subContentPackages);
-    }
-
-    public ContentPackage2FeatureModelConverter setEntryHandlersManager(EntryHandlersManager handlersManager) {
-        this.handlersManager = handlersManager;
-        return this;
-    }
-
-    public FeaturesManager getFeaturesManager() {
-        return featuresManager;
-    }
-
-    public ContentPackage2FeatureModelConverter setFeaturesManager(FeaturesManager featuresManager) {
-        this.featuresManager = featuresManager;
-        return this;
-    }
-
-    public ContentPackage2FeatureModelConverter setResourceFilter(ResourceFilter resourceFilter) {
-        this.resourceFilter = resourceFilter;
-        return this;
-    }
-
-    public ArtifactsDeployer getArtifactsDeployer() {
-        return artifactsDeployer;
-    }
-
-    public ContentPackage2FeatureModelConverter setBundlesDeployer(ArtifactsDeployer bundlesDeployer) {
-        this.artifactsDeployer = bundlesDeployer;
-        return this;
-    }
-
-    public AclManager getAclManager() {
-        return aclManager;
-    }
-
-    public ContentPackage2FeatureModelConverter setAclManager(AclManager aclManager) {
-        this.aclManager = aclManager;
-        return this;
-    }
+    private VaultPackageAssembler mainPackageAssembler = null;
 
     public VaultPackageAssembler getMainPackageAssembler() {
         return mainPackageAssembler;
@@ -282,12 +243,20 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
                                                + " not allowed by user configuration, please check configured filtering patterns");
         }
 
-        EntryHandler entryHandler = handlersManager.getEntryHandlerByEntryPath(entryPath);
+        EntryHandler entryHandler = null;
+
+        dance : for (EntryHandler currentHandler : entryHandlers) {
+            if (currentHandler.matches(entryPath)) {
+                entryHandler = currentHandler;
+                break dance;
+            }
+        }
+
         if (entryHandler == null) {
             entryHandler = mainPackageAssembler;
         }
 
-        entryHandler.handle(entryPath, archive, entry, this);
+        entryHandler.handle(entryPath, archive, entry);
     }
 
 }
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/artifacts/DefaultArtifactsDeployer.java b/src/main/java/org/apache/sling/feature/cpconverter/artifacts/DefaultArtifactsDeployer.java
index c728642..98e3be2 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/artifacts/DefaultArtifactsDeployer.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/artifacts/DefaultArtifactsDeployer.java
@@ -26,13 +26,17 @@ import java.util.StringTokenizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
 public final class DefaultArtifactsDeployer implements ArtifactsDeployer {
 
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
     private final File artifactsDirectory;
 
-    public DefaultArtifactsDeployer(File outputDirectory) {
+    @Inject
+    public DefaultArtifactsDeployer(@Named("features.artifacts.outdir") File outputDirectory) {
         artifactsDirectory = outputDirectory;
         if (!artifactsDirectory.exists()) {
             artifactsDirectory.mkdirs();
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 ac4b92b..406be6d 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
@@ -16,20 +16,24 @@
  */
 package org.apache.sling.feature.cpconverter.cli;
 
+import static com.google.inject.Guice.createInjector;
+import static com.google.inject.name.Names.named;
+
 import java.io.File;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.Map;
 import java.util.TimeZone;
 
 import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
-import org.apache.sling.feature.cpconverter.acl.DefaultAclManager;
-import org.apache.sling.feature.cpconverter.artifacts.DefaultArtifactsDeployer;
-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.inject.ContentPackage2FeatureModelConverterModule;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.inject.AbstractModule;
+import com.google.inject.TypeLiteral;
+
 import picocli.CommandLine;
 import picocli.CommandLine.Command;
 import picocli.CommandLine.Option;
@@ -40,7 +44,7 @@ import picocli.CommandLine.Parameters;
     description = "Apache Sling Content Package to Sling Feature converter",
     footer = "Copyright(c) 2019 The Apache Software Foundation."
 )
-public final class ContentPackage2FeatureModelConverterLauncher implements Runnable {
+public final class ContentPackage2FeatureModelConverterLauncher extends AbstractModule implements Runnable {
 
     @Option(names = { "-h", "--help" }, usageHelp = true, description = "Display the usage message.")
     private boolean helpRequested;
@@ -64,7 +68,7 @@ public final class ContentPackage2FeatureModelConverterLauncher implements Runna
     private int bundlesStartOrder = 0;
 
     @Option(names = { "-f", "--filtering-patterns" }, description = "Regex based pattern(s) to reject content-package archive entries.", required = false)
-    private String[] filteringPatterns;
+    private Collection<String> filteringPatterns = new LinkedList<>();
 
     @Option(names = { "-a", "--artifacts-output-directory" }, description = "The output directory where the artifacts will be deployed.", required = true)
     private File artifactsOutputDirectory;
@@ -109,27 +113,9 @@ public final class ContentPackage2FeatureModelConverterLauncher implements Runna
         logger.info("");
 
         try {
-            ContentPackage2FeatureModelConverter converter = new ContentPackage2FeatureModelConverter(strictValidation)
-                                                             .setFeaturesManager(new DefaultFeaturesManager(mergeConfigurations,
-                                                                                                            bundlesStartOrder,
-                                                                                                            featureModelsOutputDirectory,
-                                                                                                            artifactIdOverride,
-                                                                                                            properties))
-                                                             .setBundlesDeployer(new DefaultArtifactsDeployer(artifactsOutputDirectory))
-                                                             .setEntryHandlersManager(new DefaultEntryHandlersManager())
-                                                             .setAclManager(new DefaultAclManager());
-
-            if (filteringPatterns != null && filteringPatterns.length > 0) {
-                RegexBasedResourceFilter filter = new RegexBasedResourceFilter();
-
-                for (String filteringPattern : filteringPatterns) {
-                    filter.addFilteringPattern(filteringPattern);
-                }
-
-                converter.setResourceFilter(filter);
-            }
-
-            converter.convert(contentPackages);
+            createInjector(this, new ContentPackage2FeatureModelConverterModule())
+            .getInstance(ContentPackage2FeatureModelConverter.class)
+            .convert(contentPackages);
 
             logger.info( "+-----------------------------------------------------+" );
             logger.info("{} SUCCESS", appName);
@@ -150,6 +136,20 @@ public final class ContentPackage2FeatureModelConverterLauncher implements Runna
         }
     }
 
+    @Override
+    protected void configure() {
+        bindConstant().annotatedWith(named("packagemanager.validation.strict")).to(strictValidation);
+        bindConstant().annotatedWith(named("features.configurations.merge")).to(mergeConfigurations);
+        bindConstant().annotatedWith(named("features.bundles.startOrder")).to(bundlesStartOrder);
+        bind(File.class).annotatedWith(named("features.outdir")).toInstance(featureModelsOutputDirectory);
+        bindConstant().annotatedWith(named("features.artifacts.idoverride")).to(artifactIdOverride);
+
+        bind(File.class).annotatedWith(named("features.artifacts.outdir")).toInstance(artifactsOutputDirectory);
+
+        bind(new TypeLiteral<Map<String, String>>() {}).toInstance(properties);
+        bind(new TypeLiteral<Collection<String>>() {}).toInstance(filteringPatterns);
+    }
+
     private static void printVersion(final Logger logger) {
         logger.info("{} v{} (built on {})",
                 System.getProperty("project.artifactId"),
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 526cd9e..6014a58 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
@@ -36,15 +36,15 @@ import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.ExtensionType;
 import org.apache.sling.feature.Extensions;
 import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.cpconverter.interpolator.SimpleVariablesInterpolator;
 import org.apache.sling.feature.cpconverter.interpolator.VariablesInterpolator;
 import org.apache.sling.feature.io.json.FeatureJSONWriter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class DefaultFeaturesManager implements FeaturesManager {
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
 
-    private static final String JAVA_IO_TMPDIR_PROPERTY = "java.io.tmpdir";
+public class DefaultFeaturesManager implements FeaturesManager {
 
     private static final String CONTENT_PACKAGES = "content-packages";
 
@@ -56,36 +56,30 @@ public class DefaultFeaturesManager implements FeaturesManager {
 
     private final Map<String, Feature> runModes = new HashMap<>();
 
-    private final VariablesInterpolator interpolator = new SimpleVariablesInterpolator();
+    @Inject
+    private VariablesInterpolator interpolator;
 
-    private final boolean mergeConfigurations;
+    @Inject
+    @Named("features.configurations.merge")
+    private boolean mergeConfigurations;
 
-    private final int bundlesStartOrder;
+    @Inject
+    @Named("features.bundles.startOrder")
+    private int bundlesStartOrder;
 
-    private final File featureModelsOutputDirectory;
+    @Inject
+    @Named("features.outdir")
+    private File featureModelsOutputDirectory;
 
-    private final String artifactIdOverride;
+    @Inject(optional = true)
+    @Named("features.artifacts.idoverride")
+    private String artifactIdOverride;
 
-    private final Map<String, String> properties;
+    @Inject(optional = true)
+    private Map<String, String> properties;
 
     private Feature targetFeature = null;
 
-    public DefaultFeaturesManager() {
-        this(true, 20, new File(System.getProperty(JAVA_IO_TMPDIR_PROPERTY)), null, null);
-    }
-
-    public DefaultFeaturesManager(boolean mergeConfigurations,
-                                  int bundlesStartOrder,
-                                  File featureModelsOutputDirectory,
-                                  String artifactIdOverride,
-                                  Map<String, String> properties) {
-        this.mergeConfigurations = mergeConfigurations;
-        this.bundlesStartOrder = bundlesStartOrder;
-        this.featureModelsOutputDirectory = featureModelsOutputDirectory;
-        this.artifactIdOverride = artifactIdOverride;
-        this.properties = properties;
-    }
-
     public void init(String groupId, String artifactId, String version) {
         targetFeature = new Feature(new ArtifactId(groupId, artifactId, version, null, SLING_OSGI_FEATURE_TILE_TYPE));
         runModes.clear();
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/filtering/RegexBasedResourceFilter.java b/src/main/java/org/apache/sling/feature/cpconverter/filtering/RegexBasedResourceFilter.java
index d6fd02a..2ea5757 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/filtering/RegexBasedResourceFilter.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/filtering/RegexBasedResourceFilter.java
@@ -18,6 +18,7 @@ package org.apache.sling.feature.cpconverter.filtering;
 
 import static java.util.Objects.requireNonNull;
 
+import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.regex.Pattern;
@@ -25,12 +26,25 @@ import java.util.regex.Pattern;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.inject.Inject;
+
 public final class RegexBasedResourceFilter implements ResourceFilter {
 
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
     private final List<Pattern> patterns = new LinkedList<>();
 
+    @Inject(optional = true)
+    public void addFilteringPatterns(Collection<String> filteringPatterns) {
+        if (filteringPatterns == null || filteringPatterns.isEmpty()) {
+            return;
+        }
+
+        for (String filteringPattern : filteringPatterns) {
+            addFilteringPattern(filteringPattern);
+        }
+    }
+
     public void addFilteringPattern(String filteringPattern) {
         requireNonNull(filteringPattern, "Null pattern to filter resources out is not a valid filtering pattern");
 
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractConfigurationEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractConfigurationEntryHandler.java
index 8216e48..ee286f4 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractConfigurationEntryHandler.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractConfigurationEntryHandler.java
@@ -23,15 +23,24 @@ import java.util.regex.Matcher;
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
 import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.apache.sling.feature.cpconverter.features.FeaturesManager;
+
+import com.google.inject.Inject;
 
 abstract class AbstractConfigurationEntryHandler extends AbstractRegexEntryHandler {
 
+    @Inject
+    private ContentPackage2FeatureModelConverter converter;
+
+    @Inject
+    private FeaturesManager featuresManager;
+
     public AbstractConfigurationEntryHandler(String extension) {
         super("(jcr_root)?/(?:apps|libs)/.+/config(\\.([^/]+))?/.+\\." + extension);
     }
 
     @Override
-    public final void handle(String path, Archive archive, Entry entry, ContentPackage2FeatureModelConverter converter) throws Exception {
+    public final void handle(String path, Archive archive, Entry entry) throws Exception {
         String pid = entry.getName().substring(0, entry.getName().lastIndexOf('.'));
 
         String id;
@@ -77,7 +86,7 @@ abstract class AbstractConfigurationEntryHandler extends AbstractRegexEntryHandl
                                             + "' but it does not, currently");
         }
 
-        converter.getFeaturesManager().addConfiguration(runMode, id, configurationProperties);
+        featuresManager.addConfiguration(runMode, id, configurationProperties);
     }
 
     protected abstract Dictionary<String, Object> parseConfiguration(String name, InputStream input) throws Exception;
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractContentPackageHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractContentPackageHandler.java
index 103a18f..1b93296 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractContentPackageHandler.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractContentPackageHandler.java
@@ -24,24 +24,37 @@ import java.io.OutputStream;
 import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.jackrabbit.vault.packaging.PackageManager;
 import org.apache.jackrabbit.vault.packaging.VaultPackage;
-import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
 
 public abstract class AbstractContentPackageHandler extends AbstractRegexEntryHandler {
 
-    private final File temporaryDir = new File(System.getProperty("java.io.tmpdir"), "sub-content-packages");
+    private final File subContentPackagesDir;
+
+    @Inject
+    private PackageManager packageManager;
+
+    @Inject
+    @Named("packagemanager.validation.strict")
+    protected boolean strictValidation;
 
-    public AbstractContentPackageHandler() {
+    public AbstractContentPackageHandler(File temporaryDir) {
         super("(?:jcr_root)?/etc/packages/.+\\.zip");
-        temporaryDir.mkdirs();
+
+        subContentPackagesDir = new File(temporaryDir, "sub-content-packages");
+        if (!subContentPackagesDir.exists()) {
+            subContentPackagesDir.mkdirs();
+        }
     }
 
     @Override
-    public final void handle(String path, Archive archive, Entry entry, ContentPackage2FeatureModelConverter converter)
-            throws Exception {
+    public final void handle(String path, Archive archive, Entry entry) throws Exception {
         logger.info("Processing sub-content package '{}'...", entry.getName());
 
-        File temporaryContentPackage = new File(temporaryDir, entry.getName());
+        File temporaryContentPackage = new File(subContentPackagesDir, entry.getName());
 
         if (!temporaryContentPackage.exists()) {
             logger.debug("Extracting sub-content package '{}' to {} for future analysis...", entry.getName(), temporaryContentPackage);
@@ -54,13 +67,13 @@ public abstract class AbstractContentPackageHandler extends AbstractRegexEntryHa
             logger.debug("Sub-content package '{}' successfully extracted to {} ", entry.getName(), temporaryContentPackage);
         }
 
-        try (VaultPackage vaultPackage = converter.open(temporaryContentPackage)) {
-            processSubPackage(path, vaultPackage, converter);
+        try (VaultPackage vaultPackage = packageManager.open(temporaryContentPackage, strictValidation)) {
+            processSubPackage(path, vaultPackage);
         }
 
         logger.info("Sub-content package '{}' processing is over", entry.getName());
     }
 
-    protected abstract void processSubPackage(String path, VaultPackage contentPackage, ContentPackage2FeatureModelConverter converter) throws Exception;
+    protected abstract void processSubPackage(String path, VaultPackage contentPackage) throws Exception;
 
 }
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 63b9533..8621190 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
@@ -30,11 +30,14 @@ import java.util.regex.Pattern;
 
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
-import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.apache.sling.feature.cpconverter.artifacts.ArtifactsDeployer;
 import org.apache.sling.feature.cpconverter.artifacts.InputStreamArtifactWriter;
+import org.apache.sling.feature.cpconverter.features.FeaturesManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.inject.Inject;
+
 public final class BundleEntryHandler extends AbstractRegexEntryHandler {
 
     private static final String NAME_GROUP_ID = "groupId";
@@ -55,12 +58,18 @@ public final class BundleEntryHandler extends AbstractRegexEntryHandler {
 
     private final Pattern pomPropertiesPattern = Pattern.compile("META-INF/maven/[^/]+/[^/]+/pom.properties");
 
+    @Inject
+    private ArtifactsDeployer artifactsDeployer;
+
+    @Inject
+    private FeaturesManager featuresManager;
+
     public BundleEntryHandler() {
         super("(jcr_root)?/(?:apps|libs)/.+/install(\\.([^/]+))?/.+\\.jar");
     }
 
     @Override
-    public void handle(String path, Archive archive, Entry entry, ContentPackage2FeatureModelConverter converter) throws Exception {
+    public void handle(String path, Archive archive, Entry entry) throws Exception {
         logger.info("Processing bundle {}...", entry.getName());
 
         String groupId;
@@ -99,19 +108,19 @@ public final class BundleEntryHandler extends AbstractRegexEntryHandler {
         }
 
         try (InputStream input = archive.openInputStream(entry)) {
-            converter.getArtifactsDeployer().deploy(new InputStreamArtifactWriter(input),
-                                                  groupId,
-                                                  artifactId,
-                                                  version,
-                                                  classifier,
-                                                  JAR_TYPE);
-
-            converter.getFeaturesManager().addArtifact(runMode,
-                                                       groupId,
-                                                       artifactId,
-                                                       version,
-                                                       classifier,
-                                                       JAR_TYPE);
+            artifactsDeployer.deploy(new InputStreamArtifactWriter(input),
+                                     groupId,
+                                     artifactId,
+                                     version,
+                                     classifier,
+                                     JAR_TYPE);
+
+            featuresManager.addArtifact(runMode,
+                                        groupId,
+                                        artifactId,
+                                        version,
+                                        classifier,
+                                        JAR_TYPE);
         }
     }
 
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandler.java
index 2fde1a0..0159e7f 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandler.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandler.java
@@ -16,14 +16,26 @@
  */
 package org.apache.sling.feature.cpconverter.handlers;
 
+import java.io.File;
+
 import org.apache.jackrabbit.vault.packaging.VaultPackage;
 import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
 
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
 public final class ContentPackageEntryHandler extends AbstractContentPackageHandler {
 
+    @Inject
+    private ContentPackage2FeatureModelConverter converter;
+
+    @Inject
+    public ContentPackageEntryHandler(@Named("java.io.tmpdir") File temporaryDir) {
+        super(temporaryDir);
+    }
+
     @Override
-    protected void processSubPackage(String path, VaultPackage contentPackage, ContentPackage2FeatureModelConverter converter)
-            throws Exception {
+    protected void processSubPackage(String path, VaultPackage contentPackage) throws Exception {
         converter.processSubPackage(path, contentPackage);
     }
 
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
deleted file mode 100644
index 16039a9..0000000
--- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/DefaultEntryHandlersManager.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.cpconverter.handlers;
-
-import java.util.Iterator;
-import java.util.ServiceLoader;
-
-public class DefaultEntryHandlersManager implements EntryHandlersManager {
-
-    private final ServiceLoader<EntryHandler> entryHandlers = ServiceLoader.load(EntryHandler.class);
-
-    @Override
-    public EntryHandler getEntryHandlerByEntryPath(String path) {
-        Iterator<EntryHandler> entryHandlersIterator = entryHandlers.iterator();
-        while (entryHandlersIterator.hasNext()) {
-            EntryHandler entryHandler = entryHandlersIterator.next();
-
-            if (entryHandler.matches(path)) {
-                return entryHandler;
-            }
-        }
-
-        return null;
-    }
-
-}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/EntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/EntryHandler.java
index 5518313..4544b5c 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/EntryHandler.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/EntryHandler.java
@@ -18,12 +18,11 @@ package org.apache.sling.feature.cpconverter.handlers;
 
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
-import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
 
 public interface EntryHandler {
 
     boolean matches(String path);
 
-    void handle(String path, Archive archive, Entry entry, ContentPackage2FeatureModelConverter converter) throws Exception;
+    void handle(String path, Archive archive, Entry entry) throws Exception;
 
 }
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandler.java
index 7e0109a..6deacc0 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandler.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandler.java
@@ -26,22 +26,25 @@ import java.util.regex.Matcher;
 
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
-import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
 import org.apache.sling.feature.cpconverter.acl.Acl;
 import org.apache.sling.feature.cpconverter.acl.AclManager;
 import org.apache.sling.feature.cpconverter.shared.AbstractJcrNodeParser;
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 
+import com.google.inject.Inject;
+
 public final class RepPolicyEntryHandler extends AbstractRegexEntryHandler {
 
+    @Inject
+    private AclManager aclManager;
+
     public RepPolicyEntryHandler() {
         super("(jcr_root)?(/.+)/_rep_policy.xml");
     }
 
     @Override
-    public void handle(String path, Archive archive, Entry entry, ContentPackage2FeatureModelConverter converter)
-            throws Exception {
+    public void handle(String path, Archive archive, Entry entry) throws Exception {
         Matcher matcher = getPattern().matcher(path);
         // we are pretty sure it matches, here
         if (matcher.matches()) {
@@ -54,7 +57,7 @@ public final class RepPolicyEntryHandler extends AbstractRegexEntryHandler {
                                             + "' but it does not, currently");
         }
 
-        RepPolicyParser systemUserParser = new RepPolicyParser(path, converter.getAclManager());
+        RepPolicyParser systemUserParser = new RepPolicyParser(path, aclManager);
         try (InputStream input = archive.openInputStream(entry)) {
             systemUserParser.parse(input);
         }
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/SystemUsersEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/SystemUsersEntryHandler.java
index 17e22c0..4bc99de 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/SystemUsersEntryHandler.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/SystemUsersEntryHandler.java
@@ -20,20 +20,24 @@ import java.io.InputStream;
 
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
-import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.apache.sling.feature.cpconverter.acl.AclManager;
 import org.apache.sling.feature.cpconverter.shared.AbstractJcrNodeParser;
 import org.xml.sax.Attributes;
 
+import com.google.inject.Inject;
+
 public final class SystemUsersEntryHandler extends AbstractRegexEntryHandler {
 
+    @Inject
+    private AclManager aclManager;
+
     public SystemUsersEntryHandler() {
         super("(jcr_root)?/home/users/.*/\\.content.xml");
     }
 
     @Override
-    public void handle(String path, Archive archive, Entry entry, ContentPackage2FeatureModelConverter converter)
-            throws Exception {
-        SystemUserParser systemUserParser = new SystemUserParser(converter);
+    public void handle(String path, Archive archive, Entry entry) throws Exception {
+        SystemUserParser systemUserParser = new SystemUserParser(aclManager);
         try (InputStream input = archive.openInputStream(entry)) {
             systemUserParser.parse(input);
         }
@@ -45,18 +49,18 @@ public final class SystemUsersEntryHandler extends AbstractRegexEntryHandler {
 
         private final static String REP_AUTHORIZABLE_ID = "rep:authorizableId";
 
-        private final ContentPackage2FeatureModelConverter converter;
+        private final AclManager aclManager;
 
-        public SystemUserParser(ContentPackage2FeatureModelConverter converter) {
+        public SystemUserParser(AclManager aclManager) {
             super(REP_SYSTEM_USER);
-            this.converter = converter;
+            this.aclManager = aclManager;
         }
 
         @Override
         protected void onJcrRootElement(String uri, String localName, String qName, Attributes attributes) {
             String authorizableId = attributes.getValue(REP_AUTHORIZABLE_ID);
             if (authorizableId != null && !authorizableId.isEmpty()) {
-                converter.getAclManager().addSystemUser(authorizableId);
+                aclManager.addSystemUser(authorizableId);
             }
         }
 
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/VersionResolverContentPackageEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/VersionResolverContentPackageEntryHandler.java
index 2a57822..474d3e8 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/VersionResolverContentPackageEntryHandler.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/VersionResolverContentPackageEntryHandler.java
@@ -16,28 +16,31 @@
  */
 package org.apache.sling.feature.cpconverter.handlers;
 
+import java.io.File;
 import java.util.Map;
 
 import org.apache.jackrabbit.vault.packaging.PackageId;
 import org.apache.jackrabbit.vault.packaging.VaultPackage;
-import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
 public final class VersionResolverContentPackageEntryHandler extends AbstractContentPackageHandler {
 
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
-    private final Map<PackageId, String> subContentPackages;
-
-    public VersionResolverContentPackageEntryHandler(Map<PackageId, String> subContentPackages) {
-        this.subContentPackages = subContentPackages;
+    @Inject
+    public VersionResolverContentPackageEntryHandler(@Named("java.io.tmpdir") File temporaryDir) {
+        super(temporaryDir);
     }
 
-    @Override
-    protected void processSubPackage(String path, VaultPackage contentPackage, ContentPackage2FeatureModelConverter converter)
-            throws Exception {
+    @Inject
+    private Map<PackageId, String> subContentPackages;
 
+    @Override
+    protected void processSubPackage(String path, VaultPackage contentPackage) throws Exception {
         boolean addPackage = false;
         PackageId currentId = contentPackage.getId();
 
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/inject/ContentPackage2FeatureModelConverterModule.java b/src/main/java/org/apache/sling/feature/cpconverter/inject/ContentPackage2FeatureModelConverterModule.java
new file mode 100644
index 0000000..43dd742
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/inject/ContentPackage2FeatureModelConverterModule.java
@@ -0,0 +1,85 @@
+/*
+ * 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.inject;
+
+import static com.google.inject.Scopes.SINGLETON;
+import static com.google.inject.multibindings.Multibinder.newSetBinder;
+import static com.google.inject.name.Names.named;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.jackrabbit.vault.packaging.PackageId;
+import org.apache.jackrabbit.vault.packaging.PackageManager;
+import org.apache.jackrabbit.vault.packaging.impl.PackageManagerImpl;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.apache.sling.feature.cpconverter.acl.AclManager;
+import org.apache.sling.feature.cpconverter.acl.DefaultAclManager;
+import org.apache.sling.feature.cpconverter.artifacts.ArtifactsDeployer;
+import org.apache.sling.feature.cpconverter.artifacts.DefaultArtifactsDeployer;
+import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager;
+import org.apache.sling.feature.cpconverter.features.FeaturesManager;
+import org.apache.sling.feature.cpconverter.filtering.RegexBasedResourceFilter;
+import org.apache.sling.feature.cpconverter.filtering.ResourceFilter;
+import org.apache.sling.feature.cpconverter.handlers.BundleEntryHandler;
+import org.apache.sling.feature.cpconverter.handlers.ConfigurationEntryHandler;
+import org.apache.sling.feature.cpconverter.handlers.ContentPackageEntryHandler;
+import org.apache.sling.feature.cpconverter.handlers.EntryHandler;
+import org.apache.sling.feature.cpconverter.handlers.JsonConfigurationEntryHandler;
+import org.apache.sling.feature.cpconverter.handlers.PropertiesConfigurationEntryHandler;
+import org.apache.sling.feature.cpconverter.handlers.RepPolicyEntryHandler;
+import org.apache.sling.feature.cpconverter.handlers.SystemUsersEntryHandler;
+import org.apache.sling.feature.cpconverter.handlers.XmlConfigurationEntryHandler;
+import org.apache.sling.feature.cpconverter.interpolator.SimpleVariablesInterpolator;
+import org.apache.sling.feature.cpconverter.interpolator.VariablesInterpolator;
+import org.apache.sling.feature.cpconverter.vltpkg.RecollectorVaultPackageScanner;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.TypeLiteral;
+import com.google.inject.multibindings.Multibinder;
+
+public final class ContentPackage2FeatureModelConverterModule extends AbstractModule {
+
+    @Override
+    protected void configure() {
+        bind(File.class).annotatedWith(named("java.io.tmpdir")).toInstance(new File(System.getProperty("java.io.tmpdir")));
+
+        bind(new TypeLiteral<Map<PackageId, String>>() {}).toInstance(new HashMap<PackageId, String>());
+        bind(PackageManager.class).to(PackageManagerImpl.class).in(SINGLETON);
+        bind(AclManager.class).to(DefaultAclManager.class).in(SINGLETON);
+        bind(ArtifactsDeployer.class).to(DefaultArtifactsDeployer.class).in(SINGLETON);
+        bind(FeaturesManager.class).to(DefaultFeaturesManager.class).in(SINGLETON);
+        bind(ResourceFilter.class).to(RegexBasedResourceFilter.class).in(SINGLETON);
+
+        // handlers
+        Multibinder<EntryHandler> handlers = newSetBinder(binder(), EntryHandler.class);
+        handlers.addBinding().to(BundleEntryHandler.class);
+        handlers.addBinding().to(ConfigurationEntryHandler.class);
+        handlers.addBinding().to(ContentPackageEntryHandler.class);
+        handlers.addBinding().to(JsonConfigurationEntryHandler.class);
+        handlers.addBinding().to(PropertiesConfigurationEntryHandler.class);
+        handlers.addBinding().to(RepPolicyEntryHandler.class);
+        handlers.addBinding().to(SystemUsersEntryHandler.class);
+        handlers.addBinding().to(XmlConfigurationEntryHandler.class);
+
+        bind(VariablesInterpolator.class).to(SimpleVariablesInterpolator.class).in(SINGLETON);
+        bind(RecollectorVaultPackageScanner.class).in(SINGLETON);
+        bind(ContentPackage2FeatureModelConverter.class).asEagerSingleton();
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/EntryHandlersManager.java b/src/main/java/org/apache/sling/feature/cpconverter/inject/package-info.java
similarity index 83%
rename from src/main/java/org/apache/sling/feature/cpconverter/handlers/EntryHandlersManager.java
rename to src/main/java/org/apache/sling/feature/cpconverter/inject/package-info.java
index c2052b0..2817c2b 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/EntryHandlersManager.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/inject/package-info.java
@@ -14,10 +14,4 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.sling.feature.cpconverter.handlers;
-
-public interface EntryHandlersManager {
-
-    EntryHandler getEntryHandlerByEntryPath(String path);
-
-}
+package org.apache.sling.feature.cpconverter.inject;
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/BaseVaultPackageScanner.java b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/BaseVaultPackageScanner.java
index 5b67e06..82de8e8 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/BaseVaultPackageScanner.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/BaseVaultPackageScanner.java
@@ -24,26 +24,22 @@ import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
 import org.apache.jackrabbit.vault.packaging.PackageManager;
 import org.apache.jackrabbit.vault.packaging.VaultPackage;
-import org.apache.jackrabbit.vault.packaging.impl.PackageManagerImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
 public abstract class BaseVaultPackageScanner {
 
     protected final Logger logger = LoggerFactory.getLogger(getClass());
 
-    protected final PackageManager packageManager;
-
-    protected final boolean strictValidation;
+    @Inject
+    protected PackageManager packageManager;
 
-    public BaseVaultPackageScanner(boolean strictValidation) {
-        this(new PackageManagerImpl(), strictValidation);
-    }
-
-    public BaseVaultPackageScanner(PackageManager packageManager, boolean strictValidation) {
-        this.packageManager = packageManager;
-        this.strictValidation = strictValidation;
-    }
+    @Inject
+    @Named("packagemanager.validation.strict")
+    protected boolean strictValidation;
 
     public VaultPackage open(File vaultPackage) throws Exception {
         requireNonNull(vaultPackage, "Impossible to process a null vault package");
@@ -53,7 +49,7 @@ public abstract class BaseVaultPackageScanner {
     public final void traverse(File vaultPackageFile, boolean closeOnTraversed) throws Exception {
         VaultPackage vaultPackage = null;
         try {
-            vaultPackage = open(vaultPackageFile);
+            vaultPackage = packageManager.open(vaultPackageFile, strictValidation);
             traverse(vaultPackage);
         } finally {
             if (closeOnTraversed) {
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/RecollectorVaultPackageScanner.java b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/RecollectorVaultPackageScanner.java
index 1d0a747..be777bc 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/RecollectorVaultPackageScanner.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/RecollectorVaultPackageScanner.java
@@ -16,34 +16,21 @@
  */
 package org.apache.sling.feature.cpconverter.vltpkg;
 
-import java.util.Map;
-
 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.PackageManager;
-import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
 import org.apache.sling.feature.cpconverter.handlers.VersionResolverContentPackageEntryHandler;
 
-public final class RecollectorVaultPackageScanner extends BaseVaultPackageScanner {
-
-    private final ContentPackage2FeatureModelConverter converter;
+import com.google.inject.Inject;
 
-    private final VersionResolverContentPackageEntryHandler handler;
+public final class RecollectorVaultPackageScanner extends BaseVaultPackageScanner {
 
-    public RecollectorVaultPackageScanner(ContentPackage2FeatureModelConverter converter,
-                                          PackageManager packageManager,
-                                          boolean strictValidation,
-                                          Map<PackageId, String> subContentPackages) {
-        super(packageManager, strictValidation);
-        this.converter = converter;
-        handler = new VersionResolverContentPackageEntryHandler(subContentPackages);
-    }
+    @Inject
+    private VersionResolverContentPackageEntryHandler handler;
 
     @Override
     protected void onFile(String path, Archive archive, Entry entry) throws Exception {
         if (handler.matches(path)) {
-            handler.handle(path, archive, entry, converter);
+            handler.handle(path, archive, entry);
         }
     }
 
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 b6cb964..3098e0c 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
@@ -36,7 +36,6 @@ import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
 import org.apache.jackrabbit.vault.packaging.PackageProperties;
 import org.apache.jackrabbit.vault.packaging.VaultPackage;
-import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
 import org.apache.sling.feature.cpconverter.handlers.EntryHandler;
 import org.codehaus.plexus.archiver.Archiver;
 import org.codehaus.plexus.archiver.jar.JarArchiver;
@@ -122,8 +121,7 @@ public class VaultPackageAssembler implements EntryHandler {
     }
 
     @Override
-    public void handle(String path, Archive archive, Entry entry, ContentPackage2FeatureModelConverter converter)
-            throws Exception {
+    public void handle(String path, Archive archive, Entry entry) throws Exception {
         addEntry(path, archive, entry);
     }
 
diff --git a/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.handlers.EntryHandler b/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.handlers.EntryHandler
deleted file mode 100644
index 53e84b4..0000000
--- a/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.handlers.EntryHandler
+++ /dev/null
@@ -1,8 +0,0 @@
-org.apache.sling.feature.cpconverter.handlers.BundleEntryHandler
-org.apache.sling.feature.cpconverter.handlers.ConfigurationEntryHandler
-org.apache.sling.feature.cpconverter.handlers.ContentPackageEntryHandler
-org.apache.sling.feature.cpconverter.handlers.JsonConfigurationEntryHandler
-org.apache.sling.feature.cpconverter.handlers.PropertiesConfigurationEntryHandler
-org.apache.sling.feature.cpconverter.handlers.RepPolicyEntryHandler
-org.apache.sling.feature.cpconverter.handlers.SystemUsersEntryHandler
-org.apache.sling.feature.cpconverter.handlers.XmlConfigurationEntryHandler
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 ea6e83e..90e74b0 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 java.util.Arrays.asList;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -23,35 +24,28 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileReader;
-import java.io.Reader;
 import java.net.URL;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Hashtable;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Properties;
-import java.util.StringTokenizer;
 import java.util.zip.ZipFile;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.jackrabbit.vault.packaging.CyclicDependencyException;
 import org.apache.jackrabbit.vault.packaging.VaultPackage;
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.cpconverter.acl.DefaultAclManager;
-import org.apache.sling.feature.cpconverter.artifacts.DefaultArtifactsDeployer;
-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.io.json.FeatureJSONReader;
+import org.apache.sling.feature.cpconverter.artifacts.ArtifactsDeployer;
+import org.apache.sling.feature.cpconverter.features.FeaturesManager;
+import org.apache.sling.feature.cpconverter.shared.AbstractContentPackage2FeatureModelConverterTest;
 import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 
-public class ContentPackage2FeatureModelConverterTest {
+import com.google.inject.Inject;
+import com.google.inject.TypeLiteral;
+
+public class ContentPackage2FeatureModelConverterTest extends AbstractContentPackage2FeatureModelConverterTest {
 
     /**
      * Test package A-1.0. Depends on B and C-1.X
@@ -67,13 +61,20 @@ public class ContentPackage2FeatureModelConverterTest {
                                                                 "test_b-1.0.zip",
                                                                 "test_e-1.0.zip" };
 
+    @Inject
     private ContentPackage2FeatureModelConverter converter;
 
-    @Before
-    public void setUp() {
-        converter = new ContentPackage2FeatureModelConverter()
-                    .setEntryHandlersManager(new DefaultEntryHandlersManager())
-                    .setAclManager(new DefaultAclManager());
+    @Inject
+    private ArtifactsDeployer artifactsDeployer;
+
+    @Inject
+    private FeaturesManager featuresManager;
+
+    @Override
+    protected void configure() {
+        super.configure();
+
+        bind(new TypeLiteral<Collection<String>>() {}).toInstance(asList(".*\\/install(?!(\\.runMode1\\/|\\.runMode2\\/|\\/))(.*)(?=\\.zip$).*"));
     }
 
     @After
@@ -112,11 +113,9 @@ public class ContentPackage2FeatureModelConverterTest {
         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 outputDirectory = artifactsDeployer.getBundlesDirectory();
 
-        converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, null, null))
-                 .setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
-                 .convert(packageFile);
+        converter.convert(packageFile);
 
         verifyFeatureFile(outputDirectory,
                           "asd.retail.all.json",
@@ -170,78 +169,12 @@ public class ContentPackage2FeatureModelConverterTest {
         zipFile.close();
     }
 
-    private void verifyFeatureFile(File outputDirectory,
-                                   String name,
-                                   String expectedArtifactId,
-                                   List<String> expectedArtifacts,
-                                   List<String> expectedConfigurations,
-                                   List<String> expectedContentPackagesExtensions) throws Exception {
-        File featureFile = new File(outputDirectory, name);
-        assertTrue(featureFile + " was not correctly created", featureFile.exists());
-
-        try (Reader reader = new FileReader(featureFile)) {
-            Feature feature = FeatureJSONReader.read(reader, featureFile.getAbsolutePath());
-
-            assertEquals(expectedArtifactId, feature.getId().toMvnId());
-
-            for (String expectedArtifact : expectedArtifacts) {
-                assertTrue(expectedArtifact + " not found in Feature " + expectedArtifactId, feature.getBundles().containsExact(ArtifactId.fromMvnId(expectedArtifact)));
-                verifyInstalledArtifact(outputDirectory, expectedArtifact);
-            }
-
-            for (String expectedConfiguration : expectedConfigurations) {
-                assertNotNull(expectedConfiguration + " not found in Feature " + expectedArtifactId, feature.getConfigurations().getConfiguration(expectedConfiguration));
-            }
-
-            for (String expectedContentPackagesExtension : expectedContentPackagesExtensions) {
-                assertTrue(expectedContentPackagesExtension + " not found in Feature " + expectedArtifactId,
-                           feature.getExtensions().getByName("content-packages").getArtifacts().containsExact(ArtifactId.fromMvnId(expectedContentPackagesExtension)));
-                verifyInstalledArtifact(outputDirectory, expectedContentPackagesExtension);
-            }
-        }
-    }
-
-    private void verifyInstalledArtifact(File outputDirectory, String coordinates) {
-        ArtifactId bundleId = ArtifactId.fromMvnId(coordinates);
-
-        StringTokenizer tokenizer = new StringTokenizer(bundleId.getGroupId(), ".");
-        while (tokenizer.hasMoreTokens()) {
-            outputDirectory = new File(outputDirectory, tokenizer.nextToken());
-        }
-
-        outputDirectory = new File(outputDirectory, bundleId.getArtifactId());
-        outputDirectory = new File(outputDirectory, bundleId.getVersion());
-
-        StringBuilder bundleFileName = new StringBuilder()
-                                       .append(bundleId.getArtifactId())
-                                       .append('-')
-                                       .append(bundleId.getVersion());
-        if (bundleId.getClassifier() != null) {
-            bundleFileName.append('-').append(bundleId.getClassifier());
-        }
-        bundleFileName.append('.').append(bundleId.getType());
-
-        File bundleFile = new File(outputDirectory, bundleFileName.toString());
-        assertTrue("Bundle " + bundleFile + " does not exist", bundleFile.exists());
-
-        File pomFile = new File(outputDirectory, String.format("%s-%s.pom", bundleId.getArtifactId(), bundleId.getVersion()));
-        assertTrue("POM file " + pomFile + " does not exist", pomFile.exists());
-    }
-
     @Test(expected = IllegalArgumentException.class)
     public void verifyFilteringOutUndesiredPackages() throws Exception {
-        RegexBasedResourceFilter resourceFilter = new RegexBasedResourceFilter();
-        resourceFilter.addFilteringPattern(".*\\/install(?!(\\.runMode1\\/|\\.runMode2\\/|\\/))(.*)(?=\\.zip$).*");
-        converter.setResourceFilter(resourceFilter);
-
         URL packageUrl = getClass().getResource("test-content-package-unacceptable.zip");
         File packageFile = FileUtils.toFile(packageUrl);
 
-        File outputDirectory = new File(System.getProperty("java.io.tmpdir"), getClass().getName() + '_' + System.currentTimeMillis());
-
-        converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, null, null))
-                 .setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
-                 .convert(packageFile);
+        converter.convert(packageFile);
     }
 
     @Test(expected = IllegalStateException.class)
@@ -255,67 +188,14 @@ public class ContentPackage2FeatureModelConverterTest {
     }
 
     private void addSamePidConfiguration(String runmodeA, String runmodeB) throws Exception {
-        File outputDirectory = new File(System.getProperty("java.io.tmpdir"), getClass().getName() + '_' + System.currentTimeMillis());
         URL packageUrl = getClass().getResource("test-content-package.zip");
         File packageFile = FileUtils.toFile(packageUrl);
 
-        converter.setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
-                 .setFeaturesManager(new DefaultFeaturesManager(false, 5, outputDirectory, null, null))
-                 .convert(packageFile);
+        converter.convert(packageFile);
 
         String pid = "this.is.just.a.pid";
-        converter.getFeaturesManager().addConfiguration(runmodeA, pid, new Hashtable<String, Object>());
-        converter.getFeaturesManager().addConfiguration(runmodeB, pid, new Hashtable<String, Object>());
-    }
-
-    @Test
-    public void overrideFeatureId() 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());
-
-        String overrideId = "${project.groupId}:${project.artifactId}:slingosgifeature:asd.test.all-1.0.0:${project.version}";
-        converter.setFeaturesManager(new DefaultFeaturesManager(true, 5, outputDirectory, overrideId, null))
-                 .setBundlesDeployer(new DefaultArtifactsDeployer(outputDirectory))
-                 .convert(packageFile);
-
-        verifyFeatureFile(outputDirectory,
-                          "asd.retail.all.json",
-                          "${project.groupId}:${project.artifactId}:slingosgifeature:asd.test.all-1.0.0:${project.version}",
-                          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.all:zip:cp2fm-converted:0.0.1"));
-        verifyFeatureFile(outputDirectory,
-                          "asd.retail.all-author.json",
-                          "${project.groupId}:${project.artifactId}:slingosgifeature:asd.test.all-1.0.0-author:${project.version}",
-                          Arrays.asList("org.apache.sling:org.apache.sling.api:2.20.0"),
-                          Collections.emptyList(),
-                          Collections.emptyList());
-        verifyFeatureFile(outputDirectory,
-                          "asd.retail.all-publish.json",
-                          "${project.groupId}:${project.artifactId}:slingosgifeature:asd.test.all-1.0.0-publish:${project.version}",
-                          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());
-
-        ZipFile zipFile = new ZipFile(new File(outputDirectory, "asd/sample/asd.retail.all/0.0.1/asd.retail.all-0.0.1-cp2fm-converted.zip"));
-        for (String expectedEntry : new String[] {
-                "jcr_root/content/asd/.content.xml",
-                "jcr_root/content/asd/resources.xml",
-                "jcr_root/apps/.content.xml",
-                "META-INF/vault/properties.xml",
-                "META-INF/vault/config.xml",
-                "META-INF/vault/settings.xml",
-                "META-INF/vault/filter.xml",
-                "META-INF/vault/definition/.content.xml",
-                "jcr_root/etc/packages/asd/test-bundles.zip",
-                "jcr_root/etc/packages/asd/test-configurations.zip",
-                "jcr_root/etc/packages/asd/test-content.zip",
-                }) {
-            assertNotNull(zipFile.getEntry(expectedEntry));
-        }
-        zipFile.close();
+        featuresManager.addConfiguration(runmodeA, pid, new Hashtable<String, Object>());
+        featuresManager.addConfiguration(runmodeB, pid, new Hashtable<String, Object>());
     }
 
     @Test
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/IdOverrideTest.java b/src/test/java/org/apache/sling/feature/cpconverter/IdOverrideTest.java
new file mode 100644
index 0000000..adc08cd
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/cpconverter/IdOverrideTest.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;
+
+import static com.google.inject.name.Names.named;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.zip.ZipFile;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.sling.feature.cpconverter.artifacts.ArtifactsDeployer;
+import org.apache.sling.feature.cpconverter.inject.ContentPackage2FeatureModelConverterModule;
+import org.apache.sling.feature.cpconverter.shared.AbstractContentPackage2FeatureModelConverterTest;
+import org.junit.Test;
+
+import com.google.inject.Inject;
+
+public class IdOverrideTest extends AbstractContentPackage2FeatureModelConverterTest {
+
+    @Inject
+    private ContentPackage2FeatureModelConverter converter;
+
+    @Inject
+    private ArtifactsDeployer artifactsDeployer;
+
+    @Override
+    protected void configure() {
+        super.configure();
+
+        install(new ContentPackage2FeatureModelConverterModule());
+
+        bindConstant().annotatedWith(named("packagemanager.validation.strict")).to(true);
+        bindConstant().annotatedWith(named("features.configurations.merge")).to(true);
+        bindConstant().annotatedWith(named("features.bundles.startOrder")).to(5);
+
+        bindConstant().annotatedWith(named("features.artifacts.idoverride")).to("${project.groupId}:${project.artifactId}:slingosgifeature:asd.test.all-1.0.0:${project.version}");
+    }
+
+    @Test
+    public void overrideFeatureId() throws Exception {
+        URL packageUrl = getClass().getResource("test-content-package.zip");
+        File packageFile = FileUtils.toFile(packageUrl);
+
+        File outputDirectory = artifactsDeployer.getBundlesDirectory();
+
+        converter.convert(packageFile);
+
+        verifyFeatureFile(outputDirectory,
+                          "asd.retail.all.json",
+                          "${project.groupId}:${project.artifactId}:slingosgifeature:asd.test.all-1.0.0:${project.version}",
+                          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.all:zip:cp2fm-converted:0.0.1"));
+        verifyFeatureFile(outputDirectory,
+                          "asd.retail.all-author.json",
+                          "${project.groupId}:${project.artifactId}:slingosgifeature:asd.test.all-1.0.0-author:${project.version}",
+                          Arrays.asList("org.apache.sling:org.apache.sling.api:2.20.0"),
+                          Collections.emptyList(),
+                          Collections.emptyList());
+        verifyFeatureFile(outputDirectory,
+                          "asd.retail.all-publish.json",
+                          "${project.groupId}:${project.artifactId}:slingosgifeature:asd.test.all-1.0.0-publish:${project.version}",
+                          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());
+
+        ZipFile zipFile = new ZipFile(new File(outputDirectory, "asd/sample/asd.retail.all/0.0.1/asd.retail.all-0.0.1-cp2fm-converted.zip"));
+        for (String expectedEntry : new String[] {
+                "jcr_root/content/asd/.content.xml",
+                "jcr_root/content/asd/resources.xml",
+                "jcr_root/apps/.content.xml",
+                "META-INF/vault/properties.xml",
+                "META-INF/vault/config.xml",
+                "META-INF/vault/settings.xml",
+                "META-INF/vault/filter.xml",
+                "META-INF/vault/definition/.content.xml",
+                "jcr_root/etc/packages/asd/test-bundles.zip",
+                "jcr_root/etc/packages/asd/test-configurations.zip",
+                "jcr_root/etc/packages/asd/test-content.zip",
+                }) {
+            assertNotNull(zipFile.getEntry(expectedEntry));
+        }
+        zipFile.close();
+    }
+
+}
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 e336785..8dc0fc7 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
@@ -19,10 +19,7 @@ package org.apache.sling.feature.cpconverter.handlers;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import java.io.File;
@@ -32,12 +29,9 @@ import java.util.Collection;
 
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
-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.features.DefaultFeaturesManager;
 import org.apache.sling.feature.cpconverter.features.FeaturesManager;
+import org.apache.sling.feature.cpconverter.shared.AbstractContentPackage2FeatureModelConverterTest;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -45,16 +39,29 @@ import org.junit.runners.Parameterized.Parameters;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
 @RunWith(Parameterized.class)
-public final class BundleEntryHandlerTest {
+public final class BundleEntryHandlerTest extends AbstractContentPackage2FeatureModelConverterTest {
 
     private final String bundleLocation;
 
-    private final EntryHandler bundleEntryHandler;
+    private final String runMode;
+
+    @Inject
+    private BundleEntryHandler bundleEntryHandler;
+
+    @Inject
+    private FeaturesManager featuresManager;
+
+    @Inject
+    @Named("features.artifacts.outdir")
+    private File testDirectory;
 
-    public BundleEntryHandlerTest(String bundleLocation, EntryHandler bundleEntryHandler) {
+    public BundleEntryHandlerTest(String bundleLocation, String runMode) {
         this.bundleLocation = bundleLocation;
-        this.bundleEntryHandler = bundleEntryHandler;
+        this.runMode = runMode;
     }
 
     @Test
@@ -87,37 +94,26 @@ public final class BundleEntryHandlerTest {
 
         });
 
-        Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null));
-        FeaturesManager featuresManager = spy(DefaultFeaturesManager.class);
-        when(featuresManager.getTargetFeature()).thenReturn(feature);
-        when(featuresManager.getRunMode(anyString())).thenReturn(feature);
-        doCallRealMethod().when(featuresManager).addArtifact(anyString(), anyString(), anyString(), anyString(), anyString(), anyString());
+        featuresManager.init("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1");
 
-        ContentPackage2FeatureModelConverter converter = spy(ContentPackage2FeatureModelConverter.class);
-
-        File testDirectory = new File(System.getProperty("java.io.tmpdir"), getClass().getName() + '_' + System.currentTimeMillis());
-        when(converter.getArtifactsDeployer()).thenReturn(new DefaultArtifactsDeployer(testDirectory));
-        when(converter.getFeaturesManager()).thenReturn(featuresManager);
-
-        bundleEntryHandler.handle(bundleLocation, archive, entry, converter);
+        bundleEntryHandler.handle(bundleLocation, archive, entry);
 
         assertTrue(new File(testDirectory, "org/apache/felix/org.apache.felix.framework/6.0.1/org.apache.felix.framework-6.0.1.pom").exists());
         assertTrue(new File(testDirectory, "org/apache/felix/org.apache.felix.framework/6.0.1/org.apache.felix.framework-6.0.1.jar").exists());
 
-        assertFalse(featuresManager.getTargetFeature().getBundles().isEmpty());
+        Feature feature = featuresManager.getRunMode(runMode);
+        assertFalse(feature.getBundles().isEmpty());
         assertEquals(1, feature.getBundles().size());
         assertEquals("org.apache.felix:org.apache.felix.framework:6.0.1", feature.getBundles().get(0).getId().toMvnId());
     }
 
     @Parameters
     public static Collection<Object[]> data() {
-        final BundleEntryHandler bundleEntryHandler = new BundleEntryHandler();
-
         return Arrays.asList(new Object[][] {
-            { "jcr_root/apps/asd/install/test-framework-no-pom.jar", bundleEntryHandler },
-            { "jcr_root/apps/asd/install/test-framework.jar", bundleEntryHandler },
-            { "jcr_root/apps/asd/install.author/test-framework.jar", bundleEntryHandler },
-            { "jcr_root/apps/asd/install.publish/test-framework.jar", bundleEntryHandler }
+            { "jcr_root/apps/asd/install/test-framework-no-pom.jar", null },
+            { "jcr_root/apps/asd/install/test-framework.jar", null },
+            { "jcr_root/apps/asd/install.author/test-framework.jar", "author" },
+            { "jcr_root/apps/asd/install.publish/test-framework.jar", "publish" }
         });
     }
 
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/ConfigEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/ConfigEntryHandlerTest.java
index 3dc5930..714d934 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/ConfigEntryHandlerTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/ConfigEntryHandlerTest.java
@@ -21,11 +21,7 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import java.io.StringReader;
@@ -34,17 +30,23 @@ import java.util.Dictionary;
 
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
-import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
-import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager;
 import org.apache.sling.feature.cpconverter.features.FeaturesManager;
+import org.apache.sling.feature.cpconverter.shared.AbstractContentPackage2FeatureModelConverterTest;
 import org.apache.sling.feature.io.json.FeatureJSONReader;
 import org.apache.sling.feature.io.json.FeatureJSONWriter;
 import org.junit.Test;
 
-public class ConfigEntryHandlerTest {
+import com.google.inject.Inject;
+
+public class ConfigEntryHandlerTest extends AbstractContentPackage2FeatureModelConverterTest {
+
+    @Inject
+    private FeaturesManager featuresManager;
+
+    @Inject
+    private ConfigurationEntryHandler handler;
 
     @Test
     public void collectionValuesIncluded() throws Exception {
@@ -56,15 +58,11 @@ public class ConfigEntryHandlerTest {
         when(entry.getName()).thenReturn(resourceConfiguration.substring(resourceConfiguration.lastIndexOf('/') + 1));
         when(archive.openInputStream(entry)).thenReturn(getClass().getResourceAsStream(resourceConfiguration));
 
-        Feature expected = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null));
-        FeaturesManager featuresManager = spy(DefaultFeaturesManager.class);
-        when(featuresManager.getTargetFeature()).thenReturn(expected);
-        doCallRealMethod().when(featuresManager).addConfiguration(anyString(), anyString(), any());
+        featuresManager.init("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1");
 
-        ContentPackage2FeatureModelConverter converter = mock(ContentPackage2FeatureModelConverter.class);
-        when(converter.getFeaturesManager()).thenReturn(featuresManager);
+        handler.handle(resourceConfiguration, archive, entry);
 
-        new ConfigurationEntryHandler().handle(resourceConfiguration, archive, entry, converter);
+        Feature expected = featuresManager.getTargetFeature();
 
         verifyConfiguration(expected);
 
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/ConfigurationEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/ConfigurationEntryHandlerTest.java
index b2a6ee6..86e182a 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/ConfigurationEntryHandlerTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/ConfigurationEntryHandlerTest.java
@@ -19,11 +19,7 @@ package org.apache.sling.feature.cpconverter.handlers;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import java.util.Arrays;
@@ -31,33 +27,43 @@ import java.util.Collection;
 
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
-import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Configurations;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
-import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager;
 import org.apache.sling.feature.cpconverter.features.FeaturesManager;
+import org.apache.sling.feature.cpconverter.shared.AbstractContentPackage2FeatureModelConverterTest;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+
 @RunWith(Parameterized.class)
-public class ConfigurationEntryHandlerTest {
+public class ConfigurationEntryHandlerTest extends AbstractContentPackage2FeatureModelConverterTest {
 
     private static final String EXPECTED_PID = "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl";
 
     private final String resourceConfiguration;
 
+    private final String runMode;
+
     private final int expectedConfigurationsSize;
 
     private final AbstractConfigurationEntryHandler configurationEntryHandler;
 
+    @Inject
+    private FeaturesManager featuresManager;
+
+    @Inject
+    private Injector injector;
+
     public ConfigurationEntryHandlerTest(String resourceConfiguration,
+                                         String runMode,
                                          int expectedConfigurationsSize,
                                          AbstractConfigurationEntryHandler configurationEntryHandler) {
         this.resourceConfiguration = resourceConfiguration;
+        this.runMode = runMode;
         this.expectedConfigurationsSize = expectedConfigurationsSize;
         this.configurationEntryHandler = configurationEntryHandler;
     }
@@ -80,17 +86,12 @@ public class ConfigurationEntryHandlerTest {
         when(entry.getName()).thenReturn(resourceConfiguration.substring(resourceConfiguration.lastIndexOf('/') + 1));
         when(archive.openInputStream(entry)).thenReturn(getClass().getResourceAsStream(resourceConfiguration));
 
-        Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null));
-        FeaturesManager featuresManager = spy(DefaultFeaturesManager.class);
-        when(featuresManager.getTargetFeature()).thenReturn(feature);
-        doCallRealMethod().when(featuresManager).addConfiguration(anyString(), anyString(), any());
-        when(featuresManager.getRunMode(anyString())).thenReturn(feature);
-        ContentPackage2FeatureModelConverter converter = mock(ContentPackage2FeatureModelConverter.class);
-        when(converter.getFeaturesManager()).thenReturn(featuresManager);
+        featuresManager.init("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1");
 
-        configurationEntryHandler.handle(resourceConfiguration, archive, entry, converter);
+        injector.injectMembers(configurationEntryHandler);
+        configurationEntryHandler.handle(resourceConfiguration, archive, entry);
 
-        Configurations configurations = featuresManager.getTargetFeature().getConfigurations();
+        Configurations configurations = featuresManager.getRunMode(runMode).getConfigurations();
 
         assertEquals(expectedConfigurationsSize, configurations.size());
 
@@ -107,24 +108,24 @@ public class ConfigurationEntryHandlerTest {
         String path = "jcr_root/apps/asd/config/";
 
         return Arrays.asList(new Object[][] {
-            { path + EXPECTED_PID + ".empty.cfg", 0, new PropertiesConfigurationEntryHandler() },
-            { path + EXPECTED_PID + ".cfg", 1, new PropertiesConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".empty.cfg", null, 0, new PropertiesConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".cfg", null, 1, new PropertiesConfigurationEntryHandler() },
 
-            { path + EXPECTED_PID + ".empty.cfg.json", 0, new JsonConfigurationEntryHandler() },
-            { path + EXPECTED_PID + ".cfg.json", 1, new JsonConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".empty.cfg.json", null, 0, new JsonConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".cfg.json", null, 1, new JsonConfigurationEntryHandler() },
 
-            { path + EXPECTED_PID + ".empty.config", 0, new ConfigurationEntryHandler() },
-            { path + EXPECTED_PID + ".config", 1, new ConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".empty.config", null, 0, new ConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".config", null, 1, new ConfigurationEntryHandler() },
 
-            { path + EXPECTED_PID + ".empty.xml", 0, new XmlConfigurationEntryHandler() },
-            { path + EXPECTED_PID + ".xml", 1, new XmlConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".empty.xml", null, 0, new XmlConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".xml", null, 1, new XmlConfigurationEntryHandler() },
 
-            { path + EXPECTED_PID + ".empty.xml.cfg", 0, new PropertiesConfigurationEntryHandler() },
-            { path + EXPECTED_PID + ".xml.cfg", 1, new PropertiesConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".empty.xml.cfg", null, 0, new PropertiesConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".xml.cfg", null, 1, new PropertiesConfigurationEntryHandler() },
 
             // runmode aware folders
-            { "jcr_root/apps/asd/config.author/" + EXPECTED_PID + ".config", 1, new ConfigurationEntryHandler() },
-            { "jcr_root/apps/asd/config.publish/" + EXPECTED_PID + ".config", 1, new ConfigurationEntryHandler() },
+            { "jcr_root/apps/asd/config.author/" + EXPECTED_PID + ".config", "author", 1, new ConfigurationEntryHandler() },
+            { "jcr_root/apps/asd/config.publish/" + EXPECTED_PID + ".config", "publish", 1, new ConfigurationEntryHandler() },
         });
     }
 
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandlerTest.java
index 526f309..9c810aa 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandlerTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandlerTest.java
@@ -19,6 +19,8 @@ package org.apache.sling.feature.cpconverter.handlers;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import java.io.File;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -29,7 +31,7 @@ public final class ContentPackageEntryHandlerTest {
 
     @Before
     public void setUp() {
-        contentPackageEntryhandler = new ContentPackageEntryHandler();
+        contentPackageEntryhandler = new ContentPackageEntryHandler(new File(System.getProperty("java.io.tmpdir")));
     }
 
     @After
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/JsonConfigurationEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/JsonConfigurationEntryHandlerTest.java
index 8289cd5..c07e0e6 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/JsonConfigurationEntryHandlerTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/JsonConfigurationEntryHandlerTest.java
@@ -23,7 +23,6 @@ import java.io.IOException;
 
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
-import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
 import org.junit.Test;
 
 public class JsonConfigurationEntryHandlerTest {
@@ -38,9 +37,7 @@ public class JsonConfigurationEntryHandlerTest {
         when(entry.getName()).thenReturn(resourceConfiguration.substring(resourceConfiguration.lastIndexOf('/') + 1));
         when(archive.openInputStream(entry)).thenReturn(getClass().getResourceAsStream(resourceConfiguration));
 
-        ContentPackage2FeatureModelConverter converter = mock(ContentPackage2FeatureModelConverter.class);
-
-        new JsonConfigurationEntryHandler().handle(resourceConfiguration, archive, entry, converter);
+        new JsonConfigurationEntryHandler().handle(resourceConfiguration, archive, entry);
     }
 
 }
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandlerTest.java
index 1d947f4..9c12c3a 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandlerTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandlerTest.java
@@ -16,46 +16,40 @@
  */
 package org.apache.sling.feature.cpconverter.handlers;
 
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import java.io.File;
 
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
-import org.apache.sling.feature.ArtifactId;
 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;
-import org.apache.sling.feature.cpconverter.acl.DefaultAclManager;
-import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager;
+import org.apache.sling.feature.cpconverter.acl.AclManager;
 import org.apache.sling.feature.cpconverter.features.FeaturesManager;
+import org.apache.sling.feature.cpconverter.shared.AbstractContentPackage2FeatureModelConverterTest;
 import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 
-public final class RepPolicyEntryHandlerTest {
+import com.google.inject.Inject;
+
+public final class RepPolicyEntryHandlerTest extends AbstractContentPackage2FeatureModelConverterTest {
 
+    @Inject
     private RepPolicyEntryHandler handler;
 
-    @Before
-    public void setUp() {
-        handler = new RepPolicyEntryHandler();
-    }
+    @Inject
+    private FeaturesManager featuresManager;
 
-    @After
-    public void tearDown() {
-        handler = null;
-    }
+    @Inject
+    private AclManager aclManager;
 
     @Test
     public void doesNotMatch() {
@@ -154,24 +148,21 @@ public final class RepPolicyEntryHandlerTest {
 
         when(archive.openInputStream(entry)).thenReturn(getClass().getResourceAsStream(path));
 
-        Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null));
-        FeaturesManager featuresManager = spy(DefaultFeaturesManager.class);
-        when(featuresManager.getTargetFeature()).thenReturn(feature);
-        ContentPackage2FeatureModelConverter converter = spy(ContentPackage2FeatureModelConverter.class);
-        when(converter.getFeaturesManager()).thenReturn(featuresManager);
-        when(converter.getAclManager()).thenReturn(new DefaultAclManager());
+        featuresManager.init("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1");
+
+        handler.handle(path, archive, entry);
 
-        handler.handle(path, archive, entry, converter);
+        Feature feature = featuresManager.getTargetFeature();
 
         if (systemUsers != null) {
             for (String systemUser : systemUsers) {
-                converter.getAclManager().addSystemUser(systemUser);
+                aclManager.addSystemUser(systemUser);
             }
         }
 
         when(packageAssembler.getEntry(anyString())).thenReturn(new File("itdoesnotexist"));
 
-        converter.getAclManager().addRepoinitExtension(packageAssembler, feature);
+        aclManager.addRepoinitExtension(packageAssembler, feature);
         return feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT);
     }
 
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/SystemUsersEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/SystemUsersEntryHandlerTest.java
index a427c2b..64c4f83 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/SystemUsersEntryHandlerTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/SystemUsersEntryHandlerTest.java
@@ -22,38 +22,34 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.io.File;
 
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
-import org.apache.sling.feature.ArtifactId;
 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;
-import org.apache.sling.feature.cpconverter.acl.DefaultAclManager;
-import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager;
+import org.apache.sling.feature.cpconverter.acl.AclManager;
 import org.apache.sling.feature.cpconverter.features.FeaturesManager;
+import org.apache.sling.feature.cpconverter.shared.AbstractContentPackage2FeatureModelConverterTest;
 import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 
-public class SystemUsersEntryHandlerTest {
+import com.google.inject.Inject;
 
+public class SystemUsersEntryHandlerTest extends AbstractContentPackage2FeatureModelConverterTest {
+
+    @Inject
     private SystemUsersEntryHandler systemUsersEntryHandler;
 
-    @Before
-    public void setUp() {
-        systemUsersEntryHandler = new SystemUsersEntryHandler();
-    }
+    @Inject
+    private FeaturesManager featuresManager;
 
-    @After
-    public void tearDown() {
-        systemUsersEntryHandler = null;
-    }
+    @Inject
+    private AclManager aclManager;
 
     @Test
     public void doesNotMatch() {
@@ -91,18 +87,14 @@ public class SystemUsersEntryHandlerTest {
 
         when(archive.openInputStream(entry)).thenReturn(getClass().getResourceAsStream(path));
 
-        Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null));
-        FeaturesManager featuresManager = spy(DefaultFeaturesManager.class);
-        when(featuresManager.getTargetFeature()).thenReturn(feature);
-        ContentPackage2FeatureModelConverter converter = spy(ContentPackage2FeatureModelConverter.class);
-        when(converter.getFeaturesManager()).thenReturn(featuresManager);
-        when(converter.getAclManager()).thenReturn(new DefaultAclManager());
+        featuresManager.init("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1");
 
-        systemUsersEntryHandler.handle(path, archive, entry, converter);
+        systemUsersEntryHandler.handle(path, archive, entry);
 
         when(packageAssembler.getEntry(anyString())).thenReturn(new File("itdoesnotexist"));
 
-        converter.getAclManager().addRepoinitExtension(packageAssembler, feature);
+        Feature feature = featuresManager.getTargetFeature();
+        aclManager.addRepoinitExtension(packageAssembler, feature);
         return feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT);
     }
 
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/shared/AbstractContentPackage2FeatureModelConverterTest.java b/src/test/java/org/apache/sling/feature/cpconverter/shared/AbstractContentPackage2FeatureModelConverterTest.java
new file mode 100644
index 0000000..b5467b5
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/cpconverter/shared/AbstractContentPackage2FeatureModelConverterTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.shared;
+
+import static com.google.inject.Guice.createInjector;
+import static com.google.inject.name.Names.named;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.Reader;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.cpconverter.inject.ContentPackage2FeatureModelConverterModule;
+import org.apache.sling.feature.io.json.FeatureJSONReader;
+import org.junit.Before;
+
+import com.google.inject.AbstractModule;
+
+public abstract class AbstractContentPackage2FeatureModelConverterTest extends AbstractModule {
+
+    @Before
+    public void setUp() {
+        createInjector(this).injectMembers(this);
+    }
+
+    @Override
+    protected void configure() {
+        install(new ContentPackage2FeatureModelConverterModule());
+
+        bindConstant().annotatedWith(named("packagemanager.validation.strict")).to(true);
+        bindConstant().annotatedWith(named("features.configurations.merge")).to(false);
+        bindConstant().annotatedWith(named("features.bundles.startOrder")).to(5);
+
+        File outdir = new File(System.getProperty("java.io.tmpdir"), getClass().getName() + '_' + System.currentTimeMillis());
+        bind(File.class).annotatedWith(named("features.outdir")).toInstance(outdir);
+        bind(File.class).annotatedWith(named("features.artifacts.outdir")).toInstance(outdir);
+    }
+
+    protected final void verifyFeatureFile(File outputDirectory,
+                                           String name,
+                                           String expectedArtifactId,
+                                           List<String> expectedArtifacts,
+                                           List<String> expectedConfigurations,
+                                           List<String> expectedContentPackagesExtensions) throws Exception {
+        File featureFile = new File(outputDirectory, name);
+        assertTrue(featureFile + " was not correctly created", featureFile.exists());
+
+        try (Reader reader = new FileReader(featureFile)) {
+            Feature feature = FeatureJSONReader.read(reader, featureFile.getAbsolutePath());
+
+            assertEquals(expectedArtifactId, feature.getId().toMvnId());
+
+            for (String expectedArtifact : expectedArtifacts) {
+                assertTrue(expectedArtifact + " not found in Feature " + expectedArtifactId, feature.getBundles().containsExact(ArtifactId.fromMvnId(expectedArtifact)));
+                verifyInstalledArtifact(outputDirectory, expectedArtifact);
+            }
+
+            for (String expectedConfiguration : expectedConfigurations) {
+                assertNotNull(expectedConfiguration + " not found in Feature " + expectedArtifactId, feature.getConfigurations().getConfiguration(expectedConfiguration));
+            }
+
+            for (String expectedContentPackagesExtension : expectedContentPackagesExtensions) {
+                assertTrue(expectedContentPackagesExtension + " not found in Feature " + expectedArtifactId,
+                           feature.getExtensions().getByName("content-packages").getArtifacts().containsExact(ArtifactId.fromMvnId(expectedContentPackagesExtension)));
+                verifyInstalledArtifact(outputDirectory, expectedContentPackagesExtension);
+            }
+        }
+    }
+
+    private void verifyInstalledArtifact(File outputDirectory, String coordinates) {
+        ArtifactId bundleId = ArtifactId.fromMvnId(coordinates);
+
+        StringTokenizer tokenizer = new StringTokenizer(bundleId.getGroupId(), ".");
+        while (tokenizer.hasMoreTokens()) {
+            outputDirectory = new File(outputDirectory, tokenizer.nextToken());
+        }
+
+        outputDirectory = new File(outputDirectory, bundleId.getArtifactId());
+        outputDirectory = new File(outputDirectory, bundleId.getVersion());
+
+        StringBuilder bundleFileName = new StringBuilder()
+                                       .append(bundleId.getArtifactId())
+                                       .append('-')
+                                       .append(bundleId.getVersion());
+        if (bundleId.getClassifier() != null) {
+            bundleFileName.append('-').append(bundleId.getClassifier());
+        }
+        bundleFileName.append('.').append(bundleId.getType());
+
+        File bundleFile = new File(outputDirectory, bundleFileName.toString());
+        assertTrue("Bundle " + bundleFile + " does not exist", bundleFile.exists());
+
+        File pomFile = new File(outputDirectory, String.format("%s-%s.pom", bundleId.getArtifactId(), bundleId.getVersion()));
+        assertTrue("POM file " + pomFile + " does not exist", pomFile.exists());
+    }
+
+}