karaf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cschnei...@apache.org
Subject [2/2] karaf git commit: Extract InstallSupport and FeaturesConfig
Date Tue, 16 May 2017 09:53:54 GMT
Extract InstallSupport and FeaturesConfig


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/bb0e9b77
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/bb0e9b77
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/bb0e9b77

Branch: refs/heads/master
Commit: bb0e9b77eaffdd047348e29c345f10883e46ca98
Parents: af9b835
Author: Christian Schneider <chris@die-schneider.net>
Authored: Tue May 16 11:41:34 2017 +0200
Committer: Christian Schneider <chris@die-schneider.net>
Committed: Tue May 16 11:53:41 2017 +0200

----------------------------------------------------------------------
 .../karaf/features/internal/osgi/Activator.java |  45 +--
 .../internal/service/BundleInstallSupport.java  |  67 ++++
 .../service/BundleInstallSupportImpl.java       | 307 ++++++++++++++++++
 .../features/internal/service/Deployer.java     |  56 ++--
 .../internal/service/FeaturesServiceConfig.java |  57 ++++
 .../internal/service/FeaturesServiceImpl.java   | 311 ++-----------------
 .../karaf/features/FeaturesServiceTest.java     |  16 +-
 .../features/internal/service/DeployerTest.java | 169 +++++-----
 .../service/FeaturesServiceImplTest.java        |  15 +-
 .../assembly/AssemblyDeployCallback.java        |  37 ++-
 .../apache/karaf/profile/assembly/Builder.java  |   2 +-
 .../org/apache/karaf/tooling/VerifyMojo.java    |  21 +-
 12 files changed, 646 insertions(+), 457 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/bb0e9b77/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java b/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
index 8bd784e..da5eeb3 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
@@ -42,8 +42,12 @@ import org.apache.karaf.features.internal.repository.XmlRepository;
 import org.apache.karaf.features.internal.resolver.Slf4jResolverLog;
 import org.apache.karaf.features.internal.service.BootFeaturesInstaller;
 import org.apache.karaf.features.internal.service.EventAdminListener;
+import org.apache.karaf.features.internal.service.FeatureConfigInstaller;
 import org.apache.karaf.features.internal.service.FeatureFinder;
+import org.apache.karaf.features.internal.service.FeaturesServiceConfig;
 import org.apache.karaf.features.internal.service.FeaturesServiceImpl;
+import org.apache.karaf.features.internal.service.BundleInstallSupport;
+import org.apache.karaf.features.internal.service.BundleInstallSupportImpl;
 import org.apache.karaf.features.internal.service.StateStorage;
 import org.apache.karaf.util.tracker.BaseActivator;
 import org.apache.karaf.util.tracker.annotation.ProvideService;
@@ -53,6 +57,7 @@ import org.eclipse.equinox.internal.region.CollisionHookHelper;
 import org.eclipse.equinox.internal.region.StandardRegionDigraph;
 import org.eclipse.equinox.internal.region.management.StandardManageableRegionDigraph;
 import org.eclipse.equinox.region.RegionDigraph;
+import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.hooks.bundle.CollisionHook;
@@ -174,15 +179,16 @@ public class Activator extends BaseActivator {
             break;
         }
 
-        String overrides = getString("overrides", new File(System.getProperty("karaf.etc"), "overrides.properties").toURI().toString());
-        String featureResolutionRange = getString("featureResolutionRange", FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE);
-        String bundleUpdateRange = getString("bundleUpdateRange", FeaturesService.DEFAULT_BUNDLE_UPDATE_RANGE);
-        String updateSnapshots = getString("updateSnapshots", FeaturesService.DEFAULT_UPDATE_SNAPSHOTS);
-        int downloadThreads = getInt("downloadThreads", FeaturesService.DEFAULT_DOWNLOAD_THREADS);
-        long scheduleDelay = getLong("scheduleDelay", FeaturesService.DEFAULT_SCHEDULE_DELAY);
-        int scheduleMaxRun = getInt("scheduleMaxRun", FeaturesService.DEFAULT_SCHEDULE_MAX_RUN);
-        String blacklisted = getString("blacklisted", new File(System.getProperty("karaf.etc"), "blacklisted.properties").toURI().toString());
-        String serviceRequirements = getString("serviceRequirements", FeaturesService.SERVICE_REQUIREMENTS_DEFAULT);
+        FeaturesServiceConfig cfg = new FeaturesServiceConfig();
+        cfg.overrides = getString("overrides", new File(System.getProperty("karaf.etc"), "overrides.properties").toURI().toString());
+        cfg.featureResolutionRange = getString("featureResolutionRange", FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE);
+        cfg.bundleUpdateRange = getString("bundleUpdateRange", FeaturesService.DEFAULT_BUNDLE_UPDATE_RANGE);
+        cfg.updateSnapshots = getString("updateSnapshots", FeaturesService.DEFAULT_UPDATE_SNAPSHOTS);
+        cfg.downloadThreads = getInt("downloadThreads", FeaturesService.DEFAULT_DOWNLOAD_THREADS);
+        cfg.scheduleDelay = getLong("scheduleDelay", FeaturesService.DEFAULT_SCHEDULE_DELAY);
+        cfg.scheduleMaxRun = getInt("scheduleMaxRun", FeaturesService.DEFAULT_SCHEDULE_MAX_RUN);
+        cfg.blacklisted = getString("blacklisted", new File(System.getProperty("karaf.etc"), "blacklisted.properties").toURI().toString());
+        cfg.serviceRequirements = getString("serviceRequirements", FeaturesService.SERVICE_REQUIREMENTS_DEFAULT);
         boolean configCfgStore = getBoolean("configCfgStore", FeaturesService.DEFAULT_CONFIG_CFG_STORE);
         StateStorage stateStorage = new StateStorage() {
             @Override
@@ -201,26 +207,23 @@ public class Activator extends BaseActivator {
                 return new FileOutputStream(file);
             }
         };
