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

[sling-org-apache-sling-feature-cpconverter] branch guice_di created (now 6e197d2)

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

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


      at 6e197d2  initial checkin of experimental branch where spaghetti code is replaced by DI with Google Guice

This branch includes the following new commits:

     new 6e197d2  initial checkin of experimental branch where spaghetti code is replaced by DI with Google Guice

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



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

Posted by si...@apache.org.
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());
+    }
+
+}