[KARAF-5272] Enhance the features deployer so that it performs a real upgrade

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

Branch: refs/heads/master
Commit: eb9960695d978b0d6c817202e688a4e4a0f6da45
Parents: bc9adda
Author: Guillaume Nodet <[email protected]>
Authored: Tue Jul 25 15:34:43 2017 +0200
Committer: Guillaume Nodet <[email protected]>
Committed: Tue Jul 25 21:40:14 2017 +0200

----------------------------------------------------------------------
 deployer/features/pom.xml                       |   1 +
 .../features/FeatureDeploymentListener.java     | 216 ++++++++-----------
 .../apache/karaf/features/FeaturesService.java  |   6 +
 .../internal/service/FeaturesServiceImpl.java   |  99 +++++++--
 .../internal/service/RepositoryCache.java       |   6 +-
 .../karaf/kar/internal/KarServiceImpl.java      |   4 +-
 6 files changed, 186 insertions(+), 146 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/eb996069/deployer/features/pom.xml
----------------------------------------------------------------------
diff --git a/deployer/features/pom.xml b/deployer/features/pom.xml
index 0ff151a..d0c00bb 100644
--- a/deployer/features/pom.xml
+++ b/deployer/features/pom.xml
@@ -105,6 +105,7 @@
                             org.apache.karaf.deployer.features,
                             org.apache.karaf.deployer.features.osgi,
                             org.apache.karaf.util,
+                            org.apache.felix.utils.version
                         </Private-Package>
                     </instructions>
                 </configuration>

http://git-wip-us.apache.org/repos/asf/karaf/blob/eb996069/deployer/features/src/main/java/org/apache/karaf/deployer/features/FeatureDeploymentListener.java
----------------------------------------------------------------------
diff --git 
a/deployer/features/src/main/java/org/apache/karaf/deployer/features/FeatureDeploymentListener.java
 
b/deployer/features/src/main/java/org/apache/karaf/deployer/features/FeatureDeploymentListener.java
index ab954fa..58bfc2b 100644
--- 
a/deployer/features/src/main/java/org/apache/karaf/deployer/features/FeatureDeploymentListener.java
+++ 
b/deployer/features/src/main/java/org/apache/karaf/deployer/features/FeatureDeploymentListener.java
@@ -23,25 +23,27 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URI;
-import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.EnumSet;
 import java.util.Enumeration;
-import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
 
 import org.apache.felix.fileinstall.ArtifactUrlTransformer;
+import org.apache.felix.utils.version.VersionRange;
 import org.apache.karaf.features.Feature;
 import org.apache.karaf.features.FeaturesNamespaces;
 import org.apache.karaf.features.FeaturesService;
@@ -50,9 +52,8 @@ import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleEvent;
 import org.osgi.framework.BundleListener;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
