karaf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gno...@apache.org
Subject [12/13] [KARAF-2888] New FeaturesService based on the real OSGi resolver
Date Mon, 07 Apr 2014 08:53:03 GMT
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
deleted file mode 100644
index 909a963..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
+++ /dev/null
@@ -1,1192 +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;
-
-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.Conditional;
-import org.apache.karaf.features.Dependency;
-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.Resolver;
-import org.apache.karaf.features.internal.BundleManager.BundleInstallerResult;
-import org.apache.karaf.util.collections.CopyOnWriteArrayIdentityList;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.Constants;
-import org.osgi.framework.Version;
-import org.osgi.framework.startlevel.BundleStartLevel;
-import org.osgi.util.tracker.ServiceTracker;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Dictionary;
-import java.util.EnumSet;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import static java.lang.String.format;
-
-/**
- * The Features service implementation.
- * Adding a repository url will load the features contained in this repository and
- * create dummy sub shells.  When invoked, these commands will prompt the user for
- * installing the needed bundles.
- */
-public class FeaturesServiceImpl implements FeaturesService {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(FeaturesServiceImpl.class);
-
-    private static final int KARAF_BUNDLE_START_LEVEL =
-            Integer.parseInt(System.getProperty("karaf.startlevel.bundle", "80"));
-
-    private final BundleManager bundleManager;
-    private final FeatureConfigInstaller configManager;
-    private final AtomicBoolean stopped = new AtomicBoolean();
-
-    private boolean respectStartLvlDuringFeatureStartup;
-    private boolean respectStartLvlDuringFeatureUninstall;
-    private long resolverTimeout = 5000;
-    private Set<URI> uris;
-    private Map<URI, Repository> repositories = new HashMap<URI, Repository>();
-    private Map<String, Map<String, Feature>> features;
-    private Map<Feature, Set<Long>> installed = new HashMap<Feature, Set<Long>>();
-    private List<FeaturesListener> listeners = new CopyOnWriteArrayIdentityList<FeaturesListener>();
-    private ThreadLocal<Repository> repo = new ThreadLocal<Repository>();
-    private EventAdminListener eventAdminListener;
-    private String overrides;
-    private FeatureFinder featureFinder;
-    
-    public FeaturesServiceImpl(BundleManager bundleManager) {
-        this(bundleManager, null);
-    }
-    
-    public FeaturesServiceImpl(BundleManager bundleManager, FeatureConfigInstaller configManager) {
-        this.bundleManager = bundleManager;
-        this.configManager = configManager;
-    }
-    
-    public long getResolverTimeout() {
-        return resolverTimeout;
-    }
-
-    public void setResolverTimeout(long resolverTimeout) {
-        this.resolverTimeout = resolverTimeout;
-    }
-
-    public void setRespectStartLvlDuringFeatureStartup(boolean respectStartLvlDuringFeatureStartup) {
-        this.respectStartLvlDuringFeatureStartup = respectStartLvlDuringFeatureStartup;
-    }
-
-    public String getOverrides() {
-        return overrides;
-    }
-
-    public void setOverrides(String overrides) {
-        this.overrides = overrides;
-    }
-
-    public FeatureFinder getFeatureFinder() {
-        return featureFinder;
-    }
-
-    public void setFeatureFinder(FeatureFinder featureFinder) {
-        this.featureFinder = featureFinder;
-    }
-
-    public void registerListener(FeaturesListener listener) {
-        listeners.add(listener);
-        try {
-            for (Repository repository : listRepositories()) {
-                listener.repositoryEvent(new RepositoryEvent(repository, RepositoryEvent.EventType.RepositoryAdded, true));
-            }
-            for (Feature feature : listInstalledFeatures()) {
-                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);
-    }
-
-    public void setUrls(String uris) {
-        String[] s = uris.split(",");
-        this.uris = new HashSet<URI>();
-        for (String value : s) {
-            value = value.trim();
-            if (!value.isEmpty()) {
-                try {
-                    this.uris.add(new URI(value));
-                } catch (URISyntaxException e) {
-                    LOGGER.warn("Invalid features repository URI: " + value);
-                }
-            }
-        }
-    }
-
-    /**
-     * Validate a features repository XML.
-     *
-     * @param uri the features repository URI.
-     */
-    public void validateRepository(URI uri) throws Exception {
-        
-        FeatureValidationUtil.validate(uri);
-    }
-
-    /**
-     * Add a features repository.
-     *
-     * @param uri the features repository URI.
-     * @throws Exception in case of adding failure.
-     */
-    public void addRepository(URI uri) throws Exception {
-        this.addRepository(uri, false);
-    }
-
-    /**
-     * Add a features repository.
-     *
-     * @param uri the features repository URI.
-     * @param install if true, install all features contained in the features repository.
-     * @throws Exception in case of adding failure.
-     */
-    public void addRepository(URI uri, boolean install) throws Exception {
-        if (!repositories.containsKey(uri)) {
-            Repository repositoryImpl = this.internalAddRepository(uri);
-            saveState();
-            if (install) {
-                for (Feature feature : repositoryImpl.getFeatures()) {
-                    installFeature(feature, EnumSet.noneOf(Option.class));
-                }
-            }
-        } else {
-            refreshRepository(uri, install);
-        }
-    }
-    
-    /**
-     * Refresh a features repository.
-     *
-     * @param uri the features repository URI.
-     * @throws Exception in case of refresh failure.
-     */
-    @Override
-    public void refreshRepository(URI uri) throws Exception {
-        this.refreshRepository(uri, false);
-    }
-
-    /**
-     * Refresh a features repository.
-     *
-     * @param uri the features repository URI.
-     * @param install if true, install all features in the features repository.
-     * @throws Exception in case of refresh failure.
-     */
-    protected void refreshRepository(URI uri, boolean install) throws Exception {
-        try {
-            removeRepository(uri, install);
-            addRepository(uri, install);
-        } catch (Exception e) {
-            //get chance to restore previous, fix for KARAF-4
-            restoreRepository(uri);
-            throw new Exception("Unable to refresh features repository " + uri, e);
-        }
-    }
-
-    /**
-     * Add a features repository into the internal container.
-     *
-     * @param uri the features repository URI.
-     * @return the internal <code>RepositoryImpl</code> representation.
-     * @throws Exception in case of adding failure.
-     */
-    protected Repository internalAddRepository(URI uri) throws Exception {
-        validateRepository(uri);
-        RepositoryImpl repo = new RepositoryImpl(uri);
-        repositories.put(uri, repo);
-        repo.load();
-        callListeners(new RepositoryEvent(repo, RepositoryEvent.EventType.RepositoryAdded, false));
-        features = null;
-        return repo;
-        
-    }
-
-    /**
-     * Remove a features repository.
-     *
-     * @param uri the features repository URI.
-     * @throws Exception in case of remove failure.
-     */
-    public void removeRepository(URI uri) throws Exception {
-        this.removeRepository(uri, false);
-    }
-
-    /**
-     * Remove a features repository.
-     *
-     * @param uri the features repository URI.
-     * @param uninstall if true, uninstall all features from the features repository.
-     * @throws Exception in case of remove failure.
-     */
-    public void removeRepository(URI uri, boolean uninstall) throws Exception {
-        if (repositories.containsKey(uri)) {
-            if (uninstall) {
-                Repository repositoryImpl = repositories.get(uri);
-                for (Feature feature : repositoryImpl.getFeatures()) {
-                    this.uninstallFeature(feature.getName(), feature.getVersion());
-                }
-            }
-            internalRemoveRepository(uri);
-            saveState();
-        }
-    }
-
-    /**
-     * Remove a features repository from the internal container.
-     *
-     * @param uri the features repository URI.
-     */
-    protected void internalRemoveRepository(URI uri) {
-        Repository repo = repositories.remove(uri);
-        this.repo.set(repo);
-        callListeners(new RepositoryEvent(repo, RepositoryEvent.EventType.RepositoryRemoved, false));
-        features = null;
-    }
-
-    /**
-     * Restore a features repository.
-     *
-     * @param uri the features repository URI.
-     * @throws Exception in case of restore failure.
-     */
-    public void restoreRepository(URI uri) throws Exception {
-    	repositories.put(uri, repo.get());
-    	callListeners(new RepositoryEvent(repo.get(), RepositoryEvent.EventType.RepositoryAdded, false));
-        features = null;
-    }
-
-    /**
-     * Get the list of features repository.
-     *
-     * @return the list of features repository.
-     */
-    public Repository[] listRepositories() {
-        Collection<Repository> repos = repositories.values();
-        return repos.toArray(new Repository[repos.size()]);
-    }
-    
-    @Override
-    public Repository getRepository(String repoName) {
-        for (Repository repo : this.repositories.values()) {
-            if (repoName.equals(repo.getName())) {
-                return repo;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Install a feature identified by a name.
-     *
-     * @param name the name of the feature.
-     * @throws Exception in case of install failure.
-     */
-    public void installFeature(String name) throws Exception {
-    	installFeature(name, org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION);
-    }
-
-    /**
-     * Install a feature identified by a name, including a set of options.
-     *
-     * @param name the name of the feature.
-     * @param options the installation options.
-     * @throws Exception in case of install failure.
-     */
-    public void installFeature(String name, EnumSet<Option> options) throws Exception {
-        installFeature(name, org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION, options);
-    }
-
-    /**
-     * Install a feature identified by a name and a version.
-     *
-     * @param name the name of the feature.
-     * @param version the version of the feature.
-     * @throws Exception in case of install failure.
-     */
-    public void installFeature(String name, String version) throws Exception {
-        installFeature(name, version, EnumSet.noneOf(Option.class));
-    }
-
-    /**
-     * Install a feature identified by a name and a version, including a set of options.
-     *
-     * @param name the name of the feature.
-     * @param version the version of the feature.
-     * @param options the installation options.
-     * @throws Exception in case of install failure.
-     */
-    public void installFeature(String name, String version, EnumSet<Option> options) throws Exception {
-        Feature f = getFeature(name, version);
-        if (f == null) {
-            throw new Exception("No feature named '" + name
-            		+ "' with version '" + version + "' available");
-        }
-        installFeature(f, options);
-    }
-
-    /**
-     * Install a feature including a set of options.
-     *
-     * @param feature the <code>Feature</code> to install.
-     * @param options the installation options set.
-     * @throws Exception in case of install failure.
-     */
-    public void installFeature(Feature feature, EnumSet<Option> options) throws Exception {
-        installFeatures(Collections.singleton(feature), options);
-    }
-
-    /**
-     * Install a set of features, including a set of options.
-     *
-     * @param features a set of <code>Feature</code>.
-     * @param options the installation options set.
-     * @throws Exception in case of install failure.
-     */
-    public void installFeatures(Set<Feature> features, EnumSet<Option> options) throws Exception {
-
-        final InstallationState state = new InstallationState();
-        final InstallationState failure = new InstallationState();
-        boolean verbose = options.contains(FeaturesService.Option.Verbose);
-        try {
-            // Install everything
-            for (Feature f : features) {
-                InstallationState s = new InstallationState();
-            	try {
-                    doInstallFeature(s, f, verbose);
-                    doInstallFeatureConditionals(s, f, verbose);
-                    //Check if current feature satisfies the conditionals of existing features
-                    for (Feature installedFeature : listInstalledFeatures()) {
-                        doInstallFeatureConditionals(s, installedFeature, verbose);
-                    }
-                    for (Feature installedFeature : state.features.keySet()) {
-                        doInstallFeatureConditionals(s, installedFeature, verbose);
-                    }
-
-                    state.bundleInfos.putAll(s.bundleInfos);
-                    state.bundles.addAll(s.bundles);
-                    state.features.putAll(s.features);
-                    state.installed.addAll(s.installed);
-                    state.bundleStartLevels.putAll(s.bundleStartLevels);
-            	} catch (Exception e) {
-                    failure.bundles.addAll(s.bundles);
-                    failure.features.putAll(s.features);
-                    failure.installed.addAll(s.installed);
-                    if (options.contains(Option.ContinueBatchOnFailure)) {
-                        LOGGER.warn("Error when installing feature {}: {}", f.getName(), e);
-                    } else {
-                        throw e;
-                    }
-            	}
-            }
-            bundleManager.refreshBundles(state.bundles, state.installed, options);
-            // start all bundles sorted by startlvl if wished for
-            List<Bundle> bundlesSortedByStartLvl = new ArrayList<Bundle>(state.bundles);
-            if (respectStartLvlDuringFeatureStartup) {
-                Collections.sort(bundlesSortedByStartLvl, new Comparator<Bundle>() {
-                    @Override
-                    public int compare(Bundle bundle, Bundle bundle1) {
-                        return state.bundleStartLevels.get(bundle) - state.bundleStartLevels.get(bundle1);
-                    }
-                });
-            }
-            for (Bundle b : bundlesSortedByStartLvl) {
-                LOGGER.debug("Starting bundle: {}", b.getSymbolicName());
-                startBundle(state, b);
-            }
-            // Clean up for batch
-            if (!options.contains(Option.NoCleanIfFailure)) {
-                failure.installed.removeAll(state.bundles);
-                if (failure.installed.size()>0) {
-                    bundleManager.uninstall(failure.installed);
-                }
-            }
-            for (Feature f : features) {
-                callListeners(new FeatureEvent(f, FeatureEvent.EventType.FeatureInstalled, false));
-            }
-            for (Map.Entry<Feature, Set<Long>> e : state.features.entrySet()) {
-                installed.put(e.getKey(), e.getValue());
-            }
-            saveState();
-        } catch (Exception e) {
-            boolean noCleanIfFailure = options.contains(Option.NoCleanIfFailure);
-            cleanUpOnFailure(state, failure, noCleanIfFailure);
-            throw e;
-        }
-    }
-
-    /**
-     * Start a bundle.
-     *
-     * @param state the current bundle installation state.
-     * @param bundle the bundle to start.
-     * @throws Exception in case of start failure.
-     */
-	private void startBundle(InstallationState state, Bundle bundle) throws Exception {
-		if (!isFragment(bundle)) {
-		    // do not start bundles that are persistently stopped
-		    if (state.installed.contains(bundle)
-		            || (bundle.getState() != Bundle.STARTING && bundle.getState() != Bundle.ACTIVE
-		                    && bundle.adapt(BundleStartLevel.class).isPersistentlyStarted())) {
-		    	// do no start bundles when user request it
-		    	Long bundleId = bundle.getBundleId();
-		    	BundleInfo bundleInfo = state.bundleInfos.get(bundleId);
-		        if (bundleInfo == null || bundleInfo.isStart()) {
-		            try {
-		                bundle.start();
-		            } catch (BundleException be) {
-		                String msg = format("Could not start bundle %s in feature(s) %s: %s", bundle.getLocation(), getFeaturesContainingBundleList(bundle), be.getMessage());
-		                throw new Exception(msg, be);
-		            }
-		    	}
-		    }
-		}
-	}
-	
-	private boolean isFragment(Bundle b) {
-	    @SuppressWarnings("rawtypes")
-        Dictionary d = b.getHeaders();
-        String fragmentHostHeader = (String) d.get(Constants.FRAGMENT_HOST);
-        return fragmentHostHeader != null && fragmentHostHeader.trim().length() > 0;
-	}
-
-	private void cleanUpOnFailure(InstallationState state, InstallationState failure, boolean noCleanIfFailure) {
-		// cleanup on error
-		if (!noCleanIfFailure) {
-		    HashSet<Bundle> uninstall = new HashSet<Bundle>();
-		    uninstall.addAll(state.installed);
-		    uninstall.addAll(failure.installed);
-		    if (uninstall.size() > 0) {
-		        bundleManager.uninstall(uninstall);
-		    }
-		} else {
-		    // Force start of bundles so that they are flagged as persistently started
-		    for (Bundle b : state.installed) {
-		        try {
-		            b.start();
-		        } catch (Exception e2) {
-		            // Ignore
-		        }
-		    }
-		}
-	}
-
-    protected void doInstallFeature(InstallationState state, Feature feature, boolean verbose) throws Exception {
-        String msg = "Installing feature " + feature.getName() + " " + feature.getVersion();
-        LOGGER.info(msg);
-        if (verbose) {
-            System.out.println(msg);
-        }
-        for (Dependency dependency : feature.getDependencies()) {
-            installFeatureDependency(dependency, state, verbose);
-        }
-        if (configManager != null) {
-            configManager.installFeatureConfigs(feature, verbose);
-        }
-        Set<Long> bundles = new TreeSet<Long>();
-        
-        for (BundleInfo bInfo : Overrides.override(resolve(feature), this.overrides)) {
-            int startLevel = getBundleStartLevel(bInfo.getStartLevel(),feature.getStartLevel());
-            BundleInstallerResult result = bundleManager.installBundleIfNeeded(bInfo.getLocation(), startLevel, feature.getRegion());
-            state.bundles.add(result.bundle);
-            state.bundleStartLevels.put(result.bundle, getBundleStartLevelForOrdering(startLevel));
-            if (result.isNew) {
-                state.installed.add(result.bundle);
-            }
-            String msg2 = (result.isNew) ? "Found installed bundle: " + result.bundle : "Installing bundle " + bInfo.getLocation();
-            LOGGER.debug(msg2);
-            if (verbose) {
-                System.out.println(msg2);
-            }
-
-            bundles.add(result.bundle.getBundleId());
-            state.bundleInfos.put(result.bundle.getBundleId(), bInfo);
-
-        }
-        state.features.put(feature, bundles);
-    }
-
-    private int getBundleStartLevel(int bundleStartLevel, int featureStartLevel) {
-        return (bundleStartLevel > 0) ? bundleStartLevel : featureStartLevel;
-    }
-
-    private int getBundleStartLevelForOrdering(int startLevel){
-        return startLevel == 0 ? KARAF_BUNDLE_START_LEVEL : startLevel;
-    }
-
-    protected void doInstallFeatureConditionals(InstallationState state, Feature feature,  boolean verbose) throws Exception {
-        //Check conditions of the current feature.
-        feature = getFeature(feature.getName(), feature.getVersion());
-        if (feature != null) {
-            for (Conditional conditional : feature.getConditional()) {
-
-                if (dependenciesSatisfied(conditional.getCondition(), state)) {
-                    InstallationState s = new InstallationState();
-                    doInstallFeature(s, conditional.asFeature(feature.getName(), feature.getVersion()), verbose);
-                    state.bundleInfos.putAll(s.bundleInfos);
-                    state.bundles.addAll(s.bundles);
-                    state.features.putAll(s.features);
-                    state.installed.addAll(s.installed);
-                    state.bundleStartLevels.putAll(s.bundleStartLevels);
-                }
-            }
-        }
-    }
-
-    private void installFeatureDependency(Dependency dependency, InstallationState state, boolean verbose)
-            throws Exception {
-        Feature fi = getFeatureForDependency(dependency);
-        if (fi == null) {
-            throw new Exception("No feature named '" + dependency.getName()
-                    + "' with version '" + dependency.getVersion() + "' available");
-        }
-        if (state.features.containsKey(fi)) {
-            LOGGER.debug("Feature {} with version {} is already being installed", fi.getName(), fi.getVersion());
-        } else {
-            doInstallFeature(state, fi, verbose);
-        }
-    }
-    
-    protected List<BundleInfo> resolve(Feature feature) throws Exception {
-        String resolver = feature.getResolver();
-        // If no resolver is specified, we expect a list of uris
-        if (resolver == null || resolver.length() == 0) {
-        	return feature.getBundles();
-        }
-        boolean optional = false;
-        if (resolver.startsWith("(") && resolver.endsWith(")")) {
-            resolver = resolver.substring(1, resolver.length() - 1);
-            optional = true;
-        }
-        
-
-        @SuppressWarnings("unchecked")
-        ServiceTracker<Resolver, Resolver> tracker = bundleManager.createServiceTrackerForResolverName(resolver);
-        if (tracker == null) {
-            return feature.getBundles();
-        }
-        tracker.open();
-        try {
-            if (optional) {
-                Resolver r = (Resolver) tracker.getService();
-                if (r != null) {
-                    return r.resolve(feature);
-                } else {
-                    LOGGER.debug("Optional resolver '" + resolver + "' not found, using the default resolver");
-                    return feature.getBundles();
-                }
-            } else {
-                Resolver r = (Resolver) tracker.waitForService(resolverTimeout);
-                if (r == null) {
-                    throw new Exception("Unable to find required resolver '" + resolver + "'");
-                }
-                return r.resolve(feature);
-            }
-        } finally {
-            tracker.close();
-        }
-    }
-
-    public void uninstallFeature(String name) throws Exception {
-        uninstallFeature(name, EnumSet.noneOf(Option.class));
-    }
-
-    public void uninstallFeature(String name, EnumSet<Option> options) throws Exception {
-        List<String> versions = new ArrayList<String>();
-        for (Feature f : installed.keySet()) {
-            if (name.equals(f.getName())) {
-                versions.add(f.getVersion());
-            }
-        }
-        if (versions.size() == 0) {
-            throw new Exception("Feature named '" + name + "' is not installed");
-        } else if (versions.size() > 1) {
-            StringBuilder sb = new StringBuilder();
-            sb.append("Feature named '").append(name).append("' has multiple versions installed (");
-            for (int i = 0; i < versions.size(); i++) {
-                if (i > 0) {
-                    sb.append(", ");
-                }
-                sb.append(versions.get(i));
-            }
-            sb.append("). Please specify the version to uninstall.");
-            throw new Exception(sb.toString());
-        }
-        uninstallFeature(name, versions.get(0), options);
-    }
-
-    public void uninstallFeature(String name, String version) throws Exception {
-        uninstallFeature(name, version, EnumSet.noneOf(Option.class));
-    }
-
-    public void uninstallFeature(String name, String version, EnumSet<Option> options) throws Exception {
-    	Feature feature = getFeature(name, version);
-        if (feature == null || !installed.containsKey(feature)) {
-            throw new Exception("Feature named '" + name + "' with version '" + version + "' is not installed");
-        }
-        boolean verbose = options != null && options.contains(Option.Verbose);
-        boolean refresh = options == null || !options.contains(Option.NoAutoRefreshBundles);
-        String msg = "Uninstalling feature " + feature.getName() + " " + feature.getVersion();
-        LOGGER.info(msg);
-        if (verbose) {
-            System.out.println(msg);
-        }
-        // Grab all the bundles installed by this feature
-        // and remove all those who will still be in use.
-        // This gives this list of bundles to uninstall.
-        Set<Long> bundles = installed.remove(feature);
-
-        //Also remove bundles installed as conditionals
-        for (Conditional conditional : feature.getConditional()) {
-            Feature conditionalFeature = conditional.asFeature(feature.getName(),feature.getVersion());
-            if (installed.containsKey(conditionalFeature)) {
-                msg = "Uninstalling feature " + conditionalFeature.getName() + " " + conditionalFeature.getVersion();
-                LOGGER.info(msg);
-                if (verbose) {
-                    System.out.println(msg);
-                }
-            	bundles.addAll(installed.remove(conditionalFeature));
-            } else {
-            	LOGGER.info("Conditional feature {}, hasn't been installed!");
-            }
-        }
-        for (Feature f : new HashSet<Feature>(installed.keySet())) {
-            f = getFeature(f.getName(), f.getVersion());
-            if (f != null) {
-                for (Conditional conditional : f.getConditional()) {
-                    boolean satisfied = true;
-                    for (Dependency dep : conditional.getCondition()) {
-                        Feature df = getFeatureForDependency(dep);
-                        satisfied &= installed.containsKey(df);
-                    }
-                    if (!satisfied) {
-                        Feature conditionalFeature = conditional.asFeature(f.getName(), f.getVersion());
-                        if (installed.containsKey(conditionalFeature)) {
-                            msg = "Uninstalling feature " + conditionalFeature.getName() + " " + conditionalFeature.getVersion();
-                            LOGGER.info(msg);
-                            if (verbose) {
-                                System.out.println(msg);
-                            }
-                            bundles.addAll(installed.remove(conditionalFeature));
-                        }
-                    }
-                }
-            }
-        }
-
-        for (Set<Long> b : installed.values()) {
-            bundles.removeAll(b);
-        }
-   
-        List<Bundle> bundlesDescendSortedByStartLvl = new ArrayList<Bundle>();
-        for (long bundleId : bundles) {
-            Bundle b = bundleManager.getBundleContext().getBundle(bundleId);
-            if (b != null) {
-                bundlesDescendSortedByStartLvl.add(b);
-            }
-        }
-        
-        if (isRespectStartLvlDuringFeatureUninstall()) {
-            Collections.sort(bundlesDescendSortedByStartLvl, new Comparator<Bundle>() {
-                @Override
-                public int compare(Bundle bundle, Bundle bundle1) {
-                    return bundle1.adapt(BundleStartLevel.class).getStartLevel() -
-                        bundle.adapt(BundleStartLevel.class).getStartLevel();
-                }
-            });
-        }
-        bundleManager.uninstall(bundlesDescendSortedByStartLvl, refresh);
-        callListeners(new FeatureEvent(feature, FeatureEvent.EventType.FeatureUninstalled, false));
-        saveState();
-    }
-
-    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()]);
-    }
-
-    public Feature[] listInstalledFeatures() throws Exception {
-        Set<Feature> features = new HashSet<Feature>();
-        for (Map<String, Feature> featureWithDifferentVersion : getFeatures().values()) {
-            for (Feature f : featureWithDifferentVersion.values()) {
-                if (installed.containsKey(f)) {
-                    features.add(f);
-                }
-            }
-        }
-        return features.toArray(new Feature[features.size()]);
-    }
-
-    public boolean isInstalled(Feature f) {
-        return installed.containsKey(f);
-    }
-
-    public Feature getFeature(String name) throws Exception {
-        return getFeature(name, org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION);
-    }
-
-    public Feature getFeature(String name, String version) throws Exception {
-        if (version != null) {
-            version = version.trim();
-        }
-        Map<String, Feature> versions = getFeatures().get(name);
-        if (versions == null || versions.isEmpty()) {
-            return null;
-        } else {
-            Feature feature = versions.get(version);
-            if (feature == null) {
-                if (org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION.equals(version)) {
-                    Version latest = new Version(cleanupVersion(version));
-                    for (String available : versions.keySet()) {
-                        Version availableVersion = new Version(cleanupVersion(available));
-                        if (availableVersion.compareTo(latest) > 0) {
-                            feature = versions.get(available);
-                            latest = availableVersion;
-                        }
-                    }
-                } else {
-                    Version latest = new Version(cleanupVersion(org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION));
-                    VersionRange versionRange = new VersionRange(version, true, true);
-                    for (String available : versions.keySet()) {
-                        Version availableVersion = new Version(cleanupVersion(available));
-                        if (availableVersion.compareTo(latest) > 0 && versionRange.contains(availableVersion)) {
-                            feature = versions.get(available);
-                            latest = availableVersion;
-                        }
-                    }
-                }
-            }
-            return feature;
-        }
-    }
-
-    @Override
-    public URI getRepositoryUriFor(String name, String version) {
-        return featureFinder.getUriFor(name, version);
-    }
-
-    @Override
-    public String[] getRepositoryNames() {
-        return featureFinder.getNames();
-    }
-
-    protected Map<String, Map<String, Feature>> getFeatures() throws Exception {
-        if (features == null) {
-        	//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
-            for (;;) {
-                boolean newRepo = false;
-                for (Repository repo : listRepositories()) {
-                    for (URI uri : repo.getRepositories()) {
-                        if (!repositories.containsKey(uri)) {
-                            internalAddRepository(uri);
-                            newRepo = true;
-                        }
-                    }
-                }
-                if (!newRepo) {
-                    break;
-                }
-            }
-            // * then load all features
-            for (Repository repo : repositories.values()) {
-                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);
-                	}
-                }
-            }
-            features = map;
-        }
-        return features;
-    }
-
-	private void initState() {
-        if (!loadState()) {
-            if (uris != null) {
-                for (URI uri : uris) {
-                    try {
-                    	internalAddRepository(uri);
-                    } catch (Exception e) {
-                        LOGGER.warn(format("Unable to add features repository %s at startup", uri), e);    
-                    }
-                }
-            }
-            saveState();
-        }
-	}
-    
-    public void start() {
-        this.eventAdminListener = bundleManager.createAndRegisterEventAdminListener();
-        initState();
-    }
-
-    public void stop() {
-        stopped.set(true);
-        uris = new HashSet<URI>(repositories.keySet());
-        while (!repositories.isEmpty()) {
-            internalRemoveRepository(repositories.keySet().iterator().next());
-        }
-    }
-
-    protected void saveState() {
-        // Never save the state after the service has been stopped
-        if (stopped.get()) {
-            return;
-        }
-        OutputStream os = null;
-        try {
-            File file = bundleManager.getDataFile("FeaturesServiceState.properties");
-            Properties props = new Properties();
-            saveSet(props, "repositories.", repositories.keySet());
-            saveMap(props, "features.", installed);
-            os = new FileOutputStream(file);
-            props.store(new FileOutputStream(file), "FeaturesService State");
-        } catch (Exception e) {
-            LOGGER.error("Error persisting FeaturesService state", e);
-        } finally {
-            close(os);
-        }
-    }
-
-    protected boolean loadState() {
-        try {
-            File file = bundleManager.getDataFile("FeaturesServiceState.properties");
-            if (!file.exists()) {
-                return false;
-            }
-            Properties props = new Properties();
-            InputStream is = new FileInputStream(file);
-            try {
-                props.load(is);
-            } finally {
-                close(is);
-            }
-            Set<URI> repositories = loadSet(props, "repositories.");
-            for (URI repo : repositories) {
-            	try {
-            		internalAddRepository(repo);
-            	} catch (Exception e) {
-            		LOGGER.warn(format("Unable to add features repository %s at startup", repo), e);
-            	}
-            }
-            installed = loadMap(props, "features.");
-            for (Feature f : installed.keySet()) {
-                callListeners(new FeatureEvent(f, FeatureEvent.EventType.FeatureInstalled, true));
-            }
-            return true;
-        } catch (Exception e) {
-            LOGGER.error("Error loading FeaturesService state", e);
-        }
-        return false;
-    }
-
-    private void close(Closeable closeable) {
-        if (closeable != null) {
-            try {
-                closeable.close();
-            } catch (IOException e) {
-                // Ignore
-            }
-        }
-    }
-
-    protected void saveSet(Properties props, String prefix, Set<URI> set) {
-        List<URI> l = new ArrayList<URI>(set);
-        props.clear();
-        props.put(prefix + "count", Integer.toString(l.size()));
-        for (int i = 0; i < l.size(); i++) {
-            props.put(prefix + "item." + i, l.get(i).toString());
-        }
-    }
-
-    protected Set<URI> loadSet(Properties props, String prefix) {
-        Set<URI> l = new HashSet<URI>();
-        String countStr = (String) props.get(prefix + "count");
-        if (countStr != null) {
-            int count = Integer.parseInt(countStr);
-            for (int i = 0; i < count; i++) {
-                l.add(URI.create((String) props.get(prefix + "item." + i)));
-            }
-        }
-        return l;
-    }
-
-    protected void saveMap(Properties props, String prefix, Map<Feature, Set<Long>> map) {
-        for (Map.Entry<Feature, Set<Long>> entry : map.entrySet()) {
-            Feature key = entry.getKey();
-            String val = createValue(entry.getValue());
-            props.put(prefix + key.toString(), val);
-        }
-    }
-
-    protected Map<Feature, Set<Long>> loadMap(Properties props, String prefix) {
-        Map<Feature, Set<Long>> map = new HashMap<Feature, Set<Long>>();
-        for (@SuppressWarnings("rawtypes") Enumeration e = props.propertyNames(); e.hasMoreElements();) {
-            String key = (String) e.nextElement();
-            if (key.startsWith(prefix)) {
-                String val = (String) props.get(key);
-                Set<Long> set = readValue(val);
-                map.put(org.apache.karaf.features.internal.model.Feature.valueOf(key.substring(prefix.length())), set);
-            }
-        }
-        return map;
-    }
-
-    protected String createValue(Set<Long> set) {
-        StringBuilder sb = new StringBuilder();
-        for (long i : set) {
-            if (sb.length() > 0) {
-                sb.append(",");
-            }
-            sb.append(i);
-        }
-        return sb.toString();
-    }
-
-    protected Set<Long> readValue(String val) {
-        Set<Long> set = new HashSet<Long>();
-        if (val != null && val.length() != 0) {
-        	for (String str : val.split(",")) {
-        		set.add(Long.parseLong(str));
-        	}
-        }
-        return set;
-    }
-
-    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);
-        }
-    }
-
-    static Pattern fuzzyVersion  = Pattern.compile("(\\d+)(\\.(\\d+)(\\.(\\d+))?)?([^a-zA-Z0-9](.*))?",
-                                                   Pattern.DOTALL);
-    static Pattern fuzzyModifier = Pattern.compile("(\\d+[.-])*(.*)",
-                                                   Pattern.DOTALL);
-
-    /**
-     * Clean up version parameters. Other builders use more fuzzy definitions of
-     * the version syntax. This method cleans up such a version to match an OSGi
-     * version.
-     *
-     * @param version possibly bundles-non-compliant version
-     * @return osgi compliant version
-     */
-    static public String cleanupVersion(String version) {
-        Matcher m = fuzzyVersion.matcher(version);
-        if (m.matches()) {
-            StringBuffer result = new StringBuffer();
-            String d1 = m.group(1);
-            String d2 = m.group(3);
-            String d3 = m.group(5);
-            String qualifier = m.group(7);
-
-            if (d1 != null) {
-                result.append(d1);
-                if (d2 != null) {
-                    result.append(".");
-                    result.append(d2);
-                    if (d3 != null) {
-                        result.append(".");
-                        result.append(d3);
-                        if (qualifier != null) {
-                            result.append(".");
-                            cleanupModifier(result, qualifier);
-                        }
-                    } else if (qualifier != null) {
-                        result.append(".0.");
-                        cleanupModifier(result, qualifier);
-                    }
-                } else if (qualifier != null) {
-                    result.append(".0.0.");
-                    cleanupModifier(result, qualifier);
-                }
-                return result.toString();
-            }
-        }
-        return version;
-    }
-
-    static void cleanupModifier(StringBuffer result, String modifier) {
-        Matcher m = fuzzyModifier.matcher(modifier);
-        if (m.matches())
-            modifier = m.group(2);
-
-        for (int i = 0; i < modifier.length(); i++) {
-            char c = modifier.charAt(i);
-            if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')
-                    || (c >= 'A' && c <= 'Z') || c == '_' || c == '-')
-                result.append(c);
-        }
-    }
-
-    public Set<Feature> getFeaturesContainingBundle (Bundle bundle) throws Exception {
-        Set<Feature> features = new HashSet<Feature>();
-        for (Map<String, Feature> featureMap : this.getFeatures().values()) {
-            for (Feature f : featureMap.values()) {
-                for (BundleInfo bi : f.getBundles()) {
-                    if (bi.getLocation().equals(bundle.getLocation())) {
-                        features.add(f);
-                        break;
-                    }
-                }
-            }
-        }
-        return features;
-    }
-
-    private String getFeaturesContainingBundleList(Bundle bundle) throws Exception {
-        Set<Feature> features = getFeaturesContainingBundle(bundle);
-        StringBuilder buffer = new StringBuilder();
-        Iterator<Feature> iter = features.iterator();
-        while (iter.hasNext()) {
-            Feature feature= iter.next();
-            buffer.append(feature.getId());
-            if (iter.hasNext()) {
-                buffer.append(", ");
-            }
-        }
-        return buffer.toString();
-    }
-
-    /**
-     * Returns the {@link Feature} that matches the {@link Dependency}.
-     * @param dependency
-     * @return
-     * @throws Exception
-     */
-    private Feature getFeatureForDependency(Dependency dependency) throws Exception {
-        VersionRange range = org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION.equals(dependency.getVersion())
-                ? VersionRange.ANY_VERSION : new VersionRange(dependency.getVersion(), true, true);
-        Feature fi = null;
-        for (Feature f : installed.keySet()) {
-            if (f.getName().equals(dependency.getName())) {
-                Version v = VersionTable.getVersion(f.getVersion());
-                if (range.contains(v)) {
-                    if (fi == null || VersionTable.getVersion(fi.getVersion()).compareTo(v) < 0) {
-                        fi = f;
-                    }
-                }
-            }
-        }
-        if (fi == null) {
-            Map<String, Feature> avail = getFeatures().get(dependency.getName());
-            if (avail != null) {
-                for (Feature f : avail.values()) {
-                    Version v = VersionTable.getVersion(f.getVersion());
-                    if (range.contains(v)) {
-                        if (fi == null || VersionTable.getVersion(fi.getVersion()).compareTo(v) < 0) {
-                            fi = f;
-                        }
-                    }
-                }
-            }
-        }
-        return fi;
-    }
-
-    /**
-     * Estimates if the {@link List} of {@link Dependency} is satisfied.
-     * The method will look into {@link Feature}s that are already installed or now being installed (if {@link InstallationState} is provided (not null)).
-     * @param dependencies
-     * @param state
-     * @return
-     */
-    private boolean dependenciesSatisfied(List<? extends Dependency> dependencies, InstallationState state) throws Exception {
-       boolean satisfied = true;
-       for (Dependency dep : dependencies) {
-           Feature f = getFeatureForDependency(dep);
-           if (f != null && !isInstalled(f) && (state != null && !state.features.keySet().contains(f))) {
-               satisfied = false;
-           }
-       }
-       return satisfied;
-    }
-
-    public boolean isRespectStartLvlDuringFeatureUninstall() {
-        return respectStartLvlDuringFeatureUninstall;
-    }
-
-    public void setRespectStartLvlDuringFeatureUninstall(boolean respectStartLvlDuringFeatureUninstall) {
-        this.respectStartLvlDuringFeatureUninstall = respectStartLvlDuringFeatureUninstall;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/InstallationState.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/InstallationState.java b/features/core/src/main/java/org/apache/karaf/features/internal/InstallationState.java
deleted file mode 100644
index 0594497..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/InstallationState.java
+++ /dev/null
@@ -1,35 +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;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-import org.apache.karaf.features.BundleInfo;
-import org.apache.karaf.features.Feature;
-import org.osgi.framework.Bundle;
-
-public class InstallationState {
-    final Set<Bundle> installed = new HashSet<Bundle>();
-    final Set<Bundle> bundles = new TreeSet<Bundle>();
-    final Map<Long, BundleInfo> bundleInfos = new HashMap<Long, BundleInfo>();
-    final Map<Bundle,Integer> bundleStartLevels = new HashMap<Bundle, Integer>();
-    final Map<Feature, Set<Long>> features = new HashMap<Feature, Set<Long>>();
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/Overrides.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/Overrides.java b/features/core/src/main/java/org/apache/karaf/features/internal/Overrides.java
deleted file mode 100644
index dab988a..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/Overrides.java
+++ /dev/null
@@ -1,230 +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;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.jar.Manifest;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-
-import org.apache.felix.utils.manifest.Clause;
-import org.apache.felix.utils.manifest.Parser;
-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.internal.model.Bundle;
-import org.osgi.framework.Constants;
-import org.osgi.framework.Version;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Helper class to deal with overriden bundles at feature installation time.
- \*/
-public class Overrides {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(Overrides.class);
-
-    protected static final String OVERRIDE_RANGE = "range";
-    private static final String VENDOR_WARNING = "Bundle Vendor has changed, please check if this is intentional. Bundle: ";
-
-    /**
-     * Compute a list of bundles to install, taking into account overrides.
-     *
-     * The file containing the overrides will be loaded from the given url.
-     * Blank lines and lines starting with a '#' will be ignored, all other lines
-     * are considered as urls to override bundles.
-     *
-     * The list of bundles to install will be scanned and for each bundle,
-     * if a bundle override matches that bundle, it will be used instead.
-     *
-     * Matching is done on bundle symbolic name (they have to be the same)
-     * and version (the bundle override version needs to be greater than the
-     * bundle to be installed, and less than the next minor version.  A range
-     * directive can be added to the override url in which case, the matching
-     * will succeed if the bundle to be installed is within the given range.
-     *
-     * @param infos the list of bundles to install
-     * @param overridesUrl url pointing to the file containing the list of override bundles
-     * @return a new list of bundles to install
-     */
-    public static List<BundleInfo> override(List<BundleInfo> infos, String overridesUrl) {
-        List<Clause> overrides = loadOverrides(overridesUrl);
-        if (overrides.isEmpty()) {
-            return infos;
-        }
-        try {
-            Map<String, Manifest> manifests = new HashMap<String, Manifest>();
-            for (Clause override : overrides) {
-                Manifest manifest = getManifest(override.getName());
-                manifests.put(override.getName(), manifest);
-            }
-            List<BundleInfo> newInfos = new ArrayList<BundleInfo>();
-            for (BundleInfo info : infos) {
-                Manifest manifest = getManifest(info.getLocation());
-                if (manifest != null) {
-                    String bsn = getBundleSymbolicName(manifest);
-                    Version ver = getBundleVersion(manifest);
-                    String ven = getBundleVendor(manifest);
-                    String url = info.getLocation();
-                    for (Clause override : overrides) {
-                        Manifest overMan = manifests.get(override.getName());
-                        if (overMan == null) {
-                            continue;
-                        }
-                        String oBsn = getBundleSymbolicName(overMan);
-                        if (!bsn.equals(oBsn)) {
-                            continue;
-                        }
-
-                        Version oVer = getBundleVersion(overMan);
-                        VersionRange range;
-                        String vr = extractVersionRange(override);
-                        if (vr == null) {
-                            // default to micro version compatibility
-                            Version v2 = new Version(oVer.getMajor(), oVer.getMinor(), 0);
-                            if (v2.equals(oVer)) {
-                                continue;
-                            }
-                            range = new VersionRange(false, v2, oVer, true);
-                        } else {
-                            range = VersionRange.parseVersionRange(vr);
-                        }
-
-                        String vendor = getBundleVendor(overMan);
-
-                        // Before we do a replace, lets check if vendors change
-                        if (ven == null) {
-                             if (vendor != null) {
-                                 LOGGER.warn(VENDOR_WARNING + bsn);
-                             }
-                        } else {
-                             if (vendor == null) {
-                                 LOGGER.warn(VENDOR_WARNING + bsn);
-                             } else {
-                                  if (!vendor.equals(ven)) {
-                                      LOGGER.warn(VENDOR_WARNING + bsn);
-                                  } 
-                             }
-                        }
-                        // The resource matches, so replace it with the overridden resource
-                        // if the override is actually a newer version than what we currently have
-                        if (range.contains(ver) && ver.compareTo(oVer) < 0) {
-                            LOGGER.warn("Overriding original bundle " + url + " to " + override.getName());
-                            ver = oVer;
-                            url = override.getName();
-                        }
-                    }
-                    if (!info.getLocation().equals(url)) {
-                        Bundle b = new Bundle();
-                        b.setLocation(url);
-                        b.setStartLevel(info.getStartLevel());
-                        b.setStart(info.isStart());
-                        b.setDependency(info.isDependency());
-                        newInfos.add(b);
-                    } else {
-                        newInfos.add(info);
-                    }
-                } else {
-                    newInfos.add(info);
-                }
-            }
-            return newInfos;
-        } catch (Exception e) {
-            LOGGER.info("Unable to process bundle overrides", e);
-            return infos;
-        }
-    }
-
-    public static List<Clause> loadOverrides(String overridesUrl) {
-        List<Clause> overrides = new ArrayList<Clause>();
-        try {
-            if (overridesUrl != null) {
-                InputStream is = new URL(overridesUrl).openStream();
-                try {
-                    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
-                    String line;
-                    while ((line = reader.readLine()) != null) {
-                        line = line.trim();
-                        if (!line.isEmpty() && !line.startsWith("#")) {
-                            Clause[] cs = Parser.parseClauses(new String[]{line});
-                            Collections.addAll(overrides, cs);
-                        }
-                    }
-                } finally {
-                    is.close();
-                }
-            }
-        } catch (Exception e) {
-            LOGGER.debug("Unable to load overrides bundles list", e);
-        }
-        return overrides;
-    }
-
-    private static Version getBundleVersion(Manifest manifest) {
-        String ver = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
-        return VersionTable.getVersion(ver);
-    }
-
-    private static String getBundleSymbolicName(Manifest manifest) {
-        String bsn = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
-        bsn = stripSymbolicName(bsn);
-        return bsn;
-    }
-
-    private static String getBundleVendor(Manifest manifest) {
-        String ven = manifest.getMainAttributes().getValue(Constants.BUNDLE_VENDOR);
-        return ven;
-    }
-
-    private static Manifest getManifest(String url) throws IOException {
-        InputStream is = new URL(url).openStream();
-        try {
-            ZipInputStream zis = new ZipInputStream(is);
-            ZipEntry entry = null;
-            while ( (entry = zis.getNextEntry()) != null ) {
-                if ("META-INF/MANIFEST.MF".equals(entry.getName())) {
-                    return new Manifest(zis);
-                }
-            }
-            return null;
-        } finally {
-            is.close();
-        }
-    }
-
-    private static String extractVersionRange(Clause override) {
-        return override.getAttribute(OVERRIDE_RANGE);
-    }
-
-    private static String stripSymbolicName(String symbolicName) {
-        Clause[] cs = Parser.parseHeader(symbolicName);
-        if (cs == null || cs.length != 1) {
-            throw new IllegalArgumentException("Bad Bundle-SymbolicName header: " + symbolicName);
-        }
-        return cs[0].getName();
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java
deleted file mode 100644
index 435ba36..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java
+++ /dev/null
@@ -1,99 +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;
-
-import java.io.BufferedInputStream;
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.net.URI;
-import org.apache.karaf.features.Repository;
-import org.apache.karaf.features.internal.model.Features;
-import org.apache.karaf.features.internal.model.JaxbUtil;
-
-/**
- * The repository implementation.
- */
-public class RepositoryImpl implements Repository {
-
-    private URI uri;
-    private boolean valid;
-    private Features features;
-
-    public RepositoryImpl(URI uri) {
-        this.uri = uri;
-    }
-
-    public String getName() {
-        return features.getName();
-    }
-
-    public URI getURI() {
-        return uri;
-    }
-
-    public URI[] getRepositories() throws Exception {
-        load();
-        URI[] result = new URI[features.getRepository().size()];
-        for (int i = 0; i < features.getRepository().size(); i++) {
-            String uri = features.getRepository().get(i);
-            uri = uri.trim();
-            result[i] = URI.create(uri);
-        }
-        return result;
-    }
-
-    public org.apache.karaf.features.Feature[] getFeatures() throws Exception {
-        load();
-        return features.getFeature().toArray(new org.apache.karaf.features.Feature[0]);
-    }
-
-
-    public void load() throws IOException {
-        if (features == null) {
-            try {
-                InputStream inputStream = uri.toURL().openStream();
-                inputStream = new FilterInputStream(inputStream) {
-    				@Override
-    				public int read(byte b[], int off, int len) throws IOException {
-    					if (Thread.currentThread().isInterrupted()) {
-    						throw new InterruptedIOException();
-    					}
-    					return super.read(b, off, len);
-    				}
-    			};
-                try {
-                    features = JaxbUtil.unmarshal(inputStream, false);
-                } finally {
-                    inputStream.close();
-                }
-                valid = true;
-            } catch (IllegalArgumentException e) {
-                throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
-            } catch (Exception e) {
-                throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
-            }
-        }
-    }
-
-    public boolean isValid() {
-        return this.valid;
-    }
-
-}
-

http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/deployment/DeploymentBuilder.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/deployment/DeploymentBuilder.java b/features/core/src/main/java/org/apache/karaf/features/internal/deployment/DeploymentBuilder.java
new file mode 100644
index 0000000..7226471
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/deployment/DeploymentBuilder.java
@@ -0,0 +1,330 @@
+/*
+ * 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.deployment;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.apache.felix.resolver.ResolverImpl;
+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.Conditional;
+import org.apache.karaf.features.Dependency;
+import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.Repository;
+import org.apache.karaf.features.internal.repository.AggregateRepository;
+import org.apache.karaf.features.internal.repository.StaticRepository;
+import org.apache.karaf.features.internal.resolver.FeatureNamespace;
+import org.apache.karaf.features.internal.resolver.FeatureResource;
+import org.apache.karaf.features.internal.resolver.RequirementImpl;
+import org.apache.karaf.features.internal.resolver.ResolveContextImpl;
+import org.apache.karaf.features.internal.resolver.ResourceBuilder;
+import org.apache.karaf.features.internal.resolver.ResourceImpl;
+import org.apache.karaf.features.internal.resolver.Slf4jResolverLog;
+import org.apache.karaf.features.internal.service.Overrides;
+import org.apache.karaf.features.internal.util.Macro;
+import org.apache.karaf.features.internal.util.MultiException;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
+import org.osgi.service.resolver.ResolutionException;
+import org.osgi.service.resolver.ResolveContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ */
+public class DeploymentBuilder {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(DeploymentBuilder.class);
+
+    public static final String REQ_PROTOCOL = "req:";
+
+    private final Collection<Repository> repositories;
+
+    private final List<org.osgi.service.repository.Repository> resourceRepos;
+
+    String featureRange = "${range;[====,====]}";
+
+    Downloader downloader;
+    ResourceImpl requirements;
+    Map<String, Resource> resources;
+    Map<String, StreamProvider> providers;
+
+    Set<Feature> featuresToRegister = new HashSet<Feature>();
+
+    public DeploymentBuilder(Downloader downloader,
+                             Collection<Repository> repositories) {
+        this.downloader = downloader;
+        this.repositories = repositories;
+        this.resourceRepos = new ArrayList<org.osgi.service.repository.Repository>();
+    }
+
+    public void addResourceRepository(org.osgi.service.repository.Repository repository) {
+        resourceRepos.add(repository);
+    }
+
+    public Map<String, StreamProvider> getProviders() {
+        return providers;
+    }
+
+    public void setFeatureRange(String featureRange) {
+        this.featureRange = featureRange;
+    }
+
+    public Map<String, Resource> download(
+                         Set<String> features,
+                         Set<String> bundles,
+                         Set<String> reqs,
+                         Set<String> overrides,
+                         Set<String> optionals)
+                throws IOException, MultiException, InterruptedException, ResolutionException, BundleException {
+        this.resources = new ConcurrentHashMap<String, Resource>();
+        this.providers = new ConcurrentHashMap<String, StreamProvider>();
+        this.requirements = new ResourceImpl("dummy", "dummy", Version.emptyVersion);
+        // First, gather all bundle resources
+        for (String feature : features) {
+            registerMatchingFeatures(feature);
+        }
+        for (String bundle : bundles) {
+            downloadAndBuildResource(bundle);
+        }
+        for (String req : reqs) {
+            buildRequirement(req);
+        }
+        for (String override : overrides) {
+            // TODO: ignore download failures for overrides
+            downloadAndBuildResource(Overrides.extractUrl(override));
+        }
+        for (String optional : optionals) {
+            downloadAndBuildResource(optional);
+        }
+        // Wait for all resources to be created
+        downloader.await();
+        // Do override replacement
+        Overrides.override(resources, overrides);
+        // Build features resources
+        for (Feature feature : featuresToRegister) {
+            Resource resource = FeatureResource.build(feature, featureRange, resources);
+            resources.put("feature:" + feature.getName() + "/" + feature.getVersion(), resource);
+        }
+        // Build requirements
+        for (String feature : features) {
+            requireFeature(feature, requirements);
+        }
+        for (String bundle : bundles) {
+            requireResource(bundle);
+        }
+        for (String req : reqs) {
+            requireResource(REQ_PROTOCOL + req);
+        }
+        return resources;
+    }
+
+    public Collection<Resource> resolve(List<Resource> systemBundles,
+                                        boolean resolveOptionalImports) throws ResolutionException {
+        // Resolve
+        for (int i = 0; i < systemBundles.size(); i++) {
+            resources.put("system-bundle-" + i, systemBundles.get(i));
+        }
+
+        List<org.osgi.service.repository.Repository> repos = new ArrayList<org.osgi.service.repository.Repository>();
+        repos.add(new StaticRepository(resources.values()));
+        repos.addAll(resourceRepos);
+
+        ResolverImpl resolver = new ResolverImpl(new Slf4jResolverLog(LOGGER));
+        ResolveContext context = new ResolveContextImpl(
+                Collections.<Resource>singleton(requirements),
+                Collections.<Resource>emptySet(),
+                new AggregateRepository(repos),
+                resolveOptionalImports);
+
+        Map<Resource, List<Wire>> resolution = resolver.resolve(context);
+        return resolution.keySet();
+    }
+
+    public void requireFeature(String feature, ResourceImpl resource) throws IOException {
+        // Find name and version range
+        String[] split = feature.split("/");
+        String name = split[0].trim();
+        String version = (split.length > 1) ? split[1].trim() : null;
+        if (version != null && !version.equals("0.0.0") && !version.startsWith("[") && !version.startsWith("(")) {
+            version = Macro.transform(featureRange, version);
+        }
+        VersionRange range = version != null ? new VersionRange(version) : VersionRange.ANY_VERSION;
+        // Add requirement
+        Map<String, Object> attrs = new HashMap<String, Object>();
+        attrs.put(IdentityNamespace.IDENTITY_NAMESPACE, name);
+        attrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, FeatureNamespace.TYPE_FEATURE);
+        attrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, range);
+        resource.addRequirement(
+                new RequirementImpl(resource, IdentityNamespace.IDENTITY_NAMESPACE,
+                        Collections.<String, String>emptyMap(), attrs)
+        );
+    }
+
+    public void requireResource(String location) {
+        Resource res = resources.get(location);
+        if (res == null) {
+            throw new IllegalStateException("Could not find resource for " + location);
+        }
+        List<Capability> caps = res.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE);
+        if (caps.size() != 1) {
+            throw new IllegalStateException("Resource does not have a single " + IdentityNamespace.IDENTITY_NAMESPACE + " capability");
+        }
+        Capability cap = caps.get(0);
+        // Add requirement
+        Map<String, Object> attrs = new HashMap<String, Object>();
+        attrs.put(IdentityNamespace.IDENTITY_NAMESPACE, cap.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE));
+        attrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, cap.getAttributes().get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE));
+        attrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, new VersionRange((Version) cap.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE), true));
+        requirements.addRequirement(
+                new RequirementImpl(requirements, IdentityNamespace.IDENTITY_NAMESPACE,
+                        Collections.<String, String>emptyMap(), attrs));
+
+    }
+
+    public void registerMatchingFeatures(String feature) throws IOException {
+        // Find name and version range
+        String[] split = feature.split("/");
+        String name = split[0].trim();
+        String version = (split.length > 1)
+                ? split[1].trim() : Version.emptyVersion.toString();
+        // Register matching features
+        registerMatchingFeatures(name, new VersionRange(version));
+    }
+
+    public void registerMatchingFeatures(String name, String version) throws IOException {
+        if (version != null && !version.equals("0.0.0") && !version.startsWith("[") && !version.startsWith("(")) {
+            version = Macro.transform(featureRange, version);
+        }
+        registerMatchingFeatures(name, version != null ? new VersionRange(version) : VersionRange.ANY_VERSION);
+    }
+
+    public void registerMatchingFeatures(String name, VersionRange range) throws IOException {
+        for (Repository repo : repositories) {
+            Feature[] features;
+            try {
+                features = repo.getFeatures();
+            } catch (Exception e) {
+                // This should not happen as the repository has been loaded already
+                throw new IllegalStateException(e);
+            }
+            for (Feature f : features) {
+                if (name.equals(f.getName())) {
+                    Version v = VersionTable.getVersion(f.getVersion());
+                    if (range.contains(v)) {
+                        featuresToRegister.add(f);
+                        for (Dependency dep : f.getDependencies()) {
+                            registerMatchingFeatures(dep.getName(), dep.getVersion());
+                        }
+                        for (BundleInfo bundle : f.getBundles()) {
+                            downloadAndBuildResource(bundle.getLocation());
+                        }
+                        for (Conditional cond : f.getConditional()) {
+                            Feature c = cond.asFeature(f.getName(), f.getVersion());
+                            featuresToRegister.add(c);
+                            for (BundleInfo bundle : c.getBundles()) {
+                                downloadAndBuildResource(bundle.getLocation());
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public void buildRequirement(String requirement) {
+        try {
+            String location = REQ_PROTOCOL + requirement;
+            ResourceImpl resource = new ResourceImpl(location, "dummy", Version.emptyVersion);
+            for (Requirement req : ResourceBuilder.parseRequirement(resource, requirement)) {
+                resource.addRequirement(req);
+            }
+            resources.put(location, resource);
+        } catch (BundleException e) {
+            throw new IllegalArgumentException("Error parsing requirement: " + requirement, e);
+        }
+    }
+
+    public void downloadAndBuildResource(final String location) throws IOException {
+        if (!resources.containsKey(location)) {
+            downloader.download(location, new Downloader.DownloadCallback() {
+                @Override
+                public void downloaded(StreamProvider provider) throws Exception {
+                    manageResource(location, provider);
+                }
+            });
+        }
+    }
+
+    private void manageResource(String location, StreamProvider provider) throws Exception {
+        if (!resources.containsKey(location)) {
+            Attributes attributes = getAttributes(location, provider);
+            Resource resource = createResource(location, attributes);
+            resources.put(location, resource);
+            providers.put(location, provider);
+        }
+    }
+
+    private Resource createResource(String uri, Attributes attributes) throws Exception {
+        Map<String, String> headers = new HashMap<String, String>();
+        for (Map.Entry attr : attributes.entrySet()) {
+            headers.put(attr.getKey().toString(), attr.getValue().toString());
+        }
+        try {
+            return ResourceBuilder.build(uri, headers);
+        } catch (BundleException e) {
+            throw new Exception("Unable to create resource for bundle " + uri, e);
+        }
+    }
+
+    protected Attributes getAttributes(String uri, StreamProvider provider) throws Exception {
+        InputStream is = provider.open();
+        try {
+            ZipInputStream zis = new ZipInputStream(is);
+            ZipEntry entry;
+            while ( (entry = zis.getNextEntry()) != null ) {
+                if ("META-INF/MANIFEST.MF".equals(entry.getName())) {
+                    return new Manifest(zis).getMainAttributes();
+                }
+            }
+        } finally {
+            is.close();
+        }
+        throw new IllegalArgumentException("Resource " + uri + " does not contain a manifest");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/deployment/Downloader.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/deployment/Downloader.java b/features/core/src/main/java/org/apache/karaf/features/internal/deployment/Downloader.java
new file mode 100644
index 0000000..2d5dd98
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/deployment/Downloader.java
@@ -0,0 +1,35 @@
+/*
+ * 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.deployment;
+
+import java.net.MalformedURLException;
+
+import org.apache.karaf.features.internal.util.MultiException;
+
+public interface Downloader {
+
+    void await() throws InterruptedException, MultiException;
+
+    void download(String location, DownloadCallback downloadCallback) throws MalformedURLException;
+
+    interface DownloadCallback {
+
+        void downloaded(StreamProvider provider) throws Exception;
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/deployment/StreamProvider.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/deployment/StreamProvider.java b/features/core/src/main/java/org/apache/karaf/features/internal/deployment/StreamProvider.java
new file mode 100644
index 0000000..60a3dfc
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/deployment/StreamProvider.java
@@ -0,0 +1,26 @@
+/*
+ * 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.deployment;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface StreamProvider {
+
+    InputStream open() throws IOException;
+
+}


Mime
View raw message