+        BundleContext systemBundleContext = bundleContext.getBundle(0).getBundleContext();
+        FeatureConfigInstaller configInstaller = configurationAdmin != null ? new FeatureConfigInstaller(configurationAdmin, configCfgStore) : null;
+        BundleInstallSupport installSupport = new BundleInstallSupportImpl(bundleContext.getBundle(),
+                    systemBundleContext,
+                    configInstaller,
+                    dg);
         featuresService = new FeaturesServiceImpl(
                 bundleContext.getBundle(),
                 bundleContext,
-                bundleContext.getBundle(0).getBundleContext(),
+                systemBundleContext,
                 stateStorage,
                 featureFinder,
                 configurationAdmin,
                 resolver,
-                dg,
-                overrides,
-                featureResolutionRange,
-                bundleUpdateRange,
-                updateSnapshots,
-                serviceRequirements,
+                installSupport,
                 globalRepository,
-                downloadThreads,
-                scheduleDelay,
-                scheduleMaxRun,
-                blacklisted,
-                configCfgStore);
+                cfg);
         
         try {
             EventAdminListener eventAdminListener = new EventAdminListener(bundleContext);

http://git-wip-us.apache.org/repos/asf/karaf/blob/bb0e9b77/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupport.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupport.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupport.java
new file mode 100644
index 0000000..66bbc17
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupport.java
@@ -0,0 +1,67 @@
+/*
+ * 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.karaf.features.internal.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.karaf.features.Feature;
+import org.eclipse.equinox.region.RegionDigraph;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
+
+public interface BundleInstallSupport {
+
+    void print(String message, boolean verbose);
+
+    void refreshPackages(Collection<Bundle> bundles) throws InterruptedException;
+
+    Bundle installBundle(String region, String uri, InputStream is) throws BundleException;
+
+    void updateBundle(Bundle bundle, String uri, InputStream is) throws BundleException;
+
+    void uninstall(Bundle bundle) throws BundleException;
+
+    void startBundle(Bundle bundle) throws BundleException;
+
+    void stopBundle(Bundle bundle, int options) throws BundleException;
+
+    void setBundleStartLevel(Bundle bundle, int startLevel);
+
+    void resolveBundles(Set<Bundle> bundles, Map<Resource, List<Wire>> wiring,
+                        Map<Resource, Bundle> resToBnd);
+
+    void replaceDigraph(Map<String, Map<String, Map<String, Set<String>>>> policies,
+                        Map<String, Set<Long>> bundles)
+        throws BundleException, InvalidSyntaxException;
+
+    void saveState() throws IOException;
+
+    RegionDigraph getDiGraphCopy() throws BundleException;
+    
+    void installConfigs(Feature feature) throws IOException, InvalidSyntaxException;
+    
+    void installLibraries(Feature feature) throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/bb0e9b77/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupportImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupportImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupportImpl.java
new file mode 100644
index 0000000..5de6166
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupportImpl.java
@@ -0,0 +1,307 @@
+/*
+ * 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.karaf.features.internal.service;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.FeaturesService;
+import org.apache.karaf.features.internal.region.DigraphHelper;
+import org.apache.karaf.util.bundles.BundleUtils;
+import org.eclipse.equinox.region.Region;
+import org.eclipse.equinox.region.RegionDigraph;
+import org.eclipse.equinox.region.RegionFilterBuilder;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.hooks.resolver.ResolverHook;
+import org.osgi.framework.hooks.resolver.ResolverHookFactory;
+import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
+import org.osgi.framework.namespace.HostNamespace;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.FrameworkWiring;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BundleInstallSupportImpl implements BundleInstallSupport {
+    private static final Logger LOGGER = LoggerFactory.getLogger(BundleInstallSupportImpl.class);
+    
+    private final RegionDigraph digraph;
+    private final Bundle ourBundle;
+    private final FeatureConfigInstaller configInstaller;
+    
+    /**
+     * The system bundle context.
+     * For all bundles related operations, we use the system bundle context
+     * to allow this bundle to be stopped and still allow the deployment to
+     * take place.
+     */
+    private final BundleContext systemBundleContext;
+    
+    public BundleInstallSupportImpl(Bundle ourBundle,
+                   BundleContext systemBundleContext,
+                   FeatureConfigInstaller configInstaller,
+                   RegionDigraph digraph) {
+        this.ourBundle = ourBundle;
+        this.systemBundleContext = systemBundleContext;
+        this.configInstaller = configInstaller;
+        this.digraph = digraph;
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.karaf.features.internal.service.Regions#print(java.lang.String, boolean)
+     */
+    @Override
+    public void print(String message, boolean verbose) {
+        LOGGER.info(message);
+        if (verbose) {
+            System.out.println(message);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.karaf.features.internal.service.Regions#refreshPackages(java.util.Collection)
+     */
+    @Override
+    public void refreshPackages(Collection<Bundle> bundles) throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        FrameworkWiring fw = systemBundleContext.getBundle().adapt(FrameworkWiring.class);
+        fw.refreshBundles(bundles, (FrameworkListener) event -> {
+            if (event.getType() == FrameworkEvent.ERROR) {
+                LOGGER.error("Framework error", event.getThrowable());
+            }
+            latch.countDown();
+        });
+        latch.await();
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.karaf.features.internal.service.Regions#installBundle(java.lang.String, java.lang.String, java.io.InputStream)
+     */
+    @Override
+    public Bundle installBundle(String region, String uri, InputStream is) throws BundleException {
+        if (FeaturesService.ROOT_REGION.equals(region)) {
+            return digraph.getRegion(region).installBundleAtLocation(uri, is);
+        } else {
+            return digraph.getRegion(region).installBundle(uri, is);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.karaf.features.internal.service.Regions#updateBundle(org.osgi.framework.Bundle, java.lang.String, java.io.InputStream)
+     */
+    @Override
+    public void updateBundle(Bundle bundle, String uri, InputStream is) throws BundleException {
+        // We need to wrap the bundle to insert a Bundle-UpdateLocation header
+        try {
+            File file = BundleUtils.fixBundleWithUpdateLocation(is, uri);
+            bundle.update(new FileInputStream(file));
+            file.delete();
+        } catch (IOException e) {
+            throw new BundleException("Unable to update bundle", e);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.karaf.features.internal.service.Regions#uninstall(org.osgi.framework.Bundle)
+     */
+    @Override
+    public void uninstall(Bundle bundle) throws BundleException {
+        bundle.uninstall();
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.karaf.features.internal.service.Regions#startBundle(org.osgi.framework.Bundle)
+     */
+    @Override
+    public void startBundle(Bundle bundle) throws BundleException {
+        if (bundle != this.ourBundle || bundle.getState() != Bundle.STARTING) {
+            bundle.start();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.karaf.features.internal.service.Regions#stopBundle(org.osgi.framework.Bundle, int)
+     */
+    @Override
+    public void stopBundle(Bundle bundle, int options) throws BundleException {
+        bundle.stop(options);
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.karaf.features.internal.service.Regions#setBundleStartLevel(org.osgi.framework.Bundle, int)
+     */
+    @Override
+    public void setBundleStartLevel(Bundle bundle, int startLevel) {
+        bundle.adapt(BundleStartLevel.class).setStartLevel(startLevel);
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.karaf.features.internal.service.Regions#resolveBundles(java.util.Set, java.util.Map, java.util.Map)
+     */
+    @Override
+    public void resolveBundles(Set<Bundle> bundles, final Map<Resource, List<Wire>> wiring, Map<Resource, Bundle> resToBnd) {
+        // Make sure it's only used for us
+        final Thread thread = Thread.currentThread();
+        // Translate wiring
+        final Map<Bundle, Resource> bndToRes = new HashMap<>();
+        for (Resource res : resToBnd.keySet()) {
+            bndToRes.put(resToBnd.get(res), res);
+        }
+        // Hook
+        final ResolverHook hook = new ResolverHook() {
+            @Override
+            public void filterResolvable(Collection<BundleRevision> candidates) {
+            }
+            @Override
+            public void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates) {
+            }
+            @Override
+            public void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates) {
+                if (Thread.currentThread() == thread) {
+                    // osgi.ee capabilities are provided by the system bundle, so just ignore those
+                    if (ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE
+                            .equals(requirement.getNamespace())) {
+                        return;
+                    }
+                    Bundle sourceBundle = requirement.getRevision().getBundle();
+                    Resource sourceResource = bndToRes.get(sourceBundle);
+                    Set<Resource> wired = new HashSet<>();
+                    // Get a list of allowed wired resources
+                    wired.add(sourceResource);
+                    for (Wire wire : wiring.get(sourceResource)) {
+                        wired.add(wire.getProvider());
+                        if (HostNamespace.HOST_NAMESPACE.equals(wire.getRequirement().getNamespace())) {
+                            for (Wire hostWire : wiring.get(wire.getProvider())) {
+                                wired.add(hostWire.getProvider());
+                            }
+                        }
+                    }
+                    // Remove candidates that are not allowed
+                    for (Iterator<BundleCapability> candIter = candidates.iterator(); candIter.hasNext(); ) {
+                        BundleCapability cand = candIter.next();
+                        BundleRevision br = cand.getRevision();
+                        if ((br.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
+                            br = br.getWiring().getRequiredWires(null).get(0).getProvider();
+                        }
+                        Resource res = bndToRes.get(br.getBundle());
+                        if (!wired.contains(br) && !wired.contains(res)) {
+                            candIter.remove();
+                        }
+                    }
+                }
+            }
+            @Override
+            public void end() {
+            }
+        };
+        ResolverHookFactory factory = triggers -> hook;
+        ServiceRegistration<ResolverHookFactory> registration = systemBundleContext.registerService(ResolverHookFactory.class, factory, null);
+        try {
+            FrameworkWiring frameworkWiring = systemBundleContext.getBundle().adapt(FrameworkWiring.class);
+            frameworkWiring.resolveBundles(bundles);
+        } finally {
+            registration.unregister();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.karaf.features.internal.service.Regions#replaceDigraph(java.util.Map, java.util.Map)
+     */
+    @Override
+    public void replaceDigraph(Map<String, Map<String, Map<String, Set<String>>>> policies, Map<String, Set<Long>> bundles) throws BundleException, InvalidSyntaxException {
+        RegionDigraph temp = digraph.copy();
+        // Remove everything
+        for (Region region : temp.getRegions()) {
+            temp.removeRegion(region);
+        }
+        // Re-create regions
+        for (String name : policies.keySet()) {
+            temp.createRegion(name);
+        }
+        // Dispatch bundles
+        for (Map.Entry<String, Set<Long>> entry : bundles.entrySet()) {
+            Region region = temp.getRegion(entry.getKey());
+            for (long bundleId : entry.getValue()) {
+                region.addBundle(bundleId);
+            }
+        }
+        // Add policies
+        for (Map.Entry<String, Map<String, Map<String, Set<String>>>> entry1 : policies.entrySet()) {
+            Region region1 = temp.getRegion(entry1.getKey());
+            for (Map.Entry<String, Map<String, Set<String>>> entry2 : entry1.getValue().entrySet()) {
+                Region region2 = temp.getRegion(entry2.getKey());
+                RegionFilterBuilder rfb = temp.createRegionFilterBuilder();
+                for (Map.Entry<String, Set<String>> entry3 : entry2.getValue().entrySet()) {
+                    for (String flt : entry3.getValue()) {
+                        rfb.allow(entry3.getKey(), flt);
+                    }
+                }
+                region1.connectRegion(region2, rfb.build());
+            }
+        }
+        digraph.replace(temp);
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.karaf.features.internal.service.Regions#saveState()
+     */
+    @Override
+    public void saveState() throws IOException {
+        DigraphHelper.saveDigraph(ourBundle.getBundleContext(), digraph);
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.karaf.features.internal.service.Regions#getGraph()
+     */
+    @Override
+    public RegionDigraph getDiGraphCopy() throws BundleException {
+        return digraph.copy();
+    }
+
+    @Override
+    public void installConfigs(Feature feature) throws IOException, InvalidSyntaxException {
+        if (configInstaller != null) {
+            configInstaller.installFeatureConfigs(feature);
+        }
+    }
+
+    @Override
+    public void installLibraries(Feature feature) {
+        // TODO: install libraries
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/bb0e9b77/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
index 7fb066b..2bc514a 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
@@ -61,7 +61,6 @@ import org.eclipse.equinox.region.RegionDigraph;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
-import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.Version;
 import org.osgi.framework.namespace.BundleNamespace;
@@ -110,26 +109,11 @@ import static org.osgi.framework.namespace.IdentityNamespace.TYPE_BUNDLE;
 public class Deployer {
 
     public interface DeployCallback {
-
         void print(String message, boolean verbose);
-
         void saveState(State state);
         void persistResolveRequest(DeploymentRequest request) throws IOException;
-        void installFeature(Feature feature) throws IOException, InvalidSyntaxException;
         void callListeners(DeploymentEvent deployEvent);
         void callListeners(FeatureEvent featureEvent);
-
-        Bundle installBundle(String region, String uri, InputStream is) throws BundleException;
-        void updateBundle(Bundle bundle, String uri, InputStream is) throws BundleException;
-        void uninstall(Bundle bundle) throws BundleException;
-        void startBundle(Bundle bundle) throws BundleException;
-        void stopBundle(Bundle bundle, int options) throws BundleException;
-        void setBundleStartLevel(Bundle bundle, int startLevel);
-
-        void refreshPackages(Collection<Bundle> bundles) throws InterruptedException;
-        void resolveBundles(Set<Bundle> bundles, Map<Resource, List<Wire>> wiring, Map<Resource, Bundle> resToBnd);
-
-        void replaceDigraph(Map<String, Map<String,Map<String,Set<String>>>> policies, Map<String, Set<Long>> bundles) throws BundleException, InvalidSyntaxException;
     }
 
     public static class CircularPrerequisiteException extends Exception {
@@ -198,11 +182,13 @@ public class Deployer {
 
     private final DownloadManager manager;
     private final Resolver resolver;
+    private final BundleInstallSupport installSupport;
     private final DeployCallback callback;
 
-    public Deployer(DownloadManager manager, Resolver resolver, DeployCallback callback) {
+    public Deployer(DownloadManager manager, Resolver resolver, BundleInstallSupport installSupport, DeployCallback callback) {
         this.manager = manager;
         this.resolver = resolver;
+        this.installSupport = installSupport;
         this.callback = callback;
     }
 
@@ -613,14 +599,14 @@ public class Deployer {
                     dstate.bundles.values(),
                     Collections.<Resource, Bundle>emptyMap(),
                     Collections.<Resource, List<Wire>>emptyMap());
-            callback.stopBundle(dstate.serviceBundle, STOP_TRANSIENT);
+            installSupport.stopBundle(dstate.serviceBundle, STOP_TRANSIENT);
             try (
                     InputStream is = getBundleInputStream(resource, providers)
             ) {
-                callback.updateBundle(dstate.serviceBundle, uri, is);
+                installSupport.updateBundle(dstate.serviceBundle, uri, is);
             }
-            callback.refreshPackages(toRefresh.keySet());
-            callback.startBundle(dstate.serviceBundle);
+            installSupport.refreshPackages(toRefresh.keySet());
+            installSupport.startBundle(dstate.serviceBundle);
             return;
         }
 
@@ -646,7 +632,7 @@ public class Deployer {
                     print("  " + bundle.getSymbolicName() + "/" + bundle.getVersion(), verbose);
                     // If the bundle start level will be changed, stop it persistently to
                     // avoid a restart when the start level is actually changed
-                    callback.stopBundle(bundle, toUpdateStartLevel.containsKey(bundle) ? 0 : STOP_TRANSIENT);
+                    installSupport.stopBundle(bundle, toUpdateStartLevel.containsKey(bundle) ? 0 : STOP_TRANSIENT);
                     toStop.remove(bundle);
                 }
             }
@@ -668,7 +654,7 @@ public class Deployer {
                 Deployer.RegionDeployment regionDeployment = entry.getValue();
                 for (Bundle bundle : regionDeployment.toDelete) {
                     print("  " + bundle.getSymbolicName() + "/" + bundle.getVersion(), verbose);
-                    callback.uninstall(bundle);
+                    installSupport.uninstall(bundle);
                     removeFromMapSet(managedBundles, name, bundle.getBundleId());
                 }
             }
@@ -712,7 +698,7 @@ public class Deployer {
                 }
             }
             // Apply all changes
-            callback.replaceDigraph(policies, bundles);
+            installSupport.replaceDigraph(policies, bundles);
         }
 
 
@@ -736,7 +722,7 @@ public class Deployer {
                     try (
                             InputStream is = getBundleInputStream(resource, providers)
                     ) {
-                        callback.updateBundle(bundle, uri, is);
+                        installSupport.updateBundle(bundle, uri, is);
                     }
                     toStart.add(bundle);
                 }
@@ -748,7 +734,7 @@ public class Deployer {
         for (Map.Entry<Bundle, Integer> entry : toUpdateStartLevel.entrySet()) {
             Bundle bundle = entry.getKey();
             int sl = entry.getValue();
-            callback.setBundleStartLevel(bundle, sl);
+            installSupport.setBundleStartLevel(bundle, sl);
         }
         //
         // Install bundles
@@ -773,7 +759,7 @@ public class Deployer {
                     try (
                             ChecksumUtils.CRCInputStream is = new ChecksumUtils.CRCInputStream(getBundleInputStream(resource, providers))
                     ) {
-                        bundle = callback.installBundle(name, uri, is);
+                        bundle = installSupport.installBundle(name, uri, is);
                         crc = is.getCRC();
                     }
                     addToMapSet(managedBundles, name, bundle.getBundleId());
@@ -805,7 +791,7 @@ public class Deployer {
 
             // Set start levels after install to avoid starting before all bundles are installed
             for (Bundle bundle : customStartLevels.keySet()) {
-                callback.setBundleStartLevel(bundle, customStartLevels.get(bundle));
+                installSupport.setBundleStartLevel(bundle, customStartLevels.get(bundle));
             }
         }
 
@@ -827,12 +813,14 @@ public class Deployer {
             Set<String> featureIds = flatten(newFeatures);
             for (Feature feature : dstate.features.values()) {
                 if (featureIds.contains(feature.getId())) {
-                    callback.installFeature(feature);
+                    installSupport.installConfigs(feature);
+                    installSupport.installLibraries(feature);
                 }
                 for (Conditional cond : feature.getConditional()) {
                     Feature condFeature = cond.asFeature();
                     if (featureIds.contains(condFeature.getId())) {
-                        callback.installFeature(condFeature);
+                        installSupport.installConfigs(condFeature);
+                        installSupport.installLibraries(condFeature);
                     }
                 }
             }
@@ -855,7 +843,7 @@ public class Deployer {
                     List<Bundle> bs = getBundlesToStop(toStop);
                     for (Bundle bundle : bs) {
                         print("  " + bundle.getSymbolicName() + "/" + bundle.getVersion(), verbose);
-                        callback.stopBundle(bundle, STOP_TRANSIENT);
+                        installSupport.stopBundle(bundle, STOP_TRANSIENT);
                         toStop.remove(bundle);
                         toStart.add(bundle);
                     }
@@ -872,7 +860,7 @@ public class Deployer {
                 if (dstate.serviceBundle != null && toRefresh.containsKey(dstate.serviceBundle)) {
                     ensureAllClassesLoaded(dstate.serviceBundle);
                 }
-                callback.refreshPackages(toRefresh.keySet());
+                installSupport.refreshPackages(toRefresh.keySet());
 
             }
         }
@@ -882,7 +870,7 @@ public class Deployer {
         toResolve.addAll(toRefresh.keySet());
         removeBundlesInState(toResolve, UNINSTALLED);
         callback.callListeners(DeploymentEvent.BUNDLES_INSTALLED);
-        callback.resolveBundles(toResolve, resolver.getWiring(), deployment.resToBnd);
+        installSupport.resolveBundles(toResolve, resolver.getWiring(), deployment.resToBnd);
         callback.callListeners(DeploymentEvent.BUNDLES_RESOLVED);
 
         // Compute bundles to start
@@ -896,7 +884,7 @@ public class Deployer {
                 for (Bundle bundle : bs) {
                     print("  " + bundle.getSymbolicName() + "/" + bundle.getVersion(), verbose);
                     try {
-                        callback.startBundle(bundle);
+                        installSupport.startBundle(bundle);
                     } catch (BundleException e) {
                         exceptions.add(e);
                     }

http://git-wip-us.apache.org/repos/asf/karaf/blob/bb0e9b77/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceConfig.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceConfig.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceConfig.java
new file mode 100644
index 0000000..d0b397b
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceConfig.java
@@ -0,0 +1,57 @@
+/*
+ * 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.karaf.features.internal.service;
+
+public class FeaturesServiceConfig {
+
+    public String overrides;
+    
+    /**
+     * Range to use when a version is specified on a feature dependency.
+     * The default is {@link org.apache.karaf.features.FeaturesService#DEFAULT_FEATURE_RESOLUTION_RANGE}
+     */
+    public String featureResolutionRange;
+    
+    /**
+     * Range to use when verifying if a bundle should be updated or
+     * new bundle installed.
+     * The default is {@link org.apache.karaf.features.FeaturesService#DEFAULT_BUNDLE_UPDATE_RANGE}
+     */
+    public String bundleUpdateRange;
+    
+    /**
+     * Use CRC to check snapshot bundles and update them if changed.
+     * Either:
+     * - none : never update snapshots
+     * - always : always update snapshots
+     * - crc : use CRC to detect changes
+     */
+    public String updateSnapshots;
+    
+    public int downloadThreads;
+    
+    public long scheduleDelay;
+    
+    public int scheduleMaxRun;
+    
+    /**
+     * Service requirements enforcement
+     */
+    public String serviceRequirements;
+    
+    public String blacklisted;
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/bb0e9b77/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
index 81e72d7..d3b332b 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
@@ -20,7 +20,6 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -31,14 +30,12 @@ import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
-import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -65,36 +62,17 @@ import org.apache.karaf.features.Repository;
 import org.apache.karaf.features.RepositoryEvent;
 import org.apache.karaf.features.internal.download.DownloadManager;
 import org.apache.karaf.features.internal.download.DownloadManagers;
-import org.apache.karaf.features.internal.region.DigraphHelper;
 import org.apache.karaf.features.internal.util.JsonReader;
 import org.apache.karaf.features.internal.util.JsonWriter;
-import org.apache.karaf.util.bundles.BundleUtils;
 import org.apache.karaf.util.collections.CopyOnWriteArrayIdentityList;
 import org.eclipse.equinox.region.Region;
 import org.eclipse.equinox.region.RegionDigraph;
-import org.eclipse.equinox.region.RegionFilterBuilder;
 import org.ops4j.pax.url.mvn.MavenResolver;
 import org.ops4j.pax.url.mvn.MavenResolvers;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.FrameworkEvent;
-import org.osgi.framework.FrameworkListener;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.Version;
-import org.osgi.framework.hooks.resolver.ResolverHook;
-import org.osgi.framework.hooks.resolver.ResolverHookFactory;
-import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
-import org.osgi.framework.namespace.HostNamespace;
-import org.osgi.framework.startlevel.BundleStartLevel;
 import org.osgi.framework.startlevel.FrameworkStartLevel;
-import org.osgi.framework.wiring.BundleCapability;
-import org.osgi.framework.wiring.BundleRequirement;
-import org.osgi.framework.wiring.BundleRevision;
-import org.osgi.framework.wiring.FrameworkWiring;
-import org.osgi.resource.Resource;
-import org.osgi.resource.Wire;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.resolver.Resolver;
@@ -122,13 +100,6 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
      */
     private final Bundle bundle;
     private final BundleContext bundleContext;
-
-    /**
-     * The system bundle context.
-     * For all bundles related operations, we use the system bundle context
-     * to allow this bundle to be stopped and still allow the deployment to
-     * take place.
-     */
     private final BundleContext systemBundleContext;
     /**
      * Used to load and save the {@link State} of this service.
@@ -137,40 +108,8 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
     private final FeatureFinder featureFinder;
     private final ConfigurationAdmin configurationAdmin;
     private final Resolver resolver;
-    private final FeatureConfigInstaller configInstaller;
-    private final RegionDigraph digraph;
-    private final String overrides;
-    /**
-     * Range to use when a version is specified on a feature dependency.
-     * The default is {@link org.apache.karaf.features.FeaturesService#DEFAULT_FEATURE_RESOLUTION_RANGE}
-     */
-    private final String featureResolutionRange;
-    /**
-     * Range to use when verifying if a bundle should be updated or
-     * new bundle installed.
-     * The default is {@link org.apache.karaf.features.FeaturesService#DEFAULT_BUNDLE_UPDATE_RANGE}
-     */
-    private final String bundleUpdateRange;
-    /**
-     * Use CRC to check snapshot bundles and update them if changed.
-     * Either:
-     * - none : never update snapshots
-     * - always : always update snapshots
-     * - crc : use CRC to detect changes
-     */
-    private final String updateSnaphots;
-    /**
-     * Service requirements enforcement
-     */
-    private final String serviceRequirements;
-
-    private final int downloadThreads;
-
-    private final long scheduleDelay;
-
-    private final int scheduleMaxRun;
-
-    private final String blacklisted;
+    private final BundleInstallSupport installSupport;
+    private final FeaturesServiceConfig cfg;
 
     private final ThreadLocal<String> outputFile = new ThreadLocal<>();
 
@@ -197,42 +136,9 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
                                FeatureFinder featureFinder,
                                ConfigurationAdmin configurationAdmin,
                                Resolver resolver,
-                               RegionDigraph digraph,
-                               String overrides,
-                               String featureResolutionRange,
-                               String bundleUpdateRange,
-                               String updateSnaphots,
-                               String serviceRequirements,
-                               org.osgi.service.repository.Repository globalRepository,
-                               int downloadThreads,
-                               long scheduleDelay,
-                               int scheduleMaxRun,
-                               String blacklisted) {
-        this(bundle, bundleContext,systemBundleContext, storage, featureFinder, configurationAdmin,
-                resolver, digraph, overrides, featureResolutionRange, bundleUpdateRange, updateSnaphots,
-                serviceRequirements, globalRepository, downloadThreads, scheduleDelay, scheduleMaxRun, blacklisted,
-                FeaturesService.DEFAULT_CONFIG_CFG_STORE);
-    }
-
-    public FeaturesServiceImpl(Bundle bundle,
-                               BundleContext bundleContext,
-                               BundleContext systemBundleContext,
-                               StateStorage storage,
-                               FeatureFinder featureFinder,
-                               ConfigurationAdmin configurationAdmin,
-                               Resolver resolver,
-                               RegionDigraph digraph,
-                               String overrides,
-                               String featureResolutionRange,
-                               String bundleUpdateRange,
-                               String updateSnaphots,
-                               String serviceRequirements,
+                               BundleInstallSupport installSupport,
                                org.osgi.service.repository.Repository globalRepository,
-                               int downloadThreads,
-                               long scheduleDelay,
-                               int scheduleMaxRun,
-                               String blacklisted,
-                               boolean configCfgStore) {
+                               FeaturesServiceConfig cfg) {
         this.bundle = bundle;
         this.bundleContext = bundleContext;
         this.systemBundleContext = systemBundleContext;
@@ -240,18 +146,9 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
         this.featureFinder = featureFinder;
         this.configurationAdmin = configurationAdmin;
         this.resolver = resolver;
-        this.configInstaller = configurationAdmin != null ? new FeatureConfigInstaller(configurationAdmin, configCfgStore) : null;
-        this.digraph = digraph;
-        this.overrides = overrides;
-        this.featureResolutionRange = featureResolutionRange;
-        this.bundleUpdateRange = bundleUpdateRange;
-        this.updateSnaphots = updateSnaphots;
-        this.serviceRequirements = serviceRequirements;
+        this.installSupport = installSupport;
         this.globalRepository = globalRepository;
-        this.downloadThreads = downloadThreads > 0 ? downloadThreads : 1;
-        this.scheduleDelay = scheduleDelay;
-        this.scheduleMaxRun = scheduleMaxRun;
-        this.blacklisted = blacklisted;
+        this.cfg = cfg;
         this.executor = Executors.newSingleThreadExecutor();
         loadState();
         checkResolve();
@@ -330,12 +227,12 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
                 // Make sure we don't store bundle checksums if
                 // it has been disabled through configadmin
                 // so that we don't keep out-of-date checksums.
-                if (!UPDATE_SNAPSHOTS_CRC.equalsIgnoreCase(updateSnaphots)) {
+                if (!UPDATE_SNAPSHOTS_CRC.equalsIgnoreCase(cfg.updateSnapshots)) {
                     state.bundleChecksums.clear();
                 }
                 storage.save(state);
                 if (bundleContext != null) { // For tests, this should never happen at runtime
-                    DigraphHelper.saveDigraph(bundleContext, digraph);
+                    installSupport.saveState();
                 }
             }
         } catch (IOException e) {
@@ -371,7 +268,7 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
                 installedFeatures.putAll(copy(state.installedFeatures));
             }
             for (String uri : repositories) {
-                Repository repository = new RepositoryImpl(URI.create(uri), blacklisted);
+                Repository repository = new RepositoryImpl(URI.create(uri), cfg.blacklisted);
                 listener.repositoryEvent(new RepositoryEvent(repository, RepositoryEvent.EventType.RepositoryAdded, true));
             }
             for (Map.Entry<String, Set<String>> entry : installedFeatures.entrySet()) {
@@ -455,7 +352,7 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
     //
 
     public Repository loadRepository(URI uri) throws Exception {
-        RepositoryImpl repo = new RepositoryImpl(uri, blacklisted);
+        RepositoryImpl repo = new RepositoryImpl(uri, cfg.blacklisted);
         repo.load(true);
         return repo;
     }
@@ -714,7 +611,7 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
         //the outer map's key is feature name, the inner map's key is feature version
         Map<String, Map<String, Feature>> map = new HashMap<>();
         // Load blacklist
-        Set<String> blacklistStrings = Blacklist.loadBlacklist(blacklisted);
+        Set<String> blacklistStrings = Blacklist.loadBlacklist(cfg.blacklisted);
         Clause[] blacklist = Parser.parseClauses(blacklistStrings.toArray(new String[blacklistStrings.size()]));
         // Two phase load:
         // * first load dependent repositories
@@ -1136,7 +1033,7 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
         // Region -> policy mapping
         dstate.bundlesPerRegion = new HashMap<>();
         dstate.filtersPerRegion = new HashMap<>();
-        RegionDigraph clone = digraph.copy();
+        RegionDigraph clone = installSupport.getDiGraphCopy();
         for (Region region : clone.getRegions()) {
             // Get bundles
             dstate.bundlesPerRegion.put(region.getName(), new HashSet<>(region.getBundleIds()));
@@ -1160,12 +1057,12 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
 
     private Deployer.DeploymentRequest getDeploymentRequest(Map<String, Set<String>> requirements, Map<String, Map<String, FeatureState>> stateChanges, EnumSet<Option> options, String outputFile) {
         Deployer.DeploymentRequest request = new Deployer.DeploymentRequest();
-        request.bundleUpdateRange = bundleUpdateRange;
-        request.featureResolutionRange = featureResolutionRange;
-        request.serviceRequirements = serviceRequirements;
-        request.updateSnaphots = updateSnaphots;
+        request.bundleUpdateRange = cfg.bundleUpdateRange;
+        request.featureResolutionRange = cfg.featureResolutionRange;
+        request.serviceRequirements = cfg.serviceRequirements;
+        request.updateSnaphots = cfg.updateSnapshots;
         request.globalRepository = globalRepository;
-        request.overrides = Overrides.loadOverrides(overrides);
+        request.overrides = Overrides.loadOverrides(cfg.overrides);
         request.requirements = requirements;
         request.stateChanges = stateChanges;
         request.options = options;
@@ -1184,16 +1081,16 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
 
         Dictionary<String, String> props = getMavenConfig();
         MavenResolver resolver = MavenResolvers.createMavenResolver(props, "org.ops4j.pax.url.mvn");
-        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(downloadThreads);
-        executor.setMaximumPoolSize(downloadThreads);
-        DownloadManager manager = DownloadManagers.createDownloadManager(resolver, executor, scheduleDelay, scheduleMaxRun);
+        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(cfg.downloadThreads);
+        executor.setMaximumPoolSize(cfg.downloadThreads);
+        DownloadManager manager = DownloadManagers.createDownloadManager(resolver, executor, cfg.scheduleDelay, cfg.scheduleMaxRun);
         try {
             Set<String> prereqs = new HashSet<>();
             while (true) {
                 try {
                     Deployer.DeploymentState dstate = getDeploymentState(state);
                     Deployer.DeploymentRequest request = getDeploymentRequest(requirements, stateChanges, options, outputFile);
-                    new Deployer(manager, this.resolver, this).deploy(dstate, request);
+                    new Deployer(manager, this.resolver, this.installSupport, this).deploy(dstate, request);
                     break;
                 } catch (Deployer.PartialDeploymentException e) {
                     if (!prereqs.containsAll(e.getMissing())) {
@@ -1237,20 +1134,6 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
         }
     }
 
-    @Override
-    public void refreshPackages(Collection<Bundle> bundles) throws InterruptedException {
-        final CountDownLatch latch = new CountDownLatch(1);
-        FrameworkWiring fw = systemBundleContext.getBundle().adapt(FrameworkWiring.class);
-        fw.refreshBundles(bundles, (FrameworkListener) event -> {
-            if (event.getType() == FrameworkEvent.ERROR) {
-                LOGGER.error("Framework error", event.getThrowable());
-            }
-            latch.countDown();
-        });
-        latch.await();
-    }
-
-    @Override
     public void saveState(State state) {
         synchronized (lock) {
             state.repositories.clear();
@@ -1266,158 +1149,6 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
         writeResolve(request.requirements, request.options);
     }
 
-    @Override
-    public void installFeature(Feature feature) throws IOException, InvalidSyntaxException {
-        if (configInstaller != null) {
-            configInstaller.installFeatureConfigs(feature);
-        }
-        // TODO: install libraries
-    }
-
-    @Override
-    public Bundle installBundle(String region, String uri, InputStream is) throws BundleException {
-        if (ROOT_REGION.equals(region)) {
-            return digraph.getRegion(region).installBundleAtLocation(uri, is);
-        } else {
-            return digraph.getRegion(region).installBundle(uri, is);
-        }
-    }
-
-    @Override
-    public void updateBundle(Bundle bundle, String uri, InputStream is) throws BundleException {
-        // We need to wrap the bundle to insert a Bundle-UpdateLocation header
-        try {
-            File file = BundleUtils.fixBundleWithUpdateLocation(is, uri);
-            bundle.update(new FileInputStream(file));
-            file.delete();
-        } catch (IOException e) {
-            throw new BundleException("Unable to update bundle", e);
-        }
-    }
-
-    @Override
-    public void uninstall(Bundle bundle) throws BundleException {
-        bundle.uninstall();
-    }
-
-    @Override
-    public void startBundle(Bundle bundle) throws BundleException {
-        if (bundle != this.bundle || bundle.getState() != Bundle.STARTING) {
-            bundle.start();
-        }
-    }
-
-    @Override
-    public void stopBundle(Bundle bundle, int options) throws BundleException {
-        bundle.stop(options);
-    }
-
-    @Override
-    public void setBundleStartLevel(Bundle bundle, int startLevel) {
-        bundle.adapt(BundleStartLevel.class).setStartLevel(startLevel);
-    }
-
-    @Override
-    public void resolveBundles(Set<Bundle> bundles, final Map<Resource, List<Wire>> wiring, Map<Resource, Bundle> resToBnd) {
-        // Make sure it's only used for us
-        final Thread thread = Thread.currentThread();
-        // Translate wiring
-        final Map<Bundle, Resource> bndToRes = new HashMap<>();
-        for (Resource res : resToBnd.keySet()) {
-            bndToRes.put(resToBnd.get(res), res);
-        }
-        // Hook
-        final ResolverHook hook = new ResolverHook() {
-            @Override
-            public void filterResolvable(Collection<BundleRevision> candidates) {
-            }
-            @Override
-            public void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates) {
-            }
-            @Override
-            public void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates) {
-                if (Thread.currentThread() == thread) {
-                    // osgi.ee capabilities are provided by the system bundle, so just ignore those
-                    if (ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE
-                            .equals(requirement.getNamespace())) {
-                        return;
-                    }
-                    Bundle sourceBundle = requirement.getRevision().getBundle();
-                    Resource sourceResource = bndToRes.get(sourceBundle);
-                    Set<Resource> wired = new HashSet<>();
-                    // Get a list of allowed wired resources
-                    wired.add(sourceResource);
-                    for (Wire wire : wiring.get(sourceResource)) {
-                        wired.add(wire.getProvider());
-                        if (HostNamespace.HOST_NAMESPACE.equals(wire.getRequirement().getNamespace())) {
-                            for (Wire hostWire : wiring.get(wire.getProvider())) {
-                                wired.add(hostWire.getProvider());
-                            }
-                        }
-                    }
-                    // Remove candidates that are not allowed
-                    for (Iterator<BundleCapability> candIter = candidates.iterator(); candIter.hasNext(); ) {
-                        BundleCapability cand = candIter.next();
-                        BundleRevision br = cand.getRevision();
-                        if ((br.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
-                            br = br.getWiring().getRequiredWires(null).get(0).getProvider();
-                        }
-                        Resource res = bndToRes.get(br.getBundle());
-                        if (!wired.contains(br) && !wired.contains(res)) {
-                            candIter.remove();
-                        }
-                    }
-                }
-            }
-            @Override
-            public void end() {
-            }
-        };
-        ResolverHookFactory factory = triggers -> hook;
-        ServiceRegistration<ResolverHookFactory> registration = systemBundleContext.registerService(ResolverHookFactory.class, factory, null);
-        try {
-            FrameworkWiring frameworkWiring = systemBundleContext.getBundle().adapt(FrameworkWiring.class);
-            frameworkWiring.resolveBundles(bundles);
-        } finally {
-            registration.unregister();
-        }
-    }
-
-    @Override
-    public void replaceDigraph(Map<String, Map<String, Map<String, Set<String>>>> policies, Map<String, Set<Long>> bundles) throws BundleException, InvalidSyntaxException {
-        RegionDigraph temp = digraph.copy();
-        // Remove everything
-        for (Region region : temp.getRegions()) {
-            temp.removeRegion(region);
-        }
-        // Re-create regions
-        for (String name : policies.keySet()) {
-            temp.createRegion(name);
-        }
-        // Dispatch bundles
-        for (Map.Entry<String, Set<Long>> entry : bundles.entrySet()) {
-            Region region = temp.getRegion(entry.getKey());
-            for (long bundleId : entry.getValue()) {
-                region.addBundle(bundleId);
-            }
-        }
-        // Add policies
-        for (Map.Entry<String, Map<String, Map<String, Set<String>>>> entry1 : policies.entrySet()) {
-            Region region1 = temp.getRegion(entry1.getKey());
-            for (Map.Entry<String, Map<String, Set<String>>> entry2 : entry1.getValue().entrySet()) {
-                Region region2 = temp.getRegion(entry2.getKey());
-                RegionFilterBuilder rfb = temp.createRegionFilterBuilder();
-                for (Map.Entry<String, Set<String>> entry3 : entry2.getValue().entrySet()) {
-                    for (String flt : entry3.getValue()) {
-                        rfb.allow(entry3.getKey(), flt);
-                    }
-                }
-                region1.connectRegion(region2, rfb.build());
-            }
-        }
-        digraph.replace(temp);
-    }
-
     private Pattern getFeaturePattern(String name, String version) {
         String req = FEATURE_OSGI_REQUIREMENT_PREFIX + getFeatureRequirement(name, version);
         req = req.replace("[", "\\[");

http://git-wip-us.apache.org/repos/asf/karaf/blob/bb0e9b77/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java b/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
index f1c245e..cffbe8b 100644
--- a/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
+++ b/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
@@ -31,6 +31,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
 
 import org.apache.felix.resolver.ResolverImpl;
 import org.apache.karaf.features.internal.resolver.Slf4jResolverLog;
+import org.apache.karaf.features.internal.service.FeaturesServiceConfig;
 import org.apache.karaf.features.internal.service.FeaturesServiceImpl;
 import org.apache.karaf.features.internal.service.StateStorage;
 import org.easymock.EasyMock;
@@ -48,7 +49,6 @@ import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -349,7 +349,8 @@ public class FeaturesServiceTest extends TestBase {
                 + "  <feature name='f2' version='0.2'><bundle>bundle2</bundle></feature>"
                 + "</features>");
 
-        FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, resolver, null, null, null, null, null, null, null, 0, 0, 0, null);
+        FeaturesServiceConfig cfg = new FeaturesServiceConfig();
+        FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, resolver, null, null, cfg);
         svc.addRepository(uri);
 
         assertEquals(feature("f2", "0.2"), svc.getFeatures("f2", "[0.1,0.3)")[0]);
@@ -368,14 +369,13 @@ public class FeaturesServiceTest extends TestBase {
         BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
         Bundle bundle = EasyMock.createMock(Bundle.class);
         FrameworkStartLevel fsl = EasyMock.createMock(FrameworkStartLevel.class);
-        expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
-        expect(bundleContext.getBundle()).andReturn(bundle);
         expect(bundle.adapt(FrameworkStartLevel.class)).andReturn(fsl);
         expect(fsl.getInitialBundleStartLevel()).andReturn(50);
         expect(fsl.getStartLevel()).andReturn(100);
         replay(bundleContext, bundle, fsl);
 
-        FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, bundleContext, new Storage(), null, null, resolver, null, null, null, null, null, null, null, 0, 0, 0, null);
+        FeaturesServiceConfig cfg = new FeaturesServiceConfig();
+        FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, bundleContext, new Storage(), null, null, resolver, null, null, cfg);
         svc.addRepository(uri);
         try {
             List<String> features = new ArrayList<String>();
@@ -400,7 +400,8 @@ public class FeaturesServiceTest extends TestBase {
         URI uri = createTempRepo("<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
                 + "  <featur><bundle>somebundle</bundle></featur></features>");
 
-        FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, resolver, null, null, null, null, null, null, null, 0, 0, 0, null);
+        FeaturesServiceConfig cfg = new FeaturesServiceConfig();
+        FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, resolver, null, null, cfg);
         try {
             svc.addRepository(uri);
             fail("exception expected");
@@ -418,7 +419,8 @@ public class FeaturesServiceTest extends TestBase {
                 + "  <feature name='f1'><bundle>file:bundle1</bundle><bundle>file:bundle2</bundle></feature>"
                 + "</features>");
 
-        FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, resolver, null, null, null, null, null, null, null, 0, 0, 0, null);
+        FeaturesServiceConfig cfg = new FeaturesServiceConfig();
+        FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, resolver, null, null, cfg);
         svc.addRepository(uri);
         Feature[] features = svc.getFeatures("f1");
         Assert.assertEquals(1, features.length);

http://git-wip-us.apache.org/repos/asf/karaf/blob/bb0e9b77/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
index fb898c0..de7a7cd 100644
--- a/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
+++ b/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
@@ -19,7 +19,6 @@ package org.apache.karaf.features.internal.service;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -39,13 +38,14 @@ import org.apache.karaf.features.FeaturesService;
 import org.apache.karaf.features.internal.resolver.Slf4jResolverLog;
 import org.apache.karaf.features.internal.support.TestBundle;
 import org.apache.karaf.features.internal.support.TestDownloadManager;
+import org.easymock.Capture;
 import org.easymock.EasyMock;
 import org.easymock.IAnswer;
 import org.easymock.IArgumentMatcher;
+import org.easymock.IMocksControl;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
-import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.resource.Resource;
 import org.osgi.resource.Wire;
 import org.osgi.service.resolver.Resolver;
@@ -55,6 +55,8 @@ import org.slf4j.LoggerFactory;
 import static org.apache.karaf.features.FeaturesService.*;
 import static org.apache.karaf.features.internal.util.MapUtils.addToMapSet;
 import static org.easymock.EasyMock.anyInt;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.expectLastCall;
 import static org.junit.Assert.fail;
 
 public class DeployerTest {
@@ -64,7 +66,7 @@ public class DeployerTest {
 
     @Test
     public void testInstallSimpleFeature() throws Exception {
-
+        IMocksControl c = EasyMock.createControl();
         String dataDir = "data1";
 
         TestDownloadManager manager = new TestDownloadManager(getClass(), dataDir);
@@ -74,23 +76,26 @@ public class DeployerTest {
         Feature f100 = repo.getFeatures()[0];
         Feature f101 = repo.getFeatures()[1];
 
-        Deployer.DeployCallback callback = EasyMock.createMock(Deployer.DeployCallback.class);
-        Deployer deployer = new Deployer(manager, resolver, callback);
+        Deployer.DeployCallback callback = c.createMock(Deployer.DeployCallback.class);
+        BundleInstallSupport installSupport = c.createMock(BundleInstallSupportImpl.class);
+        Deployer deployer = new Deployer(manager, resolver, installSupport, callback);
 
         callback.print(EasyMock.anyString(), EasyMock.anyBoolean());
         EasyMock.expectLastCall().anyTimes();
         callback.callListeners(DeploymentEvent.DEPLOYMENT_STARTED);
         EasyMock.expectLastCall();
-        callback.replaceDigraph(EasyMock.<Map<String, Map<String, Map<String, Set<String>>>>>anyObject(),
+        installSupport.replaceDigraph(EasyMock.<Map<String, Map<String, Map<String, Set<String>>>>>anyObject(),
                 EasyMock.<Map<String, Set<Long>>>anyObject());
         EasyMock.expectLastCall();
         callback.saveState(EasyMock.<State>anyObject());
         EasyMock.expectLastCall();
-        callback.installFeature(f100);
+        installSupport.installConfigs(f100);
+        EasyMock.expectLastCall();
+        installSupport.installLibraries(f100);
         EasyMock.expectLastCall();
         callback.callListeners(DeploymentEvent.BUNDLES_INSTALLED);
         EasyMock.expectLastCall();
-        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject(),
+        installSupport.resolveBundles(EasyMock.<Set<Bundle>>anyObject(),
                                 EasyMock.<Map<Resource, List<Wire>>>anyObject(),
                                 EasyMock.<Map<Resource, Bundle>>anyObject());
         EasyMock.expectLastCall();
@@ -102,10 +107,10 @@ public class DeployerTest {
         EasyMock.expectLastCall();
 
         Bundle bundleA = createTestBundle(1, Bundle.ACTIVE, dataDir, "a100");
-        EasyMock.expect(callback.installBundle(EasyMock.eq(ROOT_REGION), EasyMock.eq("a100"), EasyMock.<InputStream>anyObject()))
+        EasyMock.expect(installSupport.installBundle(EasyMock.eq(ROOT_REGION), EasyMock.eq("a100"), EasyMock.<InputStream>anyObject()))
                 .andReturn(bundleA);
 
-        EasyMock.replay(callback);
+        c.replay();
 
         Deployer.DeploymentState dstate = new Deployer.DeploymentState();
         dstate.state = new State();
@@ -130,11 +135,12 @@ public class DeployerTest {
 
         deployer.deploy(dstate, request);
 
-        EasyMock.verify(callback);
+        c.verify();
     }
 
     @Test
     public void testUpdateSimpleFeature() throws Exception {
+        IMocksControl c = EasyMock.createControl();
 
         final String dataDir = "data1";
 
@@ -145,8 +151,9 @@ public class DeployerTest {
         Feature f100 = repo.getFeatures()[0];
         Feature f101 = repo.getFeatures()[1];
 
-        Deployer.DeployCallback callback = EasyMock.createMock(Deployer.DeployCallback.class);
-        Deployer deployer = new Deployer(manager, resolver, callback);
+        Deployer.DeployCallback callback = c.createMock(Deployer.DeployCallback.class);
+        BundleInstallSupport installSupport = c.createMock(BundleInstallSupportImpl.class);
+        Deployer deployer = new Deployer(manager, resolver, installSupport, callback);
 
         final TestBundle bundleA = createTestBundle(1L, Bundle.ACTIVE, dataDir, "a100");
 
@@ -155,7 +162,7 @@ public class DeployerTest {
         callback.callListeners(DeploymentEvent.DEPLOYMENT_STARTED);
         EasyMock.expectLastCall();
 
-        callback.stopBundle(EasyMock.eq(bundleA), anyInt());
+        installSupport.stopBundle(EasyMock.eq(bundleA), anyInt());
         EasyMock.expectLastCall().andStubAnswer(new IAnswer<Object>() {
             @Override
             public Object answer() throws Throwable {
@@ -163,7 +170,7 @@ public class DeployerTest {
                 return null;
             }
         });
-        callback.updateBundle(EasyMock.eq(bundleA), EasyMock.<String>anyObject(), EasyMock.<InputStream>anyObject());
+        installSupport.updateBundle(EasyMock.eq(bundleA), EasyMock.<String>anyObject(), EasyMock.<InputStream>anyObject());
         EasyMock.expectLastCall().andStubAnswer(new IAnswer<Object>() {
             @Override
             public Object answer() throws Throwable {
@@ -177,25 +184,27 @@ public class DeployerTest {
                 return null;
             }
         });
-        callback.startBundle(EasyMock.eq(bundleA));
+        installSupport.startBundle(EasyMock.eq(bundleA));
         EasyMock.expectLastCall();
 
-        callback.replaceDigraph(EasyMock.<Map<String, Map<String, Map<String, Set<String>>>>>anyObject(),
+        installSupport.replaceDigraph(EasyMock.<Map<String, Map<String, Map<String, Set<String>>>>>anyObject(),
                 EasyMock.<Map<String, Set<Long>>>anyObject());
         EasyMock.expectLastCall();
         callback.saveState(EasyMock.<State>anyObject());
         EasyMock.expectLastCall();
-        callback.installFeature(f101);
+        installSupport.installConfigs(f101);
+        EasyMock.expectLastCall();
+        installSupport.installLibraries(f101);
         EasyMock.expectLastCall();
         callback.callListeners(DeploymentEvent.BUNDLES_INSTALLED);
         EasyMock.expectLastCall();
-        callback.resolveBundles(EasyMock.eq(Collections.<Bundle>singleton(bundleA)),
+        installSupport.resolveBundles(EasyMock.eq(Collections.<Bundle>singleton(bundleA)),
                                 EasyMock.<Map<Resource, List<Wire>>>anyObject(),
                                 EasyMock.<Map<Resource, Bundle>>anyObject());
         EasyMock.expectLastCall();
         callback.callListeners(DeploymentEvent.BUNDLES_RESOLVED);
         EasyMock.expectLastCall();
-        callback.refreshPackages(EasyMock.eq(Collections.<Bundle>singleton(bundleA)));
+        installSupport.refreshPackages(EasyMock.eq(Collections.<Bundle>singleton(bundleA)));
         EasyMock.expectLastCall();
         callback.callListeners(FeatureEventMatcher.eq(new FeatureEvent(FeatureEvent.EventType.FeatureUninstalled, f100, FeaturesService.ROOT_REGION, false)));
         EasyMock.expectLastCall();
@@ -204,7 +213,7 @@ public class DeployerTest {
         callback.callListeners(DeploymentEvent.DEPLOYMENT_FINISHED);
         EasyMock.expectLastCall();
 
-        EasyMock.replay(callback);
+        c.replay();
 
         Deployer.DeploymentState dstate = new Deployer.DeploymentState();
         dstate.state = new State();
@@ -233,12 +242,12 @@ public class DeployerTest {
 
         deployer.deploy(dstate, request);
 
-        EasyMock.verify(callback);
+        c.verify();
     }
 
     @Test
     public void testUpdateServiceBundle() throws Exception {
-
+        IMocksControl c = EasyMock.createControl();
         String dataDir = "data1";
 
         TestDownloadManager manager = new TestDownloadManager(getClass(), dataDir);
@@ -249,23 +258,26 @@ public class DeployerTest {
 
         Bundle serviceBundle = createTestBundle(1, Bundle.ACTIVE, dataDir, "a100");
 
-        Deployer.DeployCallback callback = EasyMock.createMock(Deployer.DeployCallback.class);
-        Deployer deployer = new Deployer(manager, resolver, callback);
+        Deployer.DeployCallback callback = c.createMock(Deployer.DeployCallback.class);
+        BundleInstallSupport installSupport = c.createMock(BundleInstallSupportImpl.class);
+        Deployer deployer = new Deployer(manager, resolver, installSupport, callback);
 
         callback.print(EasyMock.anyString(), EasyMock.anyBoolean());
         EasyMock.expectLastCall().anyTimes();
         callback.callListeners(DeploymentEvent.DEPLOYMENT_STARTED);
         EasyMock.expectLastCall();
-        callback.replaceDigraph(EasyMock.<Map<String, Map<String, Map<String, Set<String>>>>>anyObject(),
+        installSupport.replaceDigraph(EasyMock.<Map<String, Map<String, Map<String, Set<String>>>>>anyObject(),
                 EasyMock.<Map<String, Set<Long>>>anyObject());
         EasyMock.expectLastCall();
         callback.saveState(EasyMock.<State>anyObject());
         EasyMock.expectLastCall();
         callback.callListeners(DeploymentEvent.BUNDLES_INSTALLED);
         EasyMock.expectLastCall();
-        callback.installFeature(f1);
+        installSupport.installConfigs(f1);
+        EasyMock.expectLastCall();
+        installSupport.installLibraries(f1);
         EasyMock.expectLastCall();
-        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject(),
+        installSupport.resolveBundles(EasyMock.<Set<Bundle>>anyObject(),
                                 EasyMock.<Map<Resource, List<Wire>>>anyObject(),
                                 EasyMock.<Map<Resource, Bundle>>anyObject());
         EasyMock.expectLastCall();
@@ -276,7 +288,7 @@ public class DeployerTest {
         callback.callListeners(DeploymentEvent.DEPLOYMENT_FINISHED);
         EasyMock.expectLastCall();
 
-        EasyMock.replay(callback);
+        c.replay();
 
         Deployer.DeploymentState dstate = new Deployer.DeploymentState();
         dstate.state = new State();
@@ -301,11 +313,12 @@ public class DeployerTest {
 
         deployer.deploy(dstate, request);
 
-        EasyMock.verify(callback);
+        c.verify();
     }
 
     @Test
     public void testPrerequisite() throws Exception {
+        IMocksControl c = EasyMock.createControl();
 
         String dataDir = "data2";
 
@@ -319,25 +332,28 @@ public class DeployerTest {
         Bundle serviceBundle1 = createTestBundle(1, Bundle.ACTIVE, dataDir, "a100");
         Bundle serviceBundle2 = createTestBundle(2, Bundle.ACTIVE, dataDir, "b100");
 
-        Deployer.DeployCallback callback = EasyMock.createMock(Deployer.DeployCallback.class);
-        Deployer deployer = new Deployer(manager, resolver, callback);
+        Deployer.DeployCallback callback = c.createMock(Deployer.DeployCallback.class);
+        BundleInstallSupport installSupport = c.createMock(BundleInstallSupportImpl.class);
+        Deployer deployer = new Deployer(manager, resolver, installSupport, callback);
 
         callback.print(EasyMock.anyString(), EasyMock.anyBoolean());
         EasyMock.expectLastCall().anyTimes();
         callback.callListeners(DeploymentEvent.DEPLOYMENT_STARTED);
         EasyMock.expectLastCall();
-        callback.installBundle(EasyMock.eq(ROOT_REGION), EasyMock.eq("a100"), EasyMock.<InputStream>anyObject());
+        installSupport.installBundle(EasyMock.eq(ROOT_REGION), EasyMock.eq("a100"), EasyMock.<InputStream>anyObject());
         EasyMock.expectLastCall().andReturn(serviceBundle1);
-        callback.replaceDigraph(EasyMock.<Map<String, Map<String, Map<String, Set<String>>>>>anyObject(),
+        installSupport.replaceDigraph(EasyMock.<Map<String, Map<String, Map<String, Set<String>>>>>anyObject(),
                 EasyMock.<Map<String, Set<Long>>>anyObject());
         EasyMock.expectLastCall();
         callback.saveState(EasyMock.<State>anyObject());
         EasyMock.expectLastCall();
-        callback.installFeature(f1);
+        installSupport.installConfigs(EasyMock.anyObject());
+        EasyMock.expectLastCall();
+        installSupport.installLibraries(EasyMock.anyObject());
         EasyMock.expectLastCall();
         callback.callListeners(DeploymentEvent.BUNDLES_INSTALLED);
         EasyMock.expectLastCall();
-        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject(),
+        installSupport.resolveBundles(EasyMock.<Set<Bundle>>anyObject(),
                 EasyMock.<Map<Resource, List<Wire>>>anyObject(),
                 EasyMock.<Map<Resource, Bundle>>anyObject());
         EasyMock.expectLastCall();
@@ -348,7 +364,7 @@ public class DeployerTest {
         callback.callListeners(DeploymentEvent.DEPLOYMENT_FINISHED);
         EasyMock.expectLastCall();
 
-        EasyMock.replay(callback);
+        c.replay();
 
         Deployer.DeploymentState dstate = new Deployer.DeploymentState();
         dstate.state = new State();
@@ -378,26 +394,28 @@ public class DeployerTest {
             // ok
         }
 
-        EasyMock.verify(callback);
+        c.verify();
 
-        EasyMock.reset(callback);
+        c.reset();
 
         callback.print(EasyMock.anyString(), EasyMock.anyBoolean());
         EasyMock.expectLastCall().anyTimes();
         callback.callListeners(DeploymentEvent.DEPLOYMENT_STARTED);
         EasyMock.expectLastCall();
-        callback.installBundle(EasyMock.eq(ROOT_REGION), EasyMock.eq("b100"), EasyMock.<InputStream>anyObject());
+        installSupport.installBundle(EasyMock.eq(ROOT_REGION), EasyMock.eq("b100"), EasyMock.<InputStream>anyObject());
         EasyMock.expectLastCall().andReturn(serviceBundle2);
-        callback.replaceDigraph(EasyMock.<Map<String, Map<String, Map<String, Set<String>>>>>anyObject(),
+        installSupport.replaceDigraph(EasyMock.<Map<String, Map<String, Map<String, Set<String>>>>>anyObject(),
                 EasyMock.<Map<String, Set<Long>>>anyObject());
         EasyMock.expectLastCall();
         callback.saveState(EasyMock.<State>anyObject());
         EasyMock.expectLastCall();
-        callback.installFeature(f2);
+        installSupport.installConfigs(f2);
+        EasyMock.expectLastCall();
+        installSupport.installLibraries(f2);
         EasyMock.expectLastCall();
         callback.callListeners(DeploymentEvent.BUNDLES_INSTALLED);
         EasyMock.expectLastCall();
-        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject(),
+        installSupport.resolveBundles(EasyMock.<Set<Bundle>>anyObject(),
                 EasyMock.<Map<Resource, List<Wire>>>anyObject(),
                 EasyMock.<Map<Resource, Bundle>>anyObject());
         EasyMock.expectLastCall();
@@ -461,7 +479,10 @@ public class DeployerTest {
         doTestPrereqOnPrereq(4);
     }
 
+    @SuppressWarnings("unchecked")
     private void doTestPrereqOnPrereq(int scenario) throws Exception {
+        IMocksControl c = EasyMock.createControl();
+
         String dataDir = "data3";
 
         TestDownloadManager manager = new TestDownloadManager(getClass(), dataDir);
@@ -494,8 +515,22 @@ public class DeployerTest {
         request.stateChanges = Collections.emptyMap();
         request.updateSnaphots = UPDATE_SNAPSHOTS_NONE;
 
-        MyDeployCallback callback = new MyDeployCallback(dstate, bundles);
-        Deployer deployer = new Deployer(manager, resolver, callback);
+        MyDeployCallback callback = new MyDeployCallback(dstate);
+        BundleInstallSupport installSupport = c.createMock(BundleInstallSupportImpl.class);
+        Capture<String> capture = Capture.newInstance();
+        installSupport.installBundle(EasyMock.anyString(), EasyMock.capture(capture), anyObject(InputStream.class));
+        EasyMock.expectLastCall().andAnswer(() -> bundles.get(capture.getValue())).atLeastOnce();
+        installSupport.installConfigs(EasyMock.anyObject());
+        EasyMock.expectLastCall().atLeastOnce();
+        installSupport.installLibraries(EasyMock.anyObject());
+        EasyMock.expectLastCall().atLeastOnce();
+        installSupport.replaceDigraph(EasyMock.<Map<String, Map<String, Map<String, Set<String>>>>>anyObject(),
+                               EasyMock.<Map<String, Set<Long>>>anyObject());
+        expectLastCall().atLeastOnce();
+        installSupport.resolveBundles(anyObject(Set.class), anyObject(Map.class), anyObject(Map.class));
+        expectLastCall().atLeastOnce();
+        Deployer deployer = new Deployer(manager, resolver, installSupport, callback);
+        c.replay();
 
         for (int i = 1; i <= 4; i++) {
             request.requirements = new HashMap<>();
@@ -557,11 +592,9 @@ public class DeployerTest {
 
     private static class MyDeployCallback implements Deployer.DeployCallback {
         final Deployer.DeploymentState dstate;
-        final Map<String, Bundle> bundles;
 
-        public MyDeployCallback(Deployer.DeploymentState dstate, Map<String, Bundle> bundles) {
+        public MyDeployCallback(Deployer.DeploymentState dstate) {
             this.dstate = dstate;
-            this.bundles = bundles;
         }
 
         @Override
@@ -578,10 +611,6 @@ public class DeployerTest {
         }
 
         @Override
-        public void installFeature(Feature feature) throws IOException, InvalidSyntaxException {
-        }
-
-        @Override
         public void callListeners(FeatureEvent featureEvent) {
         }
 
@@ -589,41 +618,5 @@ public class DeployerTest {
         public void callListeners(DeploymentEvent deployEvent) {
         }
 
-        @Override
-        public Bundle installBundle(String region, String uri, InputStream is) throws BundleException {
-            return bundles.get(uri);
-        }
-
-        @Override
-        public void updateBundle(Bundle bundle, String uri, InputStream is) throws BundleException {
-        }
-
-        @Override
-        public void uninstall(Bundle bundle) throws BundleException {
-        }
-
-        @Override
-        public void startBundle(Bundle bundle) throws BundleException {
-        }
-
-        @Override
-        public void stopBundle(Bundle bundle, int options) throws BundleException {
-        }
-
-        @Override
-        public void setBundleStartLevel(Bundle bundle, int startLevel) {
-        }
-
-        @Override
-        public void refreshPackages(Collection<Bundle> bundles) throws InterruptedException {
-        }
-
-        @Override
-        public void resolveBundles(Set<Bundle> bundles, Map<Resource, List<Wire>> wiring, Map<Resource, Bundle> resToBnd) {
-        }
-
-        @Override
-        public void replaceDigraph(Map<String, Map<String, Map<String, Set<String>>>> policies, Map<String, Set<Long>> bundles) throws BundleException, InvalidSyntaxException {
-        }
     }
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/bb0e9b77/features/core/src/test/java/org/apache/karaf/features/internal/service/FeaturesServiceImplTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/service/FeaturesServiceImplTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/service/FeaturesServiceImplTest.java
index 8d592fc..06f2ebd 100644
--- a/features/core/src/test/java/org/apache/karaf/features/internal/service/FeaturesServiceImplTest.java
+++ b/features/core/src/test/java/org/apache/karaf/features/internal/service/FeaturesServiceImplTest.java
@@ -56,7 +56,8 @@ public class FeaturesServiceImplTest extends TestBase {
     public void testGetFeature() throws Exception {
         Feature transactionFeature = feature("transaction", "1.0.0");
         final Map<String, Map<String, Feature>> features = features(transactionFeature);
-        final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, this.resolver, null, "", null, null, null, null, null, 0, 0, 0, null) {
+        FeaturesServiceConfig cfg = new FeaturesServiceConfig();
+        final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, this.resolver, null, null, cfg ) {
             protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
                 return features;
             }
@@ -67,7 +68,8 @@ public class FeaturesServiceImplTest extends TestBase {
     
     @Test
     public void testGetFeatureStripVersion() throws Exception {
-        final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, this.resolver, null, "", null, null, null, null, null, 0, 0, 0, null) {
+        FeaturesServiceConfig cfg = new FeaturesServiceConfig();
+        final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, this.resolver, null, null, cfg) {
             protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
                 return features(feature("transaction", "1.0.0"));
             }
@@ -81,7 +83,8 @@ public class FeaturesServiceImplTest extends TestBase {
     
     @Test
     public void testGetFeatureNotAvailable() throws Exception {
-        final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, this.resolver, null, "", null, null, null, null, null, 0, 0, 0, null) {
+        FeaturesServiceConfig cfg = new FeaturesServiceConfig();
+        final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, this.resolver, null, null, cfg) {
             protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
                 return features(feature("transaction", "1.0.0"));
             }
@@ -95,7 +98,8 @@ public class FeaturesServiceImplTest extends TestBase {
                 feature("transaction", "1.0.0"),
                 feature("transaction", "2.0.0")
         );
-        final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, this.resolver, null, "", null, null, null, null, null, 0, 0, 0, null) {
+        FeaturesServiceConfig cfg = new FeaturesServiceConfig();
+        final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, this.resolver, null, null, cfg) {
             protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
                 return features;
             }
@@ -113,7 +117,8 @@ public class FeaturesServiceImplTest extends TestBase {
             }
         } : null);
         try {
-            final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, this.resolver, null, "", null, null, null, null, null, 0, 0, 0, null);
+            FeaturesServiceConfig cfg = new FeaturesServiceConfig();
+            final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, null, new Storage(), null, null, this.resolver, null, null, cfg);
             impl.addRepository(URI.create("custom:cycle/a-references-b.xml"));
             impl.getFeatures();
         } finally {


Mime
View raw message