+
+import static org.apache.karaf.features.FeaturesService.ROOT_REGION;
 
 /**
  * A deployment listener able to hot deploy a feature descriptor
@@ -63,7 +64,7 @@ public class FeatureDeploymentListener implements 
ArtifactUrlTransformer, Bundle
 
     private final Logger logger = 
LoggerFactory.getLogger(FeatureDeploymentListener.class);
 
-    private DocumentBuilderFactory dbf;
+    private XMLInputFactory xif;
     private FeaturesService featuresService;
     private BundleContext bundleContext;
     private Properties properties = new Properties();
@@ -144,9 +145,9 @@ public class FeatureDeploymentListener implements 
ArtifactUrlTransformer, Bundle
     public boolean canHandle(File artifact) {
         try {
             if (artifact.isFile() && artifact.getName().endsWith(".xml")) {
-                Document doc = parse(artifact);
-                String name = doc.getDocumentElement().getLocalName();
-                String uri  = doc.getDocumentElement().getNamespaceURI();
+                QName qname = getRootElementName(artifact);
+                String name = qname.getLocalPart();
+                String uri  = qname.getNamespaceURI();
                 if ("features".equals(name) ) {
                        if(isKnownFeaturesURI(uri)){
                         return true;
@@ -176,125 +177,94 @@ public class FeatureDeploymentListener implements 
ArtifactUrlTransformer, Bundle
         }
     }
 
-    public void bundleChanged(BundleEvent bundleEvent) {
-            Bundle bundle = bundleEvent.getBundle();
-            if (bundleEvent.getType() == BundleEvent.RESOLVED) {
-                try {
-                    List<URL> urls = new ArrayList<>();
-                    Enumeration featuresUrlEnumeration = 
bundle.findEntries("/META-INF/" + FEATURE_PATH + "/", "*.xml", false);
-                    while (featuresUrlEnumeration != null && 
featuresUrlEnumeration.hasMoreElements()) {
-                        URL url = (URL) featuresUrlEnumeration.nextElement();
-                        try {
-                            featuresService.addRepository(url.toURI());
-                            URI needRemovedRepo = null;
-                            for (Repository repo : 
featuresService.listRepositories()) {
-                                if (repo.getURI().equals(url.toURI())) {
-                                    Set<Feature> features = new 
HashSet<>(Arrays.asList(repo.getFeatures()));
-                                    Set<String> autoInstallFeatures = new 
HashSet<>();
-                                    for(Feature feature:features) {
-                                        if(feature.getInstall() != null && 
feature.getInstall().equals(Feature.DEFAULT_INSTALL_MODE)){
-                                            if 
(!featuresService.isInstalled(feature)) {
-                                                
autoInstallFeatures.add(feature.getId());
-                                            }
-                                        }
-                                    }
-                                    if (!autoInstallFeatures.isEmpty()) {
-                                        
featuresService.installFeatures(autoInstallFeatures, 
EnumSet.noneOf(FeaturesService.Option.class));
-                                    }
-                                } else {
-                                    //remove older out-of-data feature repo
-                                    if 
(repo.getURI().toString().contains(FEATURE_PATH)) {
-                                        String featureFileName = 
repo.getURI().toString();
-                                        featureFileName = 
featureFileName.substring(featureFileName.lastIndexOf('/') + 1);
-                                        String newFeatureFileName = 
url.toURI().toString();
-                                        newFeatureFileName = 
newFeatureFileName.substring(newFeatureFileName.lastIndexOf('/') + 1);
-                                        if 
(featureFileName.equals(newFeatureFileName)) {
-                                            needRemovedRepo = repo.getURI();
-                                        }
-                                    }
-                                }
+    public synchronized void bundleChanged(BundleEvent bundleEvent) {
+        // Only handle resolved and uninstalled events
+        if (bundleEvent.getType() != BundleEvent.RESOLVED
+                && bundleEvent.getType() != BundleEvent.UNINSTALLED) {
+            return;
+        }
+        Bundle bundle = bundleEvent.getBundle();
+        try {
+            // Remove previous informations
+            List<URI> repsToRemove = new ArrayList<>();
+            List<String> reqsToRemove = new ArrayList<>();
+            // Remove old properties
+            String prefix = "bundle." + bundle.getBundleId();
+            String countStr = (String) properties.remove(prefix + 
".reps.count");
+            if (countStr != null) {
+                int count = Integer.parseInt(countStr);
+                for (int i = 0; i < count; i++) {
+                    String rep = (String) properties.remove(prefix + 
".reps.item" + i);
+                    repsToRemove.add(URI.create(rep));
+                }
+            }
+            countStr = (String) properties.remove(prefix + ".reqs.count");
+            if (countStr != null) {
+                int count = Integer.parseInt(countStr);
+                for (int i = 0; i < count; i++) {
+                    String req = (String) properties.remove(prefix + 
".reqs.item" + i);
+                    reqsToRemove.add(req);
+                }
+            }
+            saveProperties();
 
-                            }
-                            urls.add(url);
-                            if (needRemovedRepo != null) {
-                                
featuresService.removeRepository(needRemovedRepo);
-                            }
-                        } catch (Exception e) {
-                            logger.error("Unable to install features", e);
-                        }
-                    }
-                    synchronized (this) {
-                        String prefix = bundle.getSymbolicName() + "-" + 
bundle.getVersion();
-                        String old = (String) properties.get(prefix + 
".count");
-                        if (old != null && urls.isEmpty()) {
-                            properties.remove(prefix + ".count");
-                            saveProperties();
-                        } else if (!urls.isEmpty()) {
-                            properties.put(prefix + ".count", 
Integer.toString(urls.size()));
-                            for (int i = 0; i < urls.size(); i++) {
-                                properties.put(prefix + ".url." + i, 
urls.get(i).toExternalForm());
-                            }
-                            saveProperties();
-                        }
-                    }
-                } catch (Exception e) {
-                    logger.error("Unable to install deployed features for 
bundle: " + bundle.getSymbolicName() + " - " + bundle.getVersion(), e);
+            // Compute new informations
+            List<URI> repsToAdd = new ArrayList<>();
+            List<String> reqsToAdd = new ArrayList<>();
+            if (bundleEvent.getType() == BundleEvent.RESOLVED) {
+                Enumeration featuresUrlEnumeration = 
bundle.findEntries("/META-INF/" + FEATURE_PATH + "/", "*.xml", false);
+                while (featuresUrlEnumeration != null && 
featuresUrlEnumeration.hasMoreElements()) {
+                    URL url = (URL) featuresUrlEnumeration.nextElement();
+                    URI uri = url.toURI();
+                    repsToAdd.add(uri);
+                    Repository rep = featuresService.createRepository(uri);
+                    Stream.of(rep.getFeatures())
+                            .filter(f -> f.getInstall() == null || 
Feature.DEFAULT_INSTALL_MODE.equals(f.getInstall()))
+                            .map(f -> "feature:" + f.getName() + "/" + new 
VersionRange(f.getVersion(), true))
+                            .forEach(reqsToAdd::add);
                 }
-            } else if (bundleEvent.getType() == BundleEvent.UNINSTALLED) {
-                try {
-                    synchronized (this) {
-                        String prefix = bundle.getSymbolicName() + "-" + 
bundle.getVersion();
-                        String countStr = (String) properties.remove(prefix + 
".count");
-                        if (countStr != null) {
-                            int count = Integer.parseInt(countStr);
-                            for (int i = 0; i < count; i++) {
-                                URL url = new URL((String) 
properties.remove(prefix + ".url." + i));
-                                for (Repository repo : 
featuresService.listRepositories()) {
-                                    try {
-                                        if (repo.getURI().equals(url.toURI())) 
{
-                                            for (Feature f : 
repo.getFeatures()) {
-                                                try {
-                                                    
featuresService.uninstallFeature(f.getName(), f.getVersion());
-                                                } catch (Exception e) {
-                                                    logger.error("Unable to 
uninstall feature: " + f.getName(), e);
-                                                }
-                                            }
-                                        }
-                                    } catch (Exception e) {
-                                        logger.error("Unable to uninstall 
features: " + url, e);
-                                    }
-                                }
-                                try {
-                                    
featuresService.removeRepository(url.toURI());
-                                } catch (URISyntaxException e) {
-                                    logger.error("Unable to remove repository: 
" + url, e);
-                                }
-                            }
-                            saveProperties();
-                        }
+                if (!repsToAdd.isEmpty()) {
+                    properties.put(prefix + ".reps.count", 
Integer.toString(repsToAdd.size()));
+                    for (int i = 0; i < repsToAdd.size(); i++) {
+                        properties.put(prefix + ".reps.item" + i, 
repsToAdd.get(i).toASCIIString());
+                    }
+                    properties.put(prefix + ".reqs.count", 
Integer.toString(reqsToAdd.size()));
+                    for (int i = 0; i < reqsToAdd.size(); i++) {
+                        properties.put(prefix + ".reqs.item" + i, 
reqsToAdd.get(i));
                     }
-                } catch (Exception e) {
-                    logger.error("Unable to uninstall deployed features for 
bundle: " + bundle.getSymbolicName() + " - " + bundle.getVersion(), e);
                 }
             }
+            saveProperties();
+
+            // Call features service
+            List<Repository> requiredRepos = 
Arrays.asList(featuresService.listRequiredRepositories());
+            Set<URI> requiredReposUris = requiredRepos.stream()
+                    .map(Repository::getURI).collect(Collectors.toSet());
+            requiredReposUris.removeAll(repsToRemove);
+            requiredReposUris.addAll(repsToAdd);
+
+            Map<String, Set<String>> requirements = 
featuresService.listRequirements();
+            requirements.get(ROOT_REGION).removeAll(reqsToRemove);
+            requirements.get(ROOT_REGION).addAll(reqsToAdd);
+
+            if (!reqsToRemove.isEmpty() || !reqsToAdd.isEmpty()) {
+                featuresService.updateReposAndRequirements(requiredReposUris, 
requirements, EnumSet.noneOf(FeaturesService.Option.class));
+            }
+        } catch (Exception e) {
+            logger.error("Unable to update deployed features for bundle: " + 
bundle.getSymbolicName() + " - " + bundle.getVersion(), e);
+        }
     }
 
-    protected Document parse(File artifact) throws Exception {
-        if (dbf == null) {
-            dbf = DocumentBuilderFactory.newInstance();
-            dbf.setNamespaceAware(true);
+    private QName getRootElementName(File artifact) throws Exception {
+        if (xif == null) {
+            xif = XMLInputFactory.newFactory();
+            xif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
+        }
+        try (InputStream is = new FileInputStream(artifact)) {
+            XMLStreamReader sr = xif.createXMLStreamReader(is);
+            sr.nextTag();
+            return sr.getName();
         }
-        DocumentBuilder db = dbf.newDocumentBuilder();
-        db.setErrorHandler(new ErrorHandler() {
-            public void warning(SAXParseException exception) throws 
SAXException {
-            }
-            public void error(SAXParseException exception) throws SAXException 
{
-            }
-            public void fatalError(SAXParseException exception) throws 
SAXException {
-                throw exception;
-            }
-        });
-        return db.parse(artifact);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/eb996069/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java 
b/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
index c8cdd15..29c9093 100644
--- a/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
+++ b/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
@@ -126,6 +126,12 @@ public interface FeaturesService {
 
     void updateFeaturesState(Map<String, Map<String, FeatureState>> 
stateChanges, EnumSet<Option> options) throws Exception;
 
+    void updateReposAndRequirements(Set<URI> repos,
+                                    Map<String, Set<String>> requirements,
+                                    EnumSet<Option> options) throws Exception;
+
+    Repository createRepository(URI uri) throws Exception;
+
     Feature[] listFeatures() throws Exception;
 
     Feature[] listRequiredFeatures() throws Exception;

http://git-wip-us.apache.org/repos/asf/karaf/blob/eb996069/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 353d40d..bd98364 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
@@ -42,12 +42,14 @@ import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import org.apache.felix.utils.manifest.Clause;
 import org.apache.felix.utils.version.VersionCleaner;
 import org.apache.felix.utils.version.VersionRange;
 import org.apache.felix.utils.version.VersionTable;
@@ -178,7 +180,7 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
         // Resolve
         try {
             Map<String, Map<String, FeatureState>> stateChanges = 
Collections.emptyMap();
-            doProvisionInThread(requestedFeatures, stateChanges, copyState(), 
options);
+            doProvisionInThread(requestedFeatures, stateChanges, copyState(), 
getFeaturesById(), options);
         } catch (Exception e) {
             LOGGER.warn("Error updating state", e);
         }
@@ -645,6 +647,11 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
         return map;
     }
 
+    protected Map<String, Feature> getFeaturesById() throws Exception {
+        return getFeatureCache().values().stream().flatMap(m -> 
m.values().stream())
+                .collect(Collectors.toMap(Feature::getId, 
Function.identity()));
+    }
+
    //
     // Installed features
     //
@@ -858,7 +865,7 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
         }
         print("Adding features: " + join(featuresToDisplay), 
options.contains(Option.Verbose));
         Map<String, Map<String, FeatureState>> stateChanges = 
Collections.emptyMap();
-        doProvisionInThread(required, stateChanges, state, options);
+        doProvisionInThread(required, stateChanges, state, getFeaturesById(), 
options);
     }
 
     @Override
@@ -909,13 +916,13 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
             required.remove(region);
         }
         Map<String, Map<String, FeatureState>> stateChanges = 
Collections.emptyMap();
-        doProvisionInThread(required, stateChanges, state, options);
+        doProvisionInThread(required, stateChanges, state, getFeaturesById(), 
options);
     }
 
     @Override
     public void updateFeaturesState(Map<String, Map<String, FeatureState>> 
stateChanges, EnumSet<Option> options) throws Exception {
         State state = copyState();
-        doProvisionInThread(copy(state.requirements), stateChanges, state, 
options);
+        doProvisionInThread(copy(state.requirements), stateChanges, state, 
getFeaturesById(), options);
     }
 
     @Override
@@ -924,7 +931,7 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
         Map<String, Set<String>> required = copy(state.requirements);
         add(required, requirements);
         Map<String, Map<String, FeatureState>> stateChanges = 
Collections.emptyMap();
-        doProvisionInThread(required, stateChanges, state, options);
+        doProvisionInThread(required, stateChanges, state, getFeaturesById(), 
options);
     }
 
     @Override
@@ -933,7 +940,63 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
         Map<String, Set<String>> required = copy(state.requirements);
         remove(required, requirements);
         Map<String, Map<String, FeatureState>> stateChanges = 
Collections.emptyMap();
-        doProvisionInThread(required, stateChanges, state, options);
+        doProvisionInThread(required, stateChanges, state, getFeaturesById(), 
options);
+    }
+
+    @Override
+    public void updateReposAndRequirements(Set<URI> repos, Map<String, 
Set<String>> requirements, EnumSet<Option> options) throws Exception {
+        State stateCopy;
+        synchronized (lock) {
+            // Remove repo
+            Set<String> reps = 
repos.stream().map(URI::toString).collect(Collectors.toSet());
+            Set<String> toRemove = diff(state.repositories, reps);
+            Set<String> toAdd = diff(reps, state.repositories);
+            state.repositories.removeAll(toRemove);
+            state.repositories.addAll(toAdd);
+            featureCache = null;
+            for (String uri : toRemove) {
+                repositories.removeRepository(URI.create(uri));
+            }
+            for (String uri : toAdd) {
+                repositories.addRepository(createRepository(URI.create(uri)));
+            }
+            saveState();
+            stateCopy = state.copy();
+        }
+        Map<String, Map<String, FeatureState>> stateChanges = 
Collections.emptyMap();
+        doProvisionInThread(requirements, stateChanges, stateCopy, 
getFeaturesById(), options);
+    }
+
+    private <T> Set<T> diff(Set<T> s1, Set<T> s2) {
+        Set<T> s = new HashSet<>(s1);
+        s.removeAll(s2);
+        return s;
+    }
+
+    @Override
+    public Repository createRepository(URI uri) throws Exception {
+        return repositories.create(uri, true, true);
+    }
+
+    private Map<String, Feature> loadAllFeatures(Set<URI> uris) throws 
Exception {
+        //the outer map's key is feature name, the inner map's key is feature 
version
+        Map<String, Feature> map = new HashMap<>();
+        // Two phase load:
+        // * first load dependent repositories
+        Set<URI> loaded = new HashSet<>();
+        List<URI> toLoad = new ArrayList<>(uris);
+        Clause[] blacklisted = repositories.getBlacklisted();
+        while (!toLoad.isEmpty()) {
+            URI uri = toLoad.remove(0);
+            if (loaded.add(uri)) {
+                Repository repo = new RepositoryImpl(uri, blacklisted);
+                Collections.addAll(toLoad, repo.getRepositories());
+                for (Feature f : repo.getFeatures()) {
+                    map.put(f.getId(), f);
+                }
+            }
+        }
+        return map;
     }
 
     @Override
@@ -974,12 +1037,13 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
     private void doProvisionInThread(final Map<String, Set<String>> 
requirements,
                                     final Map<String, Map<String, 
FeatureState>> stateChanges,
                                     final State state,
+                                    final Map<String, Feature> featureById,
                                     final EnumSet<Option> options) throws 
Exception {
         try {
             final String outputFile = this.outputFile.get();
             this.outputFile.set(null);
             executor.submit(() -> {
-                doProvision(requirements, stateChanges, state, options, 
outputFile);
+                doProvision(requirements, stateChanges, state, featureById, 
options, outputFile);
                 return null;
             }).get();
         } catch (ExecutionException e) {
@@ -996,7 +1060,7 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
         }
     }
 
-    private Deployer.DeploymentState getDeploymentState(State state) throws 
Exception {
+    private Deployer.DeploymentState getDeploymentState(State state, 
Map<String, Feature> featuresById) throws Exception {
         Deployer.DeploymentState dstate = new Deployer.DeploymentState();
         dstate.state = state;
         FrameworkInfo info = installSupport.getInfo();
@@ -1005,13 +1069,7 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
         dstate.currentStartLevel = info.currentStartLevel;
         dstate.bundles = info.bundles;
         // Features
-        dstate.features = new HashMap<>();
-        for (Map<String, Feature> m : getFeatureCache().values()) {
-            for (Feature feature : m.values()) {
-                String id = feature.getId();
-                dstate.features.put(id, feature);
-            }
-        }
+        dstate.features = featuresById;
         RegionDigraph regionDigraph = installSupport.getDiGraphCopy();
         dstate.bundlesPerRegion = 
DigraphHelper.getBundlesPerRegion(regionDigraph);
         dstate.filtersPerRegion = DigraphHelper.getPolicies(regionDigraph);
@@ -1034,10 +1092,11 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
     }
 
     private void doProvision(Map<String, Set<String>> requirements,            
    // all requirements
-                            Map<String, Map<String, FeatureState>> 
stateChanges,  // features state changes
-                            State state,                                       
   // current state
-                            EnumSet<Option> options,                           
   // installation options
-                            String outputFile                                  
   // file to store the resolution or null
+                             Map<String, Map<String, FeatureState>> 
stateChanges,  // features state changes
+                             State state,                                      
    // current state
+                             Map<String, Feature> featuresById,                
    // features by id
+                             EnumSet<Option> options,                          
    // installation options
+                             String outputFile                                 
    // file to store the resolution or null
     ) throws Exception {
 
         Dictionary<String, String> props = getMavenConfig();
@@ -1049,7 +1108,7 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
             Set<String> prereqs = new HashSet<>();
             while (true) {
                 try {
-                    Deployer.DeploymentState dstate = 
getDeploymentState(state);
+                    Deployer.DeploymentState dstate = 
getDeploymentState(state, featuresById);
                     Deployer.DeploymentRequest request = 
getDeploymentRequest(requirements, stateChanges, options, outputFile);
                     new Deployer(manager, this.resolver, this).deploy(dstate, 
request);
                     break;

http://git-wip-us.apache.org/repos/asf/karaf/blob/eb996069/features/core/src/main/java/org/apache/karaf/features/internal/service/RepositoryCache.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/internal/service/RepositoryCache.java
 
b/features/core/src/main/java/org/apache/karaf/features/internal/service/RepositoryCache.java
index 54cf04f..f92b0bc 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/internal/service/RepositoryCache.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/internal/service/RepositoryCache.java
@@ -39,7 +39,11 @@ public class RepositoryCache {
     public RepositoryCache(String blacklisted) {
         this.blacklisted = loadBlacklist(blacklisted);
     }
-    
+
+    public Clause[] getBlacklisted() {
+        return blacklisted;
+    }
+
     private static Clause[] loadBlacklist(String blacklisted) {
         Set<String> blacklistStrings = Blacklist.loadBlacklist(blacklisted);
         return Parser.parseClauses(blacklistStrings.toArray(new 
String[blacklistStrings.size()]));

http://git-wip-us.apache.org/repos/asf/karaf/blob/eb996069/kar/src/main/java/org/apache/karaf/kar/internal/KarServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/kar/src/main/java/org/apache/karaf/kar/internal/KarServiceImpl.java 
b/kar/src/main/java/org/apache/karaf/kar/internal/KarServiceImpl.java
index 743e74a..30f7279 100644
--- a/kar/src/main/java/org/apache/karaf/kar/internal/KarServiceImpl.java
+++ b/kar/src/main/java/org/apache/karaf/kar/internal/KarServiceImpl.java
@@ -274,7 +274,7 @@ public class KarServiceImpl implements KarService {
                 if (repository.getURI().equals(karFeatureRepoUri)) {
                     try {
                         for (Feature feature : repository.getFeatures()) {
-                            if (feature.getInstall() == null || 
!feature.getInstall().equals("manual")) {
+                            if (feature.getInstall() == null || 
Feature.DEFAULT_INSTALL_MODE.equals(feature.getInstall())) {
                                 try {
                                     LOGGER.debug("noAutoRefreshBundles is " + 
isNoAutoRefreshBundles());
                                     if (isNoAutoRefreshBundles()) {
@@ -430,7 +430,7 @@ public class KarServiceImpl implements KarService {
                 if (repository.getURI().equals(karFeatureRepoUri)) {
                     try {
                         for (Feature feature : repository.getFeatures()) {
-                            if (feature.getInstall() == null || 
!feature.getInstall().equals("manual")) {
+                            if (feature.getInstall() == null || 
Feature.DEFAULT_INSTALL_MODE.equals(feature.getInstall())) {
                                 try {
                                     
featuresService.uninstallFeature(feature.getName(), feature.getVersion());
                                 } catch (Exception e) {

Reply via email to