You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/04/11 19:20:41 UTC

[19/33] Revert "[KARAF-2852] Merge features/core and features/command"

http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/src/main/java/org/apache/karaf/features/internal/service/Artifact.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/features/internal/service/Artifact.java b/features/src/main/java/org/apache/karaf/features/internal/service/Artifact.java
deleted file mode 100644
index 44e9a7c..0000000
--- a/features/src/main/java/org/apache/karaf/features/internal/service/Artifact.java
+++ /dev/null
@@ -1,56 +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.karaf.features.internal.service;
-
-import java.net.URI;
-
-/**
- * Simple abstraction of a maven artifact to avoid external deps
- */
-public class Artifact {
-    String groupId;
-    String artifactId;
-    String version;
-    String extension;
-    String classifier;
-    
-    public Artifact(String coords) {
-        String[] coordsAr = coords.split(":");
-        if (coordsAr.length != 5) {
-            throw new IllegalArgumentException("Maven URL " + coords + " is malformed or not complete");
-        }
-        this.groupId = coordsAr[0];
-        this.artifactId = coordsAr[1];
-        this.version = coordsAr[4];
-        this.extension = coordsAr[2];
-        this.classifier = coordsAr[3];
-    }
-    
-    public Artifact(String coords, String version) {
-        this(coords);
-        this.version = version;
-    }
-    
-    public URI getMavenUrl(String version) {
-        String uriSt = "mvn:" + this.groupId + "/" + this.artifactId + "/" + version + "/" + this.extension + "/" + this.classifier;
-        try {
-            return new URI(uriSt);
-        } catch (Exception e) {
-            return null;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/src/main/java/org/apache/karaf/features/internal/service/BootFeaturesInstaller.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/features/internal/service/BootFeaturesInstaller.java b/features/src/main/java/org/apache/karaf/features/internal/service/BootFeaturesInstaller.java
deleted file mode 100644
index 5b362b8..0000000
--- a/features/src/main/java/org/apache/karaf/features/internal/service/BootFeaturesInstaller.java
+++ /dev/null
@@ -1,170 +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.karaf.features.internal.service;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.karaf.features.BootFinished;
-import org.apache.karaf.features.Feature;
-import org.apache.karaf.features.FeaturesService;
-import org.osgi.framework.BundleContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class BootFeaturesInstaller {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(BootFeaturesInstaller.class);
-
-    public static String VERSION_PREFIX = "version=";
-
-    private final FeaturesServiceImpl featuresService;
-    private final BundleContext bundleContext;
-    private final String repositories;
-    private final String features;
-    private final boolean asynchronous;
-
-    /**
-     *
-     * @param features list of boot features separated by comma. Optionally contains ;version=x.x.x to specify a specific feature version
-     */
-    public BootFeaturesInstaller(BundleContext bundleContext,
-                                 FeaturesServiceImpl featuresService,
-                                 String repositories,
-                                 String features,
-                                 boolean asynchronous) {
-        this.bundleContext = bundleContext;
-        this.featuresService = featuresService;
-        this.repositories = repositories;
-        this.features = features;
-        this.asynchronous = asynchronous;
-    }
-
-    /**
-     * Install boot features
-     */
-    public void start() {
-        if (featuresService.isBootDone()) {
-            publishBootFinished();
-            return;
-        }
-        if (asynchronous) {
-            new Thread("Initial Features Provisioning") {
-                public void run() {
-                    installBootFeatures();
-                }
-            }.start();
-        } else {
-            installBootFeatures();
-        }
-    }
-
-    protected void installBootFeatures() {
-        try {
-            for (String repo : repositories.split(",")) {
-                repo = repo.trim();
-                if (!repo.isEmpty()) {
-                    try {
-                        featuresService.addRepository(URI.create(repo));
-                    } catch (Exception e) {
-                        LOGGER.error("Error installing boot feature repository " + repo, e);
-                    }
-                }
-            }
-
-            List<Set<String>> stagedFeatures = parseBootFeatures(features);
-            for (Set<String> features : stagedFeatures) {
-                featuresService.installFeatures(features, EnumSet.of(FeaturesService.Option.NoFailOnFeatureNotFound));
-            }
-            featuresService.bootDone();
-            publishBootFinished();
-        } catch (Exception e) {
-            // Special handling in case the bundle has been refreshed.
-            // In such a case, simply exits without logging any exception
-            // as the restart should cause the feature service to finish
-            // the work.
-            if (e instanceof IllegalStateException) {
-                try {
-                    bundleContext.getBundle();
-                } catch (IllegalStateException ies) {
-                    return;
-                }
-            }
-            LOGGER.error("Error installing boot features", e);
-        }
-    }
-
-    /**
-     *
-     * @param featureSt either feature name or <featurename>;version=<version>
-     * @return feature matching the feature string
-     * @throws Exception
-     */
-    private Feature getFeature(String featureSt) throws Exception {
-        String[] parts = featureSt.trim().split(";");
-        String featureName = parts[0];
-        String featureVersion = null;
-        for (String part : parts) {
-            // if the part starts with "version=" it contains the version info
-            if (part.startsWith(VERSION_PREFIX)) {
-                featureVersion = part.substring(VERSION_PREFIX.length());
-            }
-        }
-        if (featureVersion == null) {
-            // no version specified - use default version
-            featureVersion = org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION;
-        }
-        return featuresService.getFeature(featureName, featureVersion);
-    }
-
-    protected List<Set<String>> parseBootFeatures(String bootFeatures) {
-        Pattern pattern = Pattern.compile("(\\((.+))\\),|.+");
-        Matcher matcher = pattern.matcher(bootFeatures);
-        List<Set<String>> result = new ArrayList<Set<String>>();
-        while (matcher.find()) {
-            String group = matcher.group(2) != null ? matcher.group(2) : matcher.group();
-            result.add(parseFeatureList(group));
-        }
-        return result;
-    }
-
-    protected Set<String> parseFeatureList(String group) {
-        HashSet<String> features = new HashSet<String>();
-        for (String feature : Arrays.asList(group.trim().split("\\s*,\\s*"))) {
-            if (feature.length() > 0) {
-                features.add(feature);
-            }
-        }
-        return features;
-    }
-
-    private void publishBootFinished() {
-        if (bundleContext != null) {
-            BootFinished bootFinished = new BootFinished() {};
-            bundleContext.registerService(BootFinished.class, bootFinished, new Hashtable<String, String>());
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/src/main/java/org/apache/karaf/features/internal/service/EventAdminListener.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/features/internal/service/EventAdminListener.java b/features/src/main/java/org/apache/karaf/features/internal/service/EventAdminListener.java
deleted file mode 100644
index b6eaae5..0000000
--- a/features/src/main/java/org/apache/karaf/features/internal/service/EventAdminListener.java
+++ /dev/null
@@ -1,91 +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.karaf.features.internal.service;
-
-import java.util.Dictionary;
-import java.util.Hashtable;
-
-import org.apache.karaf.features.EventConstants;
-import org.apache.karaf.features.FeatureEvent;
-import org.apache.karaf.features.FeaturesListener;
-import org.apache.karaf.features.RepositoryEvent;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventAdmin;
-import org.osgi.util.tracker.ServiceTracker;
-
-/**
- * A listener to publish events to EventAdmin
- */
-public class EventAdminListener implements FeaturesListener {
-
-    private final ServiceTracker<EventAdmin, EventAdmin> tracker;
-
-    public EventAdminListener(BundleContext context) {
-        tracker = new ServiceTracker<EventAdmin, EventAdmin>(context, EventAdmin.class.getName(), null);
-        tracker.open();
-    }
-
-    public void featureEvent(FeatureEvent event) {
-        EventAdmin eventAdmin = tracker.getService();
-        if (eventAdmin == null) {
-            return;
-        }
-        Dictionary<String, Object> props = new Hashtable<String, Object>();
-        props.put(EventConstants.TYPE, event.getType());
-        props.put(EventConstants.EVENT, event);
-        props.put(EventConstants.TIMESTAMP, System.currentTimeMillis());
-        props.put(EventConstants.FEATURE_NAME, event.getFeature().getName());
-        props.put(EventConstants.FEATURE_VERSION, event.getFeature().getVersion());
-        String topic;
-        switch (event.getType()) {
-            case FeatureInstalled:
-                topic = EventConstants.TOPIC_FEATURES_INSTALLED;
-                break;
-            case FeatureUninstalled:
-                topic = EventConstants.TOPIC_FEATURES_UNINSTALLED;
-                break;
-            default:
-                throw new IllegalStateException("Unknown features event type: " + event.getType());
-        }
-        eventAdmin.postEvent(new Event(topic, props));
-    }
-
-    public void repositoryEvent(RepositoryEvent event) {
-        EventAdmin eventAdmin = tracker.getService();
-        if (eventAdmin == null) {
-            return;
-        }
-        Dictionary<String, Object> props = new Hashtable<String, Object>();
-        props.put(EventConstants.TYPE, event.getType());
-        props.put(EventConstants.EVENT, event);
-        props.put(EventConstants.TIMESTAMP, System.currentTimeMillis());
-        props.put(EventConstants.REPOSITORY_URI, event.getRepository().getURI().toString());
-        String topic;
-        switch (event.getType()) {
-            case RepositoryAdded:
-                topic = EventConstants.TOPIC_REPOSITORY_ADDED;
-                break;
-            case RepositoryRemoved:
-                topic = EventConstants.TOPIC_REPOSITORY_REMOVED;
-                break;
-            default:
-                throw new IllegalStateException("Unknown repository event type: " + event.getType());
-        }
-        eventAdmin.postEvent(new Event(topic, props));
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java b/features/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java
deleted file mode 100644
index 0e9038d..0000000
--- a/features/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java
+++ /dev/null
@@ -1,167 +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.karaf.features.internal.service;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Dictionary;
-import java.util.Hashtable;
-
-import org.apache.karaf.features.ConfigFileInfo;
-import org.apache.karaf.features.Feature;
-import org.osgi.framework.Constants;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class FeatureConfigInstaller {
-	private static final Logger LOGGER = LoggerFactory.getLogger(FeaturesServiceImpl.class);
-    private static final String CONFIG_KEY = "org.apache.karaf.features.configKey";
-
-    private final ConfigurationAdmin configAdmin;
-    
-    public FeatureConfigInstaller(ConfigurationAdmin configAdmin) {
-		this.configAdmin = configAdmin;
-	}
-
-    private String[] parsePid(String pid) {
-        int n = pid.indexOf('-');
-        if (n > 0) {
-            String factoryPid = pid.substring(n + 1);
-            pid = pid.substring(0, n);
-            return new String[]{pid, factoryPid};
-        } else {
-            return new String[]{pid, null};
-        }
-    }
-
-    private Configuration createConfiguration(ConfigurationAdmin configurationAdmin,
-                                                String pid, String factoryPid) throws IOException, InvalidSyntaxException {
-        if (factoryPid != null) {
-            return configurationAdmin.createFactoryConfiguration(factoryPid, null);
-        } else {
-            return configurationAdmin.getConfiguration(pid, null);
-        }
-    }
-
-    private Configuration findExistingConfiguration(ConfigurationAdmin configurationAdmin,
-                                                      String pid, String factoryPid) throws IOException, InvalidSyntaxException {
-        String filter;
-        if (factoryPid == null) {
-            filter = "(" + Constants.SERVICE_PID + "=" + pid + ")";
-        } else {
-            String key = createConfigurationKey(pid, factoryPid);
-            filter = "(" + CONFIG_KEY + "=" + key + ")";
-        }
-        Configuration[] configurations = configurationAdmin.listConfigurations(filter);
-        if (configurations != null && configurations.length > 0) {
-            return configurations[0];
-        }
-        return null;
-    }
-
-    void installFeatureConfigs(Feature feature) throws IOException, InvalidSyntaxException {
-        for (String config : feature.getConfigurations().keySet()) {
-            Dictionary<String,String> props = new Hashtable<String, String>(feature.getConfigurations().get(config));
-            String[] pid = parsePid(config);
-            Configuration cfg = findExistingConfiguration(configAdmin, pid[0], pid[1]);
-            if (cfg == null) {
-                cfg = createConfiguration(configAdmin, pid[0], pid[1]);
-                String key = createConfigurationKey(pid[0], pid[1]);
-                props.put(CONFIG_KEY, key);
-                if (cfg.getBundleLocation() != null) {
-                    cfg.setBundleLocation(null);
-                }
-                cfg.update(props);
-            }
-        }
-        for (ConfigFileInfo configFile : feature.getConfigurationFiles()) {
-            installConfigurationFile(configFile.getLocation(), configFile.getFinalname(), configFile.isOverride());
-        }
-    }
-
-    private String createConfigurationKey(String pid, String factoryPid) {
-        return factoryPid == null ? pid : pid + "-" + factoryPid;
-    }
-
-    private void installConfigurationFile(String fileLocation, String finalname, boolean override) throws IOException {
-    	String basePath = System.getProperty("karaf.base");
-    	
-    	if (finalname.contains("${")) {
-    		//remove any placeholder or variable part, this is not valid.
-    		int marker = finalname.indexOf("}");
-    		finalname = finalname.substring(marker+1);
-    	}
-    	
-    	finalname = basePath + File.separator + finalname;
-    	
-    	File file = new File(finalname); 
-    	if (file.exists()) {
-            if (!override) {
-                LOGGER.debug("Configuration file {} already exist, don't override it", finalname);
-                return;
-            } else {
-                LOGGER.info("Configuration file {} already exist, overriding it", finalname);
-            }
-    	} else {
-            LOGGER.info("Creating configuration file {}", finalname);
-        }
-
-        InputStream is = null;
-        FileOutputStream fop = null;
-        try {
-            is = new BufferedInputStream(new URL(fileLocation).openStream());
-
-            if (!file.exists()) {
-                File parentFile = file.getParentFile();
-                if (parentFile != null)
-                    parentFile.mkdirs();
-                file.createNewFile();
-            }
-
-            fop = new FileOutputStream(file);
-        
-            int bytesRead;
-            byte[] buffer = new byte[1024];
-            
-            while ((bytesRead = is.read(buffer)) != -1) {
-                fop.write(buffer, 0, bytesRead);
-            }
-        } catch (RuntimeException e) {
-            LOGGER.error(e.getMessage());
-            throw e;
-        } catch (MalformedURLException e) {
-        	LOGGER.error(e.getMessage());
-            throw e;
-		} finally {
-			if (is != null)
-				is.close();
-            if (fop != null) {
-			    fop.flush();
-			    fop.close();
-            }
-		}
-            
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/src/main/java/org/apache/karaf/features/internal/service/FeatureFinder.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/features/internal/service/FeatureFinder.java b/features/src/main/java/org/apache/karaf/features/internal/service/FeatureFinder.java
deleted file mode 100644
index d6defe0..0000000
--- a/features/src/main/java/org/apache/karaf/features/internal/service/FeatureFinder.java
+++ /dev/null
@@ -1,68 +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.karaf.features.internal.service;
-
-import java.net.URI;
-import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-import org.osgi.service.cm.ConfigurationException;
-import org.osgi.service.cm.ManagedService;
-
-public class FeatureFinder implements ManagedService {
-
-    final Map<String, String> nameToArtifactMap = new HashMap<String, String>();
-
-    public String[] getNames() {
-        synchronized (nameToArtifactMap) {
-            Set<String> strings = nameToArtifactMap.keySet();
-            return strings.toArray(new String[strings.size()]);
-        }
-    }
-
-    public URI getUriFor(String name, String version) {
-        String coords;
-        synchronized (nameToArtifactMap) {
-            coords = nameToArtifactMap.get(name);
-        }
-        if (coords == null) {
-            return null;
-        }
-        Artifact artifact = new Artifact(coords);
-        return artifact.getMavenUrl(version);
-    }
-
-    @SuppressWarnings("rawtypes")
-    public void updated(Dictionary properties) throws ConfigurationException {
-        synchronized (nameToArtifactMap) {
-            if (properties != null) {
-                nameToArtifactMap.clear();
-                Enumeration keys = properties.keys();
-                while (keys.hasMoreElements()) {
-                    String key = (String) keys.nextElement();
-                    if (!"felix.fileinstall.filename".equals(key) && !"service.pid".equals(key)) {
-                        nameToArtifactMap.put(key, (String) properties.get(key));
-                    }
-                }
-            }
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/src/main/java/org/apache/karaf/features/internal/service/FeatureValidationUtil.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/features/internal/service/FeatureValidationUtil.java b/features/src/main/java/org/apache/karaf/features/internal/service/FeatureValidationUtil.java
deleted file mode 100644
index 6903dc0..0000000
--- a/features/src/main/java/org/apache/karaf/features/internal/service/FeatureValidationUtil.java
+++ /dev/null
@@ -1,37 +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.karaf.features.internal.service;
-
-import java.net.URI;
-
-import org.apache.karaf.features.internal.model.JaxbUtil;
-
-/**
- * Utility class which fires XML Schema validation.
- */
-public class FeatureValidationUtil {
-
-    /**
-     * Runs schema validation.
-     * 
-     * @param uri Uri to validate.
-     * @throws Exception When validation fails.
-     */
-    public static void validate(URI uri) throws Exception {
-        JaxbUtil.unmarshal(uri.toASCIIString(), true);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java b/features/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
deleted file mode 100644
index 0243dc0..0000000
--- a/features/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
+++ /dev/null
@@ -1,1416 +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.karaf.features.internal.service;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-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.Callable;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import org.apache.felix.utils.version.VersionRange;
-import org.apache.felix.utils.version.VersionTable;
-import org.apache.karaf.features.BundleInfo;
-import org.apache.karaf.features.Feature;
-import org.apache.karaf.features.FeatureEvent;
-import org.apache.karaf.features.FeaturesListener;
-import org.apache.karaf.features.FeaturesService;
-import org.apache.karaf.features.Repository;
-import org.apache.karaf.features.RepositoryEvent;
-import org.apache.karaf.features.internal.deployment.DeploymentBuilder;
-import org.apache.karaf.features.internal.deployment.StreamProvider;
-import org.apache.karaf.features.internal.resolver.FeatureNamespace;
-import org.apache.karaf.features.internal.resolver.UriNamespace;
-import org.apache.karaf.features.internal.util.ChecksumUtils;
-import org.apache.karaf.features.internal.util.Macro;
-import org.apache.karaf.features.internal.util.MultiException;
-import org.apache.karaf.util.collections.CopyOnWriteArrayIdentityList;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.Constants;
-import org.osgi.framework.FrameworkEvent;
-import org.osgi.framework.FrameworkListener;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.Version;
-import org.osgi.framework.startlevel.BundleStartLevel;
-import org.osgi.framework.wiring.BundleRevision;
-import org.osgi.framework.wiring.BundleWire;
-import org.osgi.framework.wiring.BundleWiring;
-import org.osgi.framework.wiring.FrameworkWiring;
-import org.osgi.resource.Resource;
-import org.osgi.resource.Wire;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.apache.felix.resolver.Util.getSymbolicName;
-import static org.apache.felix.resolver.Util.getVersion;
-
-/**
- *
- */
-public class FeaturesServiceImpl implements FeaturesService {
-
-    public static final String UPDATE_SNAPSHOTS_NONE = "none";
-    public static final String UPDATE_SNAPSHOTS_CRC = "crc";
-    public static final String UPDATE_SNAPSHOTS_ALWAYS = "always";
-    public static final String DEFAULT_UPDATE_SNAPSHOTS = UPDATE_SNAPSHOTS_CRC;
-
-    public static final String DEFAULT_FEATURE_RESOLUTION_RANGE = "${range;[====,====]}";
-    public static final String DEFAULT_BUNDLE_UPDATE_RANGE = "${range;[==,=+)}";
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(FeaturesServiceImpl.class);
-    private static final String SNAPSHOT = "SNAPSHOT";
-    private static final String MAVEN = "mvn:";
-
-    /**
-     * Our bundle.
-     * We use it to check bundle operations affecting our own bundle.
-     */
-    private final Bundle bundle;
-
-    /**
-     * 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.
-     */
-    private final StateStorage storage;
-    private final FeatureFinder featureFinder;
-    private final EventAdminListener eventAdminListener;
-    private final FeatureConfigInstaller configInstaller;
-    private final String overrides;
-    /**
-     * Range to use when a version is specified on a feature dependency.
-     * The default is {@link FeaturesServiceImpl#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 FeaturesServiceImpl#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;
-
-    private final List<FeaturesListener> listeners = new CopyOnWriteArrayIdentityList<FeaturesListener>();
-
-    // Synchronized on lock
-    private final Object lock = new Object();
-    private final State state = new State();
-    private final Map<String, Repository> repositoryCache = new HashMap<String, Repository>();
-    private Map<String, Map<String, Feature>> featureCache;
-
-
-    public FeaturesServiceImpl(Bundle bundle,
-                               BundleContext systemBundleContext,
-                               StateStorage storage,
-                               FeatureFinder featureFinder,
-                               EventAdminListener eventAdminListener,
-                               FeatureConfigInstaller configInstaller,
-                               String overrides,
-                               String featureResolutionRange,
-                               String bundleUpdateRange,
-                               String updateSnaphots) {
-        this.bundle = bundle;
-        this.systemBundleContext = systemBundleContext;
-        this.storage = storage;
-        this.featureFinder = featureFinder;
-        this.eventAdminListener = eventAdminListener;
-        this.configInstaller = configInstaller;
-        this.overrides = overrides;
-        this.featureResolutionRange = featureResolutionRange;
-        this.bundleUpdateRange = bundleUpdateRange;
-        this.updateSnaphots = updateSnaphots;
-        loadState();
-    }
-
-    //
-    // State support
-    //
-
-    protected void loadState() {
-        try {
-            synchronized (lock) {
-                storage.load(state);
-            }
-        } catch (IOException e) {
-            LOGGER.warn("Error loading FeaturesService state", e);
-        }
-    }
-
-    protected void saveState() {
-        try {
-            synchronized (lock) {
-                // 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)) {
-                    state.bundleChecksums.clear();
-                }
-                storage.save(state);
-            }
-        } catch (IOException e) {
-            LOGGER.warn("Error saving FeaturesService state", e);
-        }
-    }
-
-    boolean isBootDone() {
-        synchronized (lock) {
-            return state.bootDone.get();
-        }
-    }
-
-    void bootDone() {
-        synchronized (lock) {
-            state.bootDone.set(true);
-            saveState();
-        }
-    }
-
-    //
-    // Listeners support
-    //
-
-    public void registerListener(FeaturesListener listener) {
-        listeners.add(listener);
-        try {
-            Set<String> repositories = new TreeSet<String>();
-            Set<String> installedFeatures = new TreeSet<String>();
-            synchronized (lock) {
-                repositories.addAll(state.repositories);
-                installedFeatures.addAll(state.installedFeatures);
-            }
-            for (String uri : repositories) {
-                Repository repository = new RepositoryImpl(URI.create(uri));
-                listener.repositoryEvent(new RepositoryEvent(repository, RepositoryEvent.EventType.RepositoryAdded, true));
-            }
-            for (String id : installedFeatures) {
-                Feature feature = org.apache.karaf.features.internal.model.Feature.valueOf(id);
-                listener.featureEvent(new FeatureEvent(feature, FeatureEvent.EventType.FeatureInstalled, true));
-            }
-        } catch (Exception e) {
-            LOGGER.error("Error notifying listener about the current state", e);
-        }
-    }
-
-    public void unregisterListener(FeaturesListener listener) {
-        listeners.remove(listener);
-    }
-
-    protected void callListeners(FeatureEvent event) {
-        if (eventAdminListener != null) {
-            eventAdminListener.featureEvent(event);
-        }
-        for (FeaturesListener listener : listeners) {
-            listener.featureEvent(event);
-        }
-    }
-
-    protected void callListeners(RepositoryEvent event) {
-        if (eventAdminListener != null) {
-            eventAdminListener.repositoryEvent(event);
-        }
-        for (FeaturesListener listener : listeners) {
-            listener.repositoryEvent(event);
-        }
-    }
-
-    //
-    // Feature Finder support
-    //
-
-    @Override
-    public URI getRepositoryUriFor(String name, String version) {
-        return featureFinder.getUriFor(name, version);
-    }
-
-    @Override
-    public String[] getRepositoryNames() {
-        return featureFinder.getNames();
-    }
-
-
-    //
-    // Repositories support
-    //
-
-    public Repository loadRepository(URI uri) throws Exception {
-        RepositoryImpl repo = new RepositoryImpl(uri);
-        repo.load(true);
-        return repo;
-    }
-
-    @Override
-    public void validateRepository(URI uri) throws Exception {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void addRepository(URI uri) throws Exception {
-        addRepository(uri, false);
-    }
-
-    @Override
-    public void addRepository(URI uri, boolean install) throws Exception {
-        if (install) {
-            // TODO: implement
-            throw new UnsupportedOperationException();
-        }
-        Repository repository = loadRepository(uri);
-        synchronized (lock) {
-            // Clean cache
-            repositoryCache.put(uri.toString(), repository);
-            featureCache = null;
-            // Add repo
-            if (!state.repositories.add(uri.toString())) {
-                return;
-            }
-            saveState();
-        }
-        callListeners(new RepositoryEvent(repository, RepositoryEvent.EventType.RepositoryAdded, false));
-    }
-
-    @Override
-    public void removeRepository(URI uri) throws Exception {
-        removeRepository(uri, true);
-    }
-
-    @Override
-    public void removeRepository(URI uri, boolean uninstall) throws Exception {
-        // TODO: check we don't have any feature installed from this repository
-        Repository repo;
-        synchronized (lock) {
-            // Remove repo
-            if (!state.repositories.remove(uri.toString())) {
-                return;
-            }
-            // Clean cache
-            featureCache = null;
-            repo = repositoryCache.get(uri.toString());
-            List<String> toRemove = new ArrayList<String>();
-            toRemove.add(uri.toString());
-            while (!toRemove.isEmpty()) {
-                Repository rep = repositoryCache.remove(toRemove.remove(0));
-                if (rep != null) {
-                    for (URI u : rep.getRepositories()) {
-                        toRemove.add(u.toString());
-                    }
-                }
-            }
-            saveState();
-        }
-        if (repo == null) {
-            repo = new RepositoryImpl(uri);
-        }
-        callListeners(new RepositoryEvent(repo, RepositoryEvent.EventType.RepositoryRemoved, false));
-    }
-
-    @Override
-    public void restoreRepository(URI uri) throws Exception {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void refreshRepository(URI uri) throws Exception {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Repository[] listRepositories() throws Exception {
-        // Make sure the cache is loaded
-        getFeatures();
-        synchronized (lock) {
-            return repositoryCache.values().toArray(new Repository[repositoryCache.size()]);
-        }
-    }
-
-    @Override
-    public Repository[] listRequiredRepositories() throws Exception {
-        // Make sure the cache is loaded
-        getFeatures();
-        synchronized (lock) {
-            List<Repository> repos = new ArrayList<Repository>();
-            for (Map.Entry<String, Repository> entry : repositoryCache.entrySet()) {
-                if (state.repositories.contains(entry.getKey())) {
-                    repos.add(entry.getValue());
-                }
-            }
-            return repos.toArray(new Repository[repos.size()]);
-        }
-    }
-
-    @Override
-    public Repository getRepository(String name) throws Exception {
-        // Make sure the cache is loaded
-        getFeatures();
-        synchronized (lock) {
-            for (Repository repo : this.repositoryCache.values()) {
-                if (name.equals(repo.getName())) {
-                    return repo;
-                }
-            }
-            return null;
-        }
-    }
-
-    //
-    // Features support
-    //
-
-    public Feature getFeature(String name) throws Exception {
-        return getFeature(name, null);
-    }
-
-    public Feature getFeature(String name, String version) throws Exception {
-        Map<String, Feature> versions = getFeatures().get(name);
-        return getFeatureMatching(versions, version);
-    }
-
-    protected Feature getFeatureMatching(Map<String, Feature> versions, String version) {
-        if (version != null) {
-            version = version.trim();
-            if (version.equals(org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION)) {
-                version = "";
-            }
-        } else {
-            version = "";
-        }
-        if (versions == null || versions.isEmpty()) {
-            return null;
-        } else {
-            Feature feature = version.isEmpty() ? null : versions.get(version);
-            if (feature == null) {
-                // Compute version range. If an version has been given, assume exact range
-                VersionRange versionRange = version.isEmpty() ?
-                        new VersionRange(Version.emptyVersion) :
-                        new VersionRange(version, true, true);
-                Version latest = Version.emptyVersion;
-                for (String available : versions.keySet()) {
-                    Version availableVersion = VersionTable.getVersion(available);
-                    if (availableVersion.compareTo(latest) >= 0 && versionRange.contains(availableVersion)) {
-                        feature = versions.get(available);
-                        latest = availableVersion;
-                    }
-                }
-            }
-            return feature;
-        }
-    }
-
-    public Feature[] listFeatures() throws Exception {
-        Set<Feature> features = new HashSet<Feature>();
-        for (Map<String, Feature> featureWithDifferentVersion : getFeatures().values()) {
-            for (Feature f : featureWithDifferentVersion.values()) {
-                features.add(f);
-            }
-        }
-        return features.toArray(new Feature[features.size()]);
-    }
-
-    protected Map<String, Map<String, Feature>> getFeatures() throws Exception {
-        List<String> uris;
-        synchronized (lock) {
-            if (featureCache != null) {
-                return featureCache;
-            }
-            uris = new ArrayList<String>(state.repositories);
-        }
-        //the outer map's key is feature name, the inner map's key is feature version
-        Map<String, Map<String, Feature>> map = new HashMap<String, Map<String, Feature>>();
-        // Two phase load:
-        // * first load dependent repositories
-        List<String> toLoad = new ArrayList<String>(uris);
-        while (!toLoad.isEmpty()) {
-            String uri = toLoad.remove(0);
-            Repository repo;
-            synchronized (lock) {
-                repo = repositoryCache.get(uri);
-            }
-            if (repo == null) {
-                RepositoryImpl rep = new RepositoryImpl(URI.create(uri));
-                rep.load();
-                repo = rep;
-                synchronized (lock) {
-                    repositoryCache.put(uri, repo);
-                }
-            }
-            for (URI u : repo.getRepositories()) {
-                toLoad.add(u.toString());
-            }
-        }
-        List<Repository> repos;
-        synchronized (lock) {
-            repos = new ArrayList<Repository>(repositoryCache.values());
-        }
-        // * then load all features
-        for (Repository repo : repos) {
-            for (Feature f : repo.getFeatures()) {
-                if (map.get(f.getName()) == null) {
-                    Map<String, Feature> versionMap = new HashMap<String, Feature>();
-                    versionMap.put(f.getVersion(), f);
-                    map.put(f.getName(), versionMap);
-                } else {
-                    map.get(f.getName()).put(f.getVersion(), f);
-                }
-            }
-        }
-        synchronized (lock) {
-            if (uris.size() == state.repositories.size() &&
-                    state.repositories.containsAll(uris)) {
-                featureCache = map;
-            }
-        }
-        return map;
-    }
-
-    //
-    // Installed features
-    //
-
-    @Override
-    public Feature[] listInstalledFeatures() throws Exception {
-        Set<Feature> features = new HashSet<Feature>();
-        Map<String, Map<String, Feature>> allFeatures = getFeatures();
-        synchronized (lock) {
-            for (Map<String, Feature> featureWithDifferentVersion : allFeatures.values()) {
-                for (Feature f : featureWithDifferentVersion.values()) {
-                    if (isInstalled(f)) {
-                        features.add(f);
-                    }
-                }
-            }
-        }
-        return features.toArray(new Feature[features.size()]);
-    }
-
-    @Override
-    public Feature[] listRequiredFeatures() throws Exception {
-        Set<Feature> features = new HashSet<Feature>();
-        Map<String, Map<String, Feature>> allFeatures = getFeatures();
-        synchronized (lock) {
-            for (Map<String, Feature> featureWithDifferentVersion : allFeatures.values()) {
-                for (Feature f : featureWithDifferentVersion.values()) {
-                    if (isRequired(f)) {
-                        features.add(f);
-                    }
-                }
-            }
-        }
-        return features.toArray(new Feature[features.size()]);
-    }
-
-
-    @Override
-    public boolean isInstalled(Feature f) {
-        String id = normalize(f.getId());
-        synchronized (lock) {
-            return state.installedFeatures.contains(id);
-        }
-    }
-
-    @Override
-    public boolean isRequired(Feature f) {
-        String id = normalize(f.getId());
-        synchronized (lock) {
-            return state.features.contains(id);
-        }
-    }
-
-    //
-    // Installation and uninstallation of features
-    //
-
-    public void installFeature(String name) throws Exception {
-        installFeature(name, EnumSet.noneOf(Option.class));
-    }
-
-    public void installFeature(String name, String version) throws Exception {
-        installFeature(version != null ? name + "/" + version : name, EnumSet.noneOf(Option.class));
-    }
-
-    public void installFeature(String name, EnumSet<Option> options) throws Exception {
-        installFeatures(Collections.singleton(name), options);
-    }
-
-    public void installFeature(String name, String version, EnumSet<Option> options) throws Exception {
-        installFeature(version != null ? name + "/" + version : name, options);
-    }
-
-    public void installFeature(Feature feature, EnumSet<Option> options) throws Exception {
-        installFeature(feature.getId());
-    }
-
-    @Override
-    public void uninstallFeature(String name, String version) throws Exception {
-        uninstallFeature(version != null ? name + "/" + version : name);
-    }
-
-    @Override
-    public void uninstallFeature(String name, String version, EnumSet<Option> options) throws Exception {
-        uninstallFeature(version != null ? name + "/" + version : name, options);
-    }
-
-    @Override
-    public void uninstallFeature(String name) throws Exception {
-        uninstallFeature(name, EnumSet.noneOf(Option.class));
-    }
-
-    @Override
-    public void uninstallFeature(String name, EnumSet<Option> options) throws Exception {
-        uninstallFeatures(Collections.singleton(name), options);
-    }
-
-
-    //
-    //
-    //
-    //   RESOLUTION
-    //
-    //
-    //
-
-
-
-
-
-
-    public void installFeatures(Set<String> features, EnumSet<Option> options) throws Exception {
-        Set<String> required;
-        Set<String> installed;
-        Set<Long> managed;
-        synchronized (lock) {
-            required = new HashSet<String>(state.features);
-            installed = new HashSet<String>(state.installedFeatures);
-            managed = new HashSet<Long>(state.managedBundles);
-        }
-        List<String> featuresToAdd = new ArrayList<String>();
-        Map<String, Map<String, Feature>> featuresMap = getFeatures();
-        for (String feature : features) {
-            feature = normalize(feature);
-            String name = feature.substring(0, feature.indexOf("/"));
-            String version = feature.substring(feature.indexOf("/") + 1);
-            Feature f = getFeatureMatching(featuresMap.get(name), version);
-            if (f == null) {
-                if (!options.contains(Option.NoFailOnFeatureNotFound)) {
-                    throw new IllegalArgumentException("No matching features for " + feature);
-                }
-            } else {
-                featuresToAdd.add(normalize(f.getId()));
-            }
-        }
-        featuresToAdd = new ArrayList<String>(new LinkedHashSet<String>(featuresToAdd));
-        StringBuilder sb = new StringBuilder();
-        sb.append("Adding features: ");
-        for (int i = 0; i < featuresToAdd.size(); i++) {
-            if (i > 0) {
-                sb.append(", ");
-            }
-            sb.append(featuresToAdd.get(i));
-        }
-        print(sb.toString(), options.contains(Option.Verbose));
-        required.addAll(featuresToAdd);
-        doInstallFeaturesInThread(required, installed, managed, options);
-    }
-
-    public void uninstallFeatures(Set<String> features, EnumSet<Option> options) throws Exception {
-        Set<String> required;
-        Set<String> installed;
-        Set<Long> managed;
-        synchronized (lock) {
-            required = new HashSet<String>(state.features);
-            installed = new HashSet<String>(state.installedFeatures);
-            managed = new HashSet<Long>(state.managedBundles);
-        }
-        List<String> featuresToRemove = new ArrayList<String>();
-        for (String feature : new HashSet<String>(features)) {
-            List<String> toRemove = new ArrayList<String>();
-            feature = normalize(feature);
-            if (feature.endsWith("/0.0.0")) {
-                String nameSep = feature.substring(0, feature.indexOf("/") + 1);
-                for (String f : required) {
-                    if (normalize(f).startsWith(nameSep)) {
-                        toRemove.add(f);
-                    }
-                }
-            } else {
-                toRemove.add(feature);
-            }
-            toRemove.retainAll(required);
-            if (toRemove.isEmpty()) {
-                throw new IllegalArgumentException("Feature named '" + feature + "' is not installed");
-            } else if (toRemove.size() > 1) {
-                String name = feature.substring(0, feature.indexOf("/"));
-                StringBuilder sb = new StringBuilder();
-                sb.append("Feature named '").append(name).append("' has multiple versions installed (");
-                for (int i = 0; i < toRemove.size(); i++) {
-                    if (i > 0) {
-                        sb.append(", ");
-                    }
-                    sb.append(toRemove.get(i));
-                }
-                sb.append("). Please specify the version to uninstall.");
-                throw new IllegalArgumentException(sb.toString());
-            }
-            featuresToRemove.addAll(toRemove);
-        }
-        featuresToRemove = new ArrayList<String>(new LinkedHashSet<String>(featuresToRemove));
-        StringBuilder sb = new StringBuilder();
-        sb.append("Removing features: ");
-        for (int i = 0; i < featuresToRemove.size(); i++) {
-            if (i > 0) {
-                sb.append(", ");
-            }
-            sb.append(featuresToRemove.get(i));
-        }
-        print(sb.toString(), options.contains(Option.Verbose));
-        required.removeAll(featuresToRemove);
-        doInstallFeaturesInThread(required, installed, managed, options);
-    }
-
-    protected String normalize(String feature) {
-        if (!feature.contains("/")) {
-            feature += "/0.0.0";
-        }
-        int idx = feature.indexOf("/");
-        String name = feature.substring(0, idx);
-        String version = feature.substring(idx + 1);
-        return name + "/" + VersionTable.getVersion(version).toString();
-    }
-
-    /**
-     * Actual deployment needs to be done in a separate thread.
-     * The reason is that if the console is refreshed, the current thread which is running
-     * the command may be interrupted while waiting for the refresh to be done, leading
-     * to bundles not being started after the refresh.
-     */
-    public void doInstallFeaturesInThread(final Set<String> features,
-                                          final Set<String> installed,
-                                          final Set<Long> managed,
-                                          final EnumSet<Option> options) throws Exception {
-        ExecutorService executor = Executors.newCachedThreadPool();
-        try {
-            executor.submit(new Callable<Object>() {
-                @Override
-                public Object call() throws Exception {
-                    doInstallFeatures(features, installed, managed, options);
-                    return null;
-                }
-            }).get();
-        } catch (ExecutionException e) {
-            Throwable t = e.getCause();
-            if (t instanceof RuntimeException) {
-                throw ((RuntimeException) t);
-            } else if (t instanceof Error) {
-                throw ((Error) t);
-            } else if (t instanceof Exception) {
-                throw (Exception) t;
-            } else {
-                throw e;
-            }
-        } finally {
-            executor.shutdown();
-        }
-    }
-
-    public void doInstallFeatures(Set<String> features,    // all request features
-                                  Set<String> installed,   // installed features
-                                  Set<Long> managed,       // currently managed bundles
-                                  EnumSet<Option> options  // installation options
-                    ) throws Exception {
-
-        boolean noRefreshUnmanaged = options.contains(Option.NoAutoRefreshUnmanagedBundles);
-        boolean noRefreshManaged = options.contains(Option.NoAutoRefreshManagedBundles);
-        boolean noRefresh = options.contains(Option.NoAutoRefreshBundles);
-        boolean noStart = options.contains(Option.NoAutoStartBundles);
-        boolean verbose = options.contains(Option.Verbose);
-        boolean simulate = options.contains(Option.Simulate);
-
-        // Get a list of resolved and unmanaged bundles to use as capabilities during resolution
-        List<Resource> systemBundles = new ArrayList<Resource>();
-        Bundle[] bundles = systemBundleContext.getBundles();
-        for (Bundle bundle : bundles) {
-            if (bundle.getState() >= Bundle.RESOLVED && !managed.contains(bundle.getBundleId())) {
-                Resource res = bundle.adapt(BundleRevision.class);
-                systemBundles.add(res);
-            }
-        }
-        // Resolve
-        // TODO: requirements
-        // TODO: bundles
-        // TODO: regions: on isolated regions, we may need different resolution for each region
-        Set<String>  overrides    = Overrides.loadOverrides(this.overrides);
-        Repository[] repositories = listRepositories();
-        DeploymentBuilder builder = createDeploymentBuilder(repositories);
-        builder.setFeatureRange(featureResolutionRange);
-        builder.download(features,
-                         Collections.<String>emptySet(),
-                         Collections.<String>emptySet(),
-                         overrides,
-                         Collections.<String>emptySet());
-        Map<Resource, List<Wire>> resolution = builder.resolve(systemBundles);
-        Collection<Resource> allResources = resolution.keySet();
-        Map<String, StreamProvider> providers = builder.getProviders();
-
-        // Install conditionals
-        List<String> installedFeatureIds = getFeatureIds(allResources);
-        List<String> newFeatures = new ArrayList<String>(installedFeatureIds);
-        newFeatures.removeAll(installed);
-        List<String> delFeatures = new ArrayList<String>(installed);
-        delFeatures.removeAll(installedFeatureIds);
-
-        //
-        // Compute list of installable resources (those with uris)
-        //
-        List<Resource> resources = getBundles(allResources);
-
-        // Compute information for each bundle
-        Map<String, BundleInfo> bundleInfos = new HashMap<String, BundleInfo>();
-        for (Feature feature : getFeatures(repositories, getFeatureIds(allResources))) {
-            for (BundleInfo bi : feature.getBundles()) {
-                BundleInfo oldBi = bundleInfos.get(bi.getLocation());
-                if (oldBi != null) {
-                    bi = mergeBundleInfo(bi, oldBi);
-                }
-                bundleInfos.put(bi.getLocation(), bi);
-            }
-        }
-
-        // TODO: handle bundleInfo.isStart()
-
-        // Get all resources that will be used to satisfy the old features set
-        Set<Resource> resourceLinkedToOldFeatures = new HashSet<Resource>();
-        if (noStart) {
-            for (Resource resource : resolution.keySet()) {
-                String name = FeatureNamespace.getName(resource);
-                if (name != null) {
-                    Version version = FeatureNamespace.getVersion(resource);
-                    String id = version != null ? name + "/" + version : name;
-                    if (installed.contains(id)) {
-                        addTransitive(resource, resourceLinkedToOldFeatures, resolution);
-                    }
-                }
-            }
-        }
-
-        //
-        // Compute deployment
-        //
-        Map<String, Long> bundleChecksums = new HashMap<String, Long>();
-        synchronized (lock) {
-            bundleChecksums.putAll(state.bundleChecksums);
-        }
-        Deployment deployment = computeDeployment(managed, bundles, providers, resources, bundleChecksums);
-
-        if (deployment.toDelete.isEmpty() &&
-                deployment.toUpdate.isEmpty() &&
-                deployment.toInstall.isEmpty()) {
-            print("No deployment change.", verbose);
-            return;
-        }
-        //
-        // Log deployment
-        //
-        logDeployment(deployment, verbose);
-
-        //
-        // Compute the set of bundles to refresh
-        //
-        Set<Bundle> toRefresh = new HashSet<Bundle>();
-        toRefresh.addAll(deployment.toDelete);
-        toRefresh.addAll(deployment.toUpdate.keySet());
-
-        if (!noRefreshManaged) {
-            int size;
-            do {
-                size = toRefresh.size();
-                for (Bundle bundle : bundles) {
-                    // Continue if we already know about this bundle
-                    if (toRefresh.contains(bundle)) {
-                        continue;
-                    }
-                    // Ignore non resolved bundle
-                    BundleWiring wiring = bundle.adapt(BundleWiring.class);
-                    if (wiring == null) {
-                        continue;
-                    }
-                    // Get through the old resolution and flag this bundle
-                    // if it was wired to a bundle to be refreshed
-                    for (BundleWire wire : wiring.getRequiredWires(null)) {
-                        if (toRefresh.contains(wire.getProvider().getBundle())) {
-                            toRefresh.add(bundle);
-                            break;
-                        }
-                    }
-                    // Get through the new resolution and flag this bundle
-                    // if it's wired to any new bundle
-                    List<Wire> newWires = resolution.get(wiring.getRevision());
-                    if (newWires != null) {
-                        for (Wire wire : newWires) {
-                            Bundle b = null;
-                            if (wire.getProvider() instanceof BundleRevision) {
-                                b = ((BundleRevision) wire.getProvider()).getBundle();
-                            } else {
-                                b = deployment.resToBnd.get(wire.getProvider());
-                            }
-                            if (b == null || toRefresh.contains(b)) {
-                                toRefresh.add(bundle);
-                                break;
-                            }
-                        }
-                    }
-                }
-            } while (toRefresh.size() > size);
-        }
-        if (noRefreshUnmanaged) {
-            Set<Bundle> newSet = new HashSet<Bundle>();
-            for (Bundle bundle : toRefresh) {
-                if (managed.contains(bundle.getBundleId())) {
-                    newSet.add(bundle);
-                }
-            }
-            toRefresh = newSet;
-        }
-
-
-        if (simulate) {
-            if (!toRefresh.isEmpty()) {
-                print("  Bundles to refresh:", verbose);
-                for (Bundle bundle : toRefresh) {
-                    print("    " + bundle.getSymbolicName() + " / " + bundle.getVersion(), verbose);
-                }
-            }
-            return;
-        }
-
-        Set<Bundle> toStart = new HashSet<Bundle>();
-
-        //
-        // Execute deployment
-        //
-
-        // TODO: handle update on the features service itself
-        if (deployment.toUpdate.containsKey(bundle) ||
-                deployment.toDelete.contains(bundle)) {
-
-            LOGGER.warn("Updating or uninstalling of the FeaturesService is not supported");
-            deployment.toUpdate.remove(bundle);
-            deployment.toDelete.remove(bundle);
-
-        }
-
-        //
-        // Perform bundle operations
-        //
-
-        // Stop bundles by chunks
-        Set<Bundle> toStop = new HashSet<Bundle>();
-        toStop.addAll(deployment.toUpdate.keySet());
-        toStop.addAll(deployment.toDelete);
-        removeFragmentsAndBundlesInState(toStop, Bundle.UNINSTALLED | Bundle.RESOLVED | Bundle.STOPPING);
-        if (!toStop.isEmpty()) {
-            print("Stopping bundles:", verbose);
-            while (!toStop.isEmpty()) {
-                List<Bundle> bs = getBundlesToStop(toStop);
-                for (Bundle bundle : bs) {
-                    print("  " + bundle.getSymbolicName() + " / " + bundle.getVersion(), verbose);
-                    bundle.stop(Bundle.STOP_TRANSIENT);
-                    toStop.remove(bundle);
-                }
-            }
-        }
-        if (!deployment.toDelete.isEmpty()) {
-            print("Uninstalling bundles:", verbose);
-            for (Bundle bundle : deployment.toDelete) {
-                print("  " + bundle.getSymbolicName() + " / " + bundle.getVersion(), verbose);
-                bundle.uninstall();
-                managed.remove(bundle.getBundleId());
-            }
-        }
-        if (!deployment.toUpdate.isEmpty()) {
-            print("Updating bundles:", verbose);
-            for (Map.Entry<Bundle, Resource> entry : deployment.toUpdate.entrySet()) {
-                Bundle bundle = entry.getKey();
-                Resource resource = entry.getValue();
-                String uri = UriNamespace.getUri(resource);
-                print("  " + uri, verbose);
-                InputStream is = getBundleInputStream(resource, providers);
-                bundle.update(is);
-                toStart.add(bundle);
-                BundleInfo bi = bundleInfos.get(uri);
-                if (bi != null && bi.getStartLevel() > 0) {
-                    bundle.adapt(BundleStartLevel.class).setStartLevel(bi.getStartLevel());
-                }
-                // TODO: handle region
-            }
-        }
-        if (!deployment.toInstall.isEmpty()) {
-            print("Installing bundles:", verbose);
-            for (Resource resource : deployment.toInstall) {
-                String uri = UriNamespace.getUri(resource);
-                print("  " + uri, verbose);
-                InputStream is = getBundleInputStream(resource, providers);
-                Bundle bundle = systemBundleContext.installBundle(uri, is);
-                managed.add(bundle.getBundleId());
-                if (!noStart || resourceLinkedToOldFeatures.contains(resource)) {
-                    toStart.add(bundle);
-                }
-                deployment.resToBnd.put(resource, bundle);
-                // save a checksum of installed snapshot bundle
-                if (UPDATE_SNAPSHOTS_CRC.equals(updateSnaphots)
-                        && isUpdateable(resource) && !deployment.newCheckums.containsKey(bundle.getLocation())) {
-                    deployment.newCheckums.put(bundle.getLocation(), ChecksumUtils.checksum(getBundleInputStream(resource, providers)));
-                }
-                BundleInfo bi = bundleInfos.get(uri);
-                if (bi != null && bi.getStartLevel() > 0) {
-                    bundle.adapt(BundleStartLevel.class).setStartLevel(bi.getStartLevel());
-                }
-                // TODO: handle region
-            }
-        }
-
-        //
-        // Update and save state
-        //
-        synchronized (lock) {
-            state.bundleChecksums.putAll(deployment.newCheckums);
-            state.features.clear();
-            state.features.addAll(features);
-            state.installedFeatures.clear();
-            state.installedFeatures.addAll(installedFeatureIds);
-            state.managedBundles.clear();
-            state.managedBundles.addAll(managed);
-            saveState();
-        }
-
-        //
-        // Install configurations
-        //
-        if (configInstaller != null && !newFeatures.isEmpty()) {
-            for (Repository repository : repositories) {
-                for (Feature feature : repository.getFeatures()) {
-                    if (newFeatures.contains(feature.getId())) {
-                        configInstaller.installFeatureConfigs(feature);
-                    }
-                }
-            }
-        }
-
-        // TODO: remove this hack, but it avoids loading the class after the bundle is refreshed
-        new CopyOnWriteArrayIdentityList().iterator();
-        RequirementSort.sort(Collections.<Resource>emptyList());
-
-        if (!noRefresh) {
-            toStop = new HashSet<Bundle>();
-            toStop.addAll(toRefresh);
-            removeFragmentsAndBundlesInState(toStop, Bundle.UNINSTALLED | Bundle.RESOLVED | Bundle.STOPPING);
-            if (!toStop.isEmpty()) {
-                print("Stopping bundles:", verbose);
-                while (!toStop.isEmpty()) {
-                    List<Bundle> bs = getBundlesToStop(toStop);
-                    for (Bundle bundle : bs) {
-                        print("  " + bundle.getSymbolicName() + " / " + bundle.getVersion(), verbose);
-                        bundle.stop(Bundle.STOP_TRANSIENT);
-                        toStop.remove(bundle);
-                        toStart.add(bundle);
-                    }
-                }
-            }
-
-            if (!toRefresh.isEmpty()) {
-                print("Refreshing bundles:", verbose);
-                for (Bundle bundle : toRefresh) {
-                    print("  " + bundle.getSymbolicName() + " / " + bundle.getVersion(), verbose);
-                }
-                if (!toRefresh.isEmpty()) {
-                    refreshPackages(toRefresh);
-                }
-            }
-        }
-
-        // Compute bundles to start
-        removeFragmentsAndBundlesInState(toStart, Bundle.UNINSTALLED | Bundle.ACTIVE | Bundle.STARTING);
-        if (!toStart.isEmpty()) {
-            // Compute correct start order
-            List<Exception> exceptions = new ArrayList<Exception>();
-            print("Starting bundles:", verbose);
-            while (!toStart.isEmpty()) {
-                List<Bundle> bs = getBundlesToStart(toStart);
-                for (Bundle bundle : bs) {
-                    LOGGER.info("  " + bundle.getSymbolicName() + " / " + bundle.getVersion());
-                    try {
-                        bundle.start();
-                    } catch (BundleException e) {
-                        exceptions.add(e);
-                    }
-                    toStart.remove(bundle);
-                }
-            }
-            if (!exceptions.isEmpty()) {
-                throw new MultiException("Error restarting bundles", exceptions);
-            }
-        }
-
-        // Call listeners
-        for (Feature feature : getFeatures(repositories, delFeatures)) {
-            callListeners(new FeatureEvent(feature, FeatureEvent.EventType.FeatureUninstalled, false));
-        }
-        for (Feature feature : getFeatures(repositories, newFeatures)) {
-            callListeners(new FeatureEvent(feature, FeatureEvent.EventType.FeatureInstalled, false));
-        }
-
-        print("Done.", verbose);
-    }
-
-    private void addTransitive(Resource resource, Set<Resource> resources, Map<Resource, List<Wire>> resolution) {
-        if (resources.add(resource)) {
-            for (Wire wire : resolution.get(resource)) {
-                addTransitive(wire.getProvider(), resources, resolution);
-            }
-        }
-    }
-
-    protected BundleInfo mergeBundleInfo(BundleInfo bi, BundleInfo oldBi) {
-        // TODO: we need a proper merge strategy when a bundle
-        // TODO: comes from different features
-        return bi;
-    }
-
-    private void print(String message, boolean verbose) {
-        LOGGER.info(message);
-        if (verbose) {
-            System.out.println(message);
-        }
-    }
-
-    private void removeFragmentsAndBundlesInState(Collection<Bundle> bundles, int state) {
-        for (Bundle bundle : new ArrayList<Bundle>(bundles)) {
-            if ((bundle.getState() & state) != 0
-                     || bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null) {
-                bundles.remove(bundle);
-            }
-        }
-    }
-
-    protected void logDeployment(Deployment deployment, boolean verbose) {
-        print("Changes to perform:", verbose);
-        if (!deployment.toDelete.isEmpty()) {
-            print("  Bundles to uninstall:", verbose);
-            for (Bundle bundle : deployment.toDelete) {
-                print("    " + bundle.getSymbolicName() + " / " + bundle.getVersion(), verbose);
-            }
-        }
-        if (!deployment.toUpdate.isEmpty()) {
-            print("  Bundles to update:", verbose);
-            for (Map.Entry<Bundle, Resource> entry : deployment.toUpdate.entrySet()) {
-                print("    " + entry.getKey().getSymbolicName() + " / " + entry.getKey().getVersion() + " with " + UriNamespace.getUri(entry.getValue()), verbose);
-            }
-        }
-        if (!deployment.toInstall.isEmpty()) {
-            print("  Bundles to install:", verbose);
-            for (Resource resource : deployment.toInstall) {
-                print("    " + UriNamespace.getUri(resource), verbose);
-            }
-        }
-    }
-
-    protected Deployment computeDeployment(
-                                Set<Long> managed,
-                                Bundle[] bundles,
-                                Map<String, StreamProvider> providers,
-                                List<Resource> resources,
-                                Map<String, Long> bundleChecksums) throws IOException {
-        Deployment deployment = new Deployment();
-
-        // TODO: regions
-        List<Resource> toDeploy = new ArrayList<Resource>(resources);
-
-        // First pass: go through all installed bundles and mark them
-        // as either to ignore or delete
-        for (Bundle bundle : bundles) {
-            if (bundle.getSymbolicName() != null && bundle.getBundleId() != 0) {
-                Resource resource = null;
-                for (Resource res : toDeploy) {
-                    if (bundle.getSymbolicName().equals(getSymbolicName(res))) {
-                        if (bundle.getVersion().equals(getVersion(res))) {
-                            resource = res;
-                            break;
-                        }
-                    }
-                }
-                // We found a matching bundle
-                if (resource != null) {
-                    // In case of snapshots, check if the snapshot is out of date
-                    // and flag it as to update
-                    if (managed.contains(bundle.getBundleId()) && isUpdateable(resource)) {
-                        // Always update snapshots
-                        if (UPDATE_SNAPSHOTS_ALWAYS.equalsIgnoreCase(updateSnaphots)) {
-                            LOGGER.debug("Update snapshot for " + bundle.getLocation());
-                            deployment.toUpdate.put(bundle, resource);
-                        }
-                        else if (UPDATE_SNAPSHOTS_CRC.equalsIgnoreCase(updateSnaphots)) {
-                            // if the checksum are different
-                            InputStream is = null;
-                            try {
-                                is = getBundleInputStream(resource, providers);
-                                long newCrc = ChecksumUtils.checksum(is);
-                                long oldCrc = bundleChecksums.containsKey(bundle.getLocation()) ? bundleChecksums.get(bundle.getLocation()) : 0l;
-                                if (newCrc != oldCrc) {
-                                    LOGGER.debug("New snapshot available for " + bundle.getLocation());
-                                    deployment.toUpdate.put(bundle, resource);
-                                    deployment.newCheckums.put(bundle.getLocation(), newCrc);
-                                }
-                            } finally {
-                                if (is != null) {
-                                    is.close();
-                                }
-                            }
-                        }
-                    }
-                    // We're done for this resource
-                    toDeploy.remove(resource);
-                    deployment.resToBnd.put(resource, bundle);
-                // There's no matching resource
-                // If the bundle is managed, we need to delete it
-                } else if (managed.contains(bundle.getBundleId())) {
-                    deployment.toDelete.add(bundle);
-                }
-            }
-        }
-
-        // Second pass on remaining resources
-        for (Resource resource : toDeploy) {
-            TreeMap<Version, Bundle> matching = new TreeMap<Version, Bundle>();
-            VersionRange range = new VersionRange(Macro.transform(bundleUpdateRange, getVersion(resource).toString()));
-            for (Bundle bundle : deployment.toDelete) {
-                if (bundle.getSymbolicName().equals(getSymbolicName(resource)) && range.contains(bundle.getVersion())) {
-                    matching.put(bundle.getVersion(), bundle);
-                }
-            }
-            if (!matching.isEmpty()) {
-                Bundle bundle = matching.lastEntry().getValue();
-                deployment.toUpdate.put(bundle, resource);
-                deployment.toDelete.remove(bundle);
-                deployment.resToBnd.put(resource, bundle);
-            } else {
-                deployment.toInstall.add(resource);
-            }
-        }
-        return deployment;
-    }
-
-    protected List<Resource> getBundles(Collection<Resource> allResources) {
-        Map<String, Resource> deploy = new TreeMap<String, Resource>();
-        for (Resource res : allResources) {
-            String uri = UriNamespace.getUri(res);
-            if (uri != null) {
-                deploy.put(uri, res);
-            }
-        }
-        return new ArrayList<Resource>(deploy.values());
-    }
-
-    protected List<Feature> getFeatures(Repository[] repositories, List<String> featureIds) throws Exception {
-        List<Feature> installedFeatures = new ArrayList<Feature>();
-        for (Repository repository : repositories) {
-            for (Feature feature : repository.getFeatures()) {
-                String id = feature.getName() + "/" + VersionTable.getVersion(feature.getVersion());
-                if (featureIds.contains(id)) {
-                    installedFeatures.add(feature);
-                }
-            }
-        }
-        return installedFeatures;
-    }
-
-    protected List<String> getFeatureIds(Collection<Resource> allResources) {
-        List<String> installedFeatureIds = new ArrayList<String>();
-        for (Resource resource : allResources) {
-            String name = FeatureNamespace.getName(resource);
-            if (name != null) {
-                Version version = FeatureNamespace.getVersion(resource);
-                String id = version != null ? name + "/" + version : name;
-                installedFeatureIds.add(id);
-            }
-        }
-        return installedFeatureIds;
-    }
-
-    protected DeploymentBuilder createDeploymentBuilder(Repository[] repositories) {
-        return new DeploymentBuilder(new SimpleDownloader(), Arrays.asList(repositories));
-    }
-
-
-    protected boolean isUpdateable(Resource resource) {
-        return (getVersion(resource).getQualifier().endsWith(SNAPSHOT) ||
-                UriNamespace.getUri(resource).contains(SNAPSHOT) ||
-                !UriNamespace.getUri(resource).contains(MAVEN));
-    }
-
-    protected List<Bundle> getBundlesToStart(Collection<Bundle> bundles) {
-        // TODO: make this pluggable ?
-        // TODO: honor respectStartLvlDuringFeatureStartup
-
-        // We hit FELIX-2949 if we don't use the correct order as Felix resolver isn't greedy.
-        // In order to minimize that, we make sure we resolve the bundles in the order they
-        // are given back by the resolution, meaning that all root bundles (i.e. those that were
-        // not flagged as dependencies in features) are started before the others.   This should
-        // make sure those important bundles are started first and minimize the problem.
-
-        // Restart the features service last, regardless of any other consideration
-        // so that we don't end up with the service trying to do stuff before we're done
-        boolean restart = bundles.remove(bundle);
-
-        List<BundleRevision> revs = new ArrayList<BundleRevision>();
-        for (Bundle bundle : bundles) {
-            revs.add(bundle.adapt(BundleRevision.class));
-        }
-        List<Bundle> sorted = new ArrayList<Bundle>();
-        for (BundleRevision rev : RequirementSort.sort(revs)) {
-            sorted.add(rev.getBundle());
-        }
-        if (restart) {
-            sorted.add(bundle);
-        }
-        return sorted;
-    }
-
-    protected List<Bundle> getBundlesToStop(Collection<Bundle> bundles) {
-        // TODO: make this pluggable ?
-        // TODO: honor respectStartLvlDuringFeatureUninstall
-
-        List<Bundle> bundlesToDestroy = new ArrayList<Bundle>();
-        for (Bundle bundle : bundles) {
-            ServiceReference[] references = bundle.getRegisteredServices();
-            int usage = 0;
-            if (references != null) {
-                for (ServiceReference reference : references) {
-                    usage += getServiceUsage(reference, bundles);
-                }
-            }
-            LOGGER.debug("Usage for bundle {} is {}", bundle, usage);
-            if (usage == 0) {
-                bundlesToDestroy.add(bundle);
-            }
-        }
-        if (!bundlesToDestroy.isEmpty()) {
-            Collections.sort(bundlesToDestroy, new Comparator<Bundle>() {
-                public int compare(Bundle b1, Bundle b2) {
-                    return (int) (b2.getLastModified() - b1.getLastModified());
-                }
-            });
-            LOGGER.debug("Selected bundles {} for destroy (no services in use)", bundlesToDestroy);
-        } else {
-            ServiceReference ref = null;
-            for (Bundle bundle : bundles) {
-                ServiceReference[] references = bundle.getRegisteredServices();
-                for (ServiceReference reference : references) {
-                    if (getServiceUsage(reference, bundles) == 0) {
-                        continue;
-                    }
-                    if (ref == null || reference.compareTo(ref) < 0) {
-                        LOGGER.debug("Currently selecting bundle {} for destroy (with reference {})", bundle, reference);
-                        ref = reference;
-                    }
-                }
-            }
-            if (ref != null) {
-                bundlesToDestroy.add(ref.getBundle());
-            }
-            LOGGER.debug("Selected bundle {} for destroy (lowest ranking service)", bundlesToDestroy);
-        }
-        return bundlesToDestroy;
-    }
-
-    private static int getServiceUsage(ServiceReference ref, Collection<Bundle> bundles) {
-        Bundle[] usingBundles = ref.getUsingBundles();
-        int nb = 0;
-        if (usingBundles != null) {
-            for (Bundle bundle : usingBundles) {
-                if (bundles.contains(bundle)) {
-                    nb++;
-                }
-            }
-        }
-        return nb;
-    }
-
-    protected InputStream getBundleInputStream(Resource resource, Map<String, StreamProvider> providers) throws IOException {
-        String uri = UriNamespace.getUri(resource);
-        if (uri == null) {
-            throw new IllegalStateException("Resource has no uri");
-        }
-        StreamProvider provider = providers.get(uri);
-        if (provider == null) {
-            throw new IllegalStateException("Resource " + uri + " has no StreamProvider");
-        }
-        return provider.open();
-    }
-
-    protected void refreshPackages(Collection<Bundle> bundles) throws InterruptedException {
-        final CountDownLatch latch = new CountDownLatch(1);
-        FrameworkWiring fw = systemBundleContext.getBundle().adapt(FrameworkWiring.class);
-        fw.refreshBundles(bundles, new FrameworkListener() {
-            @Override
-            public void frameworkEvent(FrameworkEvent event) {
-                if (event.getType() == FrameworkEvent.ERROR) {
-                    LOGGER.error("Framework error", event.getThrowable());
-                }
-                latch.countDown();
-            }
-        });
-        latch.await();
-    }
-
-
-    static class Deployment {
-        Map<String, Long> newCheckums = new HashMap<String, Long>();
-        Map<Resource, Bundle> resToBnd = new HashMap<Resource, Bundle>();
-        List<Resource> toInstall = new ArrayList<Resource>();
-        List<Bundle> toDelete = new ArrayList<Bundle>();
-        Map<Bundle, Resource> toUpdate = new HashMap<Bundle, Resource>();
-    }
-
-}