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

[5/6] git commit: [KARAF-2923] Region support in features service

[KARAF-2923] Region support in features service


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

Branch: refs/heads/master
Commit: 2705ad8894a2af462487774c705066ef7de13f25
Parents: 486ad3a
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Fri Apr 18 21:16:02 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Fri Apr 18 23:33:51 2014 +0200

----------------------------------------------------------------------
 assemblies/features/standard/pom.xml            |  17 -
 .../standard/src/main/feature/feature.xml       |   6 -
 features/command/pom.xml                        |   5 +
 .../features/command/InstallFeatureCommand.java |   5 +-
 .../features/command/RegionInfoCommand.java     | 129 ++++
 .../command/UninstallFeatureCommand.java        |   5 +-
 features/core/pom.xml                           |  18 +-
 .../java/org/apache/karaf/features/Feature.java |   4 +-
 .../apache/karaf/features/FeaturesService.java  |   4 +
 .../karaf/features/RegionsPersistence.java      |  26 -
 .../org/apache/karaf/features/Repository.java   |   2 -
 .../org/apache/karaf/features/Resolver.java     |  25 -
 .../org/apache/karaf/features/ScopeFilter.java  |  25 +
 .../java/org/apache/karaf/features/Scoping.java |  29 +
 .../internal/deployment/DeploymentBuilder.java  | 354 ----------
 .../internal/deployment/Downloader.java         |  35 -
 .../internal/deployment/StreamProvider.java     |  26 -
 .../internal/download/DownloadCallback.java     |  23 +
 .../internal/download/DownloadManager.java      |  27 +
 .../features/internal/download/Downloader.java  |  29 +
 .../internal/download/StreamProvider.java       |  29 +
 .../download/simple/SimpleDownloader.java       | 120 ++++
 .../karaf/features/internal/model/Feature.java  |  23 +-
 .../karaf/features/internal/model/JaxbUtil.java |  33 +-
 .../features/internal/model/ScopeFilter.java    |  56 ++
 .../karaf/features/internal/model/Scoping.java  |  70 ++
 .../karaf/features/internal/osgi/Activator.java |  73 +-
 .../region/AbstractRegionDigraphVisitor.java    | 121 ++++
 .../internal/region/CandidateComparator.java    | 125 ++++
 .../internal/region/ResourceComparator.java     |  43 ++
 .../features/internal/region/Subsystem.java     | 341 ++++++++++
 .../region/SubsystemResolveContext.java         | 188 ++++++
 .../internal/region/SubsystemResolver.java      | 317 +++++++++
 .../internal/resolver/CandidateComparator.java  | 129 ----
 .../internal/resolver/FeatureNamespace.java     |  72 --
 .../internal/resolver/FeatureResource.java      |  43 +-
 .../internal/resolver/IdentityCapability.java   |   2 +-
 .../internal/resolver/ResolveContextImpl.java   | 102 ---
 .../internal/resolver/ResourceBuilder.java      |   7 +-
 .../internal/resolver/ResourceUtils.java        | 103 +++
 .../internal/resolver/UriNamespace.java         |  47 --
 .../service/FeatureConfigInstaller.java         |  40 +-
 .../internal/service/FeaturesServiceImpl.java   | 667 ++++++++++++-------
 .../internal/service/RepositoryImpl.java        |   8 +-
 .../internal/service/SimpleDownloader.java      |  51 --
 .../karaf/features/internal/service/State.java  |  29 +-
 .../features/internal/service/StateStorage.java | 131 ++--
 .../features/internal/util/JsonWriter.java      |   8 +
 .../karaf/features/internal/util/MapUtils.java  |  65 ++
 .../equinox/internal/region/DigraphHelper.java  | 149 +++++
 .../karaf/features/karaf-features-1.3.0.xsd     |  58 +-
 .../karaf/features/FeaturesServiceTest.java     |  11 +-
 .../features/internal/region/SubsystemTest.java | 213 ++++++
 .../service/FeaturesServiceImplTest.java        |   8 +-
 .../internal/service/OverridesTest.java         |  13 +-
 .../internal/service/StateStorageTest.java      |  80 +++
 .../karaf/features/internal/region/data1/a.mf   |   6 +
 .../karaf/features/internal/region/data1/b.mf   |   6 +
 .../karaf/features/internal/region/data1/c.mf   |   6 +
 .../features/internal/region/data1/features.xml |  31 +
 .../karaf/features/internal/region/data2/a.mf   |   7 +
 .../karaf/features/internal/region/data2/b.mf   |   7 +
 .../karaf/features/internal/region/data2/c.mf   |   6 +
 .../karaf/features/internal/region/data2/d.mf   |   6 +
 .../karaf/features/internal/region/data2/e.mf   |   6 +
 .../features/internal/region/data2/features.xml |  42 ++
 .../karaf/features/internal/service/f07.xml     |  11 +
 .../org/apache/karaf/itests/RegionTest.java     |  48 --
 .../itests/features/StandardFeaturesTest.java   |   5 -
 pom.xml                                         |  14 +-
 region/NOTICE                                   |  71 --
 region/pom.xml                                  | 136 ----
 .../karaf/region/commands/AddBundleCommand.java |  52 --
 .../karaf/region/commands/AddFilterCommand.java | 169 -----
 .../karaf/region/commands/AddRegionCommand.java |  38 --
 .../karaf/region/commands/InfoCommand.java      | 117 ----
 .../region/commands/RegionCommandSupport.java   |  79 ---
 .../karaf/region/commands/util/FileUtil.java    | 177 -----
 .../region/persist/internal/Activator.java      |  91 ---
 .../persist/internal/RegionsBundleTracker.java  |  77 ---
 .../internal/RegionsPersistenceImpl.java        | 203 ------
 .../internal/model/FilterAttributeType.java     |  94 ---
 .../internal/model/FilterBundleType.java        | 156 -----
 .../internal/model/FilterNamespaceType.java     | 102 ---
 .../internal/model/FilterPackageType.java       | 129 ----
 .../persist/internal/model/FilterType.java      | 195 ------
 .../persist/internal/model/ObjectFactory.java   | 116 ----
 .../internal/model/RegionBundleType.java        |  94 ---
 .../persist/internal/model/RegionType.java      | 106 ---
 .../persist/internal/model/RegionsType.java     | 112 ----
 .../persist/internal/model/package-info.java    |   9 -
 .../internal/util/ManifestHeaderProcessor.java  | 661 ------------------
 .../internal/util/ManifestHeaderUtils.java      |  85 ---
 .../persist/internal/util/VersionRange.java     | 456 -------------
 .../org/apache/karaf/region/persist/region.xsd  | 109 ---
 .../webconsole/features/ExtendedFeature.java    |   6 +-
 96 files changed, 3127 insertions(+), 5083 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/assemblies/features/standard/pom.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/standard/pom.xml b/assemblies/features/standard/pom.xml
index f050441..bad1015 100644
--- a/assemblies/features/standard/pom.xml
+++ b/assemblies/features/standard/pom.xml
@@ -162,11 +162,6 @@
             <classifier>uber</classifier>
             <scope>provided</scope>
         </dependency>
-        <dependency>
-            <groupId>org.apache.karaf.features</groupId>
-            <artifactId>org.apache.karaf.features.obr</artifactId>
-            <scope>provided</scope>
-        </dependency>
 
         <!-- config deps -->
         <dependency>
@@ -175,18 +170,6 @@
             <scope>provided</scope>
         </dependency>
 
-        <!-- region deps -->
-        <dependency>
-            <groupId>org.eclipse.equinox</groupId>
-            <artifactId>org.eclipse.equinox.region</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.karaf.region</groupId>
-            <artifactId>org.apache.karaf.region.core</artifactId>
-            <scope>provided</scope>
-        </dependency>
-
         <!-- jetty deps -->
         <dependency>
             <groupId>org.apache.servicemix.specs</groupId>

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/assemblies/features/standard/src/main/feature/feature.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/standard/src/main/feature/feature.xml b/assemblies/features/standard/src/main/feature/feature.xml
index f50cd65..e7d77c5 100644
--- a/assemblies/features/standard/src/main/feature/feature.xml
+++ b/assemblies/features/standard/src/main/feature/feature.xml
@@ -147,12 +147,6 @@
         <bundle start-level="30" start="true">mvn:org.apache.karaf.log/org.apache.karaf.log.core/${project.version}</bundle>
     </feature>
 
-    <feature name="region" description="Provide Region Support" version="${project.version}">
-        <bundle dependency="true" start-level="20">mvn:org.apache.aries/org.apache.aries.util/${aries.util.version}</bundle>
-        <bundle start-level="30">mvn:org.eclipse.equinox/org.eclipse.equinox.region/${equinox.region.version}</bundle>
-        <bundle start-level="30">mvn:org.apache.karaf.region/org.apache.karaf.region.core/${project.version}</bundle>
-    </feature>
-
     <feature name="package" version="${project.version}" description="Package commands and mbeans">
         <bundle start-level="30">mvn:org.apache.karaf.package/org.apache.karaf.package.core/${project.version}</bundle>
     </feature>

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/command/pom.xml
----------------------------------------------------------------------
diff --git a/features/command/pom.xml b/features/command/pom.xml
index 4f49bb1..5978b38 100644
--- a/features/command/pom.xml
+++ b/features/command/pom.xml
@@ -56,6 +56,11 @@
             <groupId>org.apache.karaf.shell</groupId>
             <artifactId>org.apache.karaf.shell.core</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.eclipse.equinox</groupId>
+            <artifactId>org.eclipse.equinox.region</artifactId>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
     <build>

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java
----------------------------------------------------------------------
diff --git a/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java
index b7f8184..ccd869a 100644
--- a/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java
+++ b/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java
@@ -50,6 +50,9 @@ public class InstallFeatureCommand extends FeaturesCommandSupport {
     @Option(name = "-t", aliases = "--simulate", description = "Perform a simulation only", required = false, multiValued = false)
     boolean simulate;
 
+    @Option(name = "-g", aliases = "--region", description = "Region to install to")
+    String region;
+
     protected void doExecute(FeaturesService admin) throws Exception {
         EnumSet<FeaturesService.Option> options = EnumSet.noneOf(FeaturesService.Option.class);
         if (simulate) {
@@ -64,6 +67,6 @@ public class InstallFeatureCommand extends FeaturesCommandSupport {
         if (verbose) {
             options.add(FeaturesService.Option.Verbose);
         }
-        admin.installFeatures(new HashSet<String>(features), options);
+        admin.installFeatures(new HashSet<String>(features), region, options);
     }
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/command/src/main/java/org/apache/karaf/features/command/RegionInfoCommand.java
----------------------------------------------------------------------
diff --git a/features/command/src/main/java/org/apache/karaf/features/command/RegionInfoCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/RegionInfoCommand.java
new file mode 100644
index 0000000..8a3e73e
--- /dev/null
+++ b/features/command/src/main/java/org/apache/karaf/features/command/RegionInfoCommand.java
@@ -0,0 +1,129 @@
+/*
+ * 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.command;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
+
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.eclipse.equinox.region.Region;
+import org.eclipse.equinox.region.RegionDigraph;
+import org.eclipse.equinox.region.RegionFilter;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+@Command(scope = "feature", name = "regions", description = "Prints information about region digraph.")
+@Service
+public class RegionInfoCommand implements Action {
+
+    @Option(name = "-v", aliases = "--verbose", required = false, description = "Show all info.")
+    boolean verbose;
+
+    @Option(name = "-b", aliases = "--bundles", required = false, description = "Show bundles in each region.")
+    boolean bundles;
+
+    @Option(name = "-f", aliases = "--filters", required = false, description = "Show filters.")
+    boolean filters;
+
+    @Option(name = "-n", aliases = "--namespaces", required = false, description = "Show namespaces in each filter.")
+    boolean namespaces;
+
+    @Argument(index = 0, name = "regions", description = "Regions to provide detailed info for.", required = false, multiValued = true)
+    List<String> regions;
+
+    @Reference
+    RegionDigraph regionDigraph;
+
+    @Reference
+    BundleContext bundleContext;
+
+    public Object execute() throws Exception {
+        System.out.println("Regions");
+        if (regions == null) {
+            for (Region region : regionDigraph.getRegions()) {
+                showRegion(region);
+            }
+        } else {
+            bundles = true;
+            filters = true;
+            namespaces = true;
+            for (String regionName : regions) {
+                Region region = regionDigraph.getRegion(regionName);
+                if (region == null) {
+                    System.out.println("No region " + regionName);
+                } else {
+                    showRegion(region);
+                }
+            }
+        }
+        return null;
+    }
+
+    private void showRegion(Region region) {
+        BundleContext bundleContext = this.bundleContext.getBundle(0).getBundleContext();
+        System.out.println(region.getName());
+        if (verbose || bundles) {
+            for (Long id : new TreeSet<Long>(region.getBundleIds())) {
+                Bundle b = bundleContext.getBundle(id);
+                System.out.println(String.format("  %3d  %s%s", id, getStateString(b), b));
+            }
+        }
+        if (verbose || filters || namespaces) {
+            for (RegionDigraph.FilteredRegion f : region.getEdges()) {
+                System.out.println("  filter to " + f.getRegion().getName());
+                if (verbose || namespaces) {
+                    RegionFilter rf = f.getFilter();
+                    for (Map.Entry<String, Collection<String>> policy : rf.getSharingPolicy().entrySet()) {
+                        String namespace = policy.getKey();
+                        System.out.println("    namespace: " + namespace);
+                        for (String e : policy.getValue()) {
+                            System.out.println("      " + e);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public String getStateString(Bundle bundle) {
+        if (bundle == null) {
+            return "Bundle null ";
+        }
+        int state = bundle.getState();
+        if (state == Bundle.ACTIVE) {
+            return "Active      ";
+        } else if (state == Bundle.INSTALLED) {
+            return "Installed   ";
+        } else if (state == Bundle.RESOLVED) {
+            return "Resolved    ";
+        } else if (state == Bundle.STARTING) {
+            return "Starting    ";
+        } else if (state == Bundle.STOPPING) {
+            return "Stopping    ";
+        } else {
+            return "Unknown     ";
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java
----------------------------------------------------------------------
diff --git a/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java
index e62f697..0fb7d14 100644
--- a/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java
+++ b/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java
@@ -45,6 +45,9 @@ public class UninstallFeatureCommand extends FeaturesCommandSupport {
     @Option(name = "-t", aliases = "--simulate", description = "Perform a simulation only", required = false, multiValued = false)
     boolean simulate;
 
+    @Option(name = "-g", aliases = "--region", description = "Region to install to")
+    String region;
+
     protected void doExecute(FeaturesService admin) throws Exception {
         // iterate in the provided feature
         EnumSet<FeaturesService.Option> options = EnumSet.noneOf(FeaturesService.Option.class);
@@ -57,6 +60,6 @@ public class UninstallFeatureCommand extends FeaturesCommandSupport {
         if (verbose) {
             options.add(FeaturesService.Option.Verbose);
         }
-        admin.uninstallFeatures(new HashSet<String>(features), options);
+        admin.uninstallFeatures(new HashSet<String>(features), region, options);
     }
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/pom.xml
----------------------------------------------------------------------
diff --git a/features/core/pom.xml b/features/core/pom.xml
index d8a2620..8998ba9 100644
--- a/features/core/pom.xml
+++ b/features/core/pom.xml
@@ -72,6 +72,12 @@
         </dependency>
 
         <dependency>
+            <groupId>org.eclipse.equinox</groupId>
+            <artifactId>org.eclipse.equinox.region</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-jdk14</artifactId>
             <scope>test</scope>
@@ -110,8 +116,15 @@
                             org.apache.karaf.features;
                             org.apache.karaf.features.management;
                             org.apache.karaf.features.management.codec;
-                                -noimport:=true
+                                -noimport:=true,
+                            org.osgi.service.resolver;-split-package:=merge-first,
+                            org.osgi.service.repository,
+                            org.eclipse.equinox.region.*
                         </Export-Package>
+                        <Import-Package>
+                            !org.eclipse.osgi.service.resolver,
+                            *
+                        </Import-Package>
                         <Provide-Capability>
                             service-reference;effective:=active;objectClass=org.apache.karaf.features.FeaturesService
                         </Provide-Capability>
@@ -123,8 +136,7 @@
                             org.apache.karaf.util.collections,
                             org.apache.karaf.util.json,
                             org.apache.karaf.util.tracker,
-                            org.osgi.service.resolver,
-                            org.osgi.service.repository
+                            org.eclipse.equinox.internal.region.*;-split-package:=merge-first,
                         </Private-Package>
                         <Embed-Dependency>
                             org.apache.karaf.util;inline="org/apache/karaf/util/XmlUtils*.class"

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/Feature.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/Feature.java b/features/core/src/main/java/org/apache/karaf/features/Feature.java
index 2f9f001..06235e2 100644
--- a/features/core/src/main/java/org/apache/karaf/features/Feature.java
+++ b/features/core/src/main/java/org/apache/karaf/features/Feature.java
@@ -54,10 +54,10 @@ public interface Feature {
     
     int getStartLevel();
 
-    String getRegion();
-
     List<? extends Capability> getCapabilities();
 
     List<? extends Requirement> getRequirements();
 
+    Scoping getScoping();
+
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/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 ef3dbcf..7bb3cf8 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
@@ -71,6 +71,8 @@ public interface FeaturesService {
 
     void installFeatures(Set<String> features, EnumSet<Option> options) throws Exception;
 
+    void installFeatures(Set<String> features, String region, EnumSet<Option> options) throws Exception;
+
     void uninstallFeature(String name, EnumSet<Option> options) throws Exception;
 
     void uninstallFeature(String name) throws Exception;
@@ -81,6 +83,8 @@ public interface FeaturesService {
 
     void uninstallFeatures(Set<String> features, EnumSet<Option> options) throws Exception;
 
+    void uninstallFeatures(Set<String> features, String region, EnumSet<Option> options) throws Exception;
+
     Feature[] listFeatures() throws Exception;
 
     Feature[] listRequiredFeatures() throws Exception;

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/RegionsPersistence.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/RegionsPersistence.java b/features/core/src/main/java/org/apache/karaf/features/RegionsPersistence.java
deleted file mode 100644
index 96ca7da..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/RegionsPersistence.java
+++ /dev/null
@@ -1,26 +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;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleException;
-
-public interface RegionsPersistence {
-    void install(Bundle b, String regionName) throws BundleException;
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/Repository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/Repository.java b/features/core/src/main/java/org/apache/karaf/features/Repository.java
index 3ea12ec..84d4a50 100644
--- a/features/core/src/main/java/org/apache/karaf/features/Repository.java
+++ b/features/core/src/main/java/org/apache/karaf/features/Repository.java
@@ -32,6 +32,4 @@ public interface Repository {
 
     Feature[] getFeatures() throws Exception;
     
-    boolean isValid();
-
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/Resolver.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/Resolver.java b/features/core/src/main/java/org/apache/karaf/features/Resolver.java
deleted file mode 100644
index d2fa941..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/Resolver.java
+++ /dev/null
@@ -1,25 +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;
-
-import java.util.List;
-
-public interface Resolver {
-
-    List<BundleInfo> resolve(Feature feature) throws Exception;
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/ScopeFilter.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/ScopeFilter.java b/features/core/src/main/java/org/apache/karaf/features/ScopeFilter.java
new file mode 100644
index 0000000..c4ddbfe
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/ScopeFilter.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+public interface ScopeFilter {
+
+    String getNamespace();
+
+    String getFilter();
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/Scoping.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/Scoping.java b/features/core/src/main/java/org/apache/karaf/features/Scoping.java
new file mode 100644
index 0000000..b97e6f2
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/Scoping.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+import java.util.List;
+
+public interface Scoping {
+
+    boolean acceptDependencies();
+
+    List<? extends ScopeFilter> getImports();
+
+    List<? extends ScopeFilter> getExports();
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/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
deleted file mode 100644
index c3ac2b7..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/deployment/DeploymentBuilder.java
+++ /dev/null
@@ -1,354 +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.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;
-    Set<Resource> optionals;
-    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.optionals = new HashSet<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);
-            for (Conditional cond : feature.getConditional()) {
-                this.optionals.add(FeatureResource.build(feature, cond, featureRange, resources));
-            }
-        }
-        // Build requirements
-        for (String feature : features) {
-            requireFeature(feature);
-        }
-        for (String bundle : bundles) {
-            requireResource(bundle);
-        }
-        for (String req : reqs) {
-            requireResource(REQ_PROTOCOL + req);
-        }
-        return resources;
-    }
-
-    public Map<Resource, List<Wire>> resolve(List<Resource> systemBundles) 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),
-                false);
-        Map<Resource, List<Wire>> best = resolver.resolve(context);
-
-        // TODO: we actually need to use multiple passes for conditionals
-        // TODO: but it may be optimized by passing the old wiring instead
-        // TODO: of computing everything again
-        Set<Resource> resources = new HashSet<Resource>();
-        resources.add(requirements);
-        for (Resource optional : optionals) {
-            try {
-                Set<Resource> newSet = new HashSet<Resource>(resources);
-                newSet.add(optional);
-                context = new ResolveContextImpl(
-                        newSet,
-                        Collections.<Resource>emptySet(),
-                        new AggregateRepository(repos),
-                        false);
-                best = resolver.resolve(context);
-                resources = newSet;
-            } catch (ResolutionException e) {
-                // Ignore this resource
-            }
-        }
-        return best;
-    }
-
-    public void requireFeature(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() : 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);
-        requirements.addRequirement(
-                new RequirementImpl(requirements, 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/2705ad88/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
deleted file mode 100644
index 2d5dd98..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/deployment/Downloader.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.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/2705ad88/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
deleted file mode 100644
index 60a3dfc..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/deployment/StreamProvider.java
+++ /dev/null
@@ -1,26 +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.deployment;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-public interface StreamProvider {
-
-    InputStream open() throws IOException;
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/download/DownloadCallback.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/download/DownloadCallback.java b/features/core/src/main/java/org/apache/karaf/features/internal/download/DownloadCallback.java
new file mode 100644
index 0000000..b8fda6a
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/download/DownloadCallback.java
@@ -0,0 +1,23 @@
+/*
+ * 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.download;
+
+public interface DownloadCallback {
+
+    void downloaded(StreamProvider provider) throws Exception;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/download/DownloadManager.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/download/DownloadManager.java b/features/core/src/main/java/org/apache/karaf/features/internal/download/DownloadManager.java
new file mode 100644
index 0000000..0bac194
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/download/DownloadManager.java
@@ -0,0 +1,27 @@
+/*
+ * 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.download;
+
+import java.util.Map;
+
+public interface DownloadManager {
+
+    Downloader createDownloader();
+
+    Map<String,StreamProvider> getProviders();
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/download/Downloader.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/download/Downloader.java b/features/core/src/main/java/org/apache/karaf/features/internal/download/Downloader.java
new file mode 100644
index 0000000..baf71a0
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/download/Downloader.java
@@ -0,0 +1,29 @@
+/*
+ * 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.download;
+
+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;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/download/StreamProvider.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/download/StreamProvider.java b/features/core/src/main/java/org/apache/karaf/features/internal/download/StreamProvider.java
new file mode 100644
index 0000000..c25fa43
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/download/StreamProvider.java
@@ -0,0 +1,29 @@
+/*
+ * 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.download;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+public interface StreamProvider {
+
+    InputStream open() throws IOException;
+
+    Map<String, String> getMetadata() throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/download/simple/SimpleDownloader.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/download/simple/SimpleDownloader.java b/features/core/src/main/java/org/apache/karaf/features/internal/download/simple/SimpleDownloader.java
new file mode 100644
index 0000000..4569fe2
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/download/simple/SimpleDownloader.java
@@ -0,0 +1,120 @@
+/*
+ * 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.download.simple;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.apache.karaf.features.internal.download.DownloadCallback;
+import org.apache.karaf.features.internal.download.DownloadManager;
+import org.apache.karaf.features.internal.download.Downloader;
+import org.apache.karaf.features.internal.download.StreamProvider;
+import org.apache.karaf.features.internal.util.MultiException;
+
+public class SimpleDownloader implements DownloadManager, Downloader {
+
+    protected final MultiException exception = new MultiException("Error");
+
+    protected final ConcurrentMap<String, StreamProvider> providers = new ConcurrentHashMap<String, StreamProvider>();
+
+    @Override
+    public Downloader createDownloader() {
+        return this;
+    }
+
+    @Override
+    public void await() throws InterruptedException, MultiException {
+        exception.throwIfExceptions();
+    }
+
+    @Override
+    public void download(final String location, final DownloadCallback downloadCallback) throws MalformedURLException {
+        if (!providers.containsKey(location)) {
+            providers.putIfAbsent(location, createProvider(location));
+        }
+        try {
+            downloadCallback.downloaded(providers.get(location));
+        } catch (Exception e) {
+            exception.addException(e);
+        }
+    }
+
+    protected StreamProvider createProvider(String location) throws MalformedURLException {
+        return new UrlProvider(new URL(location));
+    }
+
+    public Map<String, StreamProvider> getProviders() {
+        return providers;
+    }
+
+    static class UrlProvider implements StreamProvider {
+        private final URL url;
+        private volatile Map<String, String> metadata;
+
+        UrlProvider(URL url) {
+            this.url = url;
+        }
+
+        @Override
+        public InputStream open() throws IOException {
+            return url.openStream();
+        }
+
+        @Override
+        public Map<String, String> getMetadata() throws IOException {
+            if (metadata == null) {
+                synchronized (this) {
+                    if (metadata == null) {
+                        metadata = doGetMetadata();
+                    }
+                }
+            }
+            return metadata;
+        }
+
+        protected Map<String, String> doGetMetadata() throws IOException {
+            InputStream is = open();
+            try {
+                ZipInputStream zis = new ZipInputStream(is);
+                ZipEntry entry;
+                while ((entry = zis.getNextEntry()) != null) {
+                    if ("META-INF/MANIFEST.MF".equals(entry.getName())) {
+                        Attributes attributes = new Manifest(zis).getMainAttributes();
+                        Map<String, String> headers = new HashMap<String, String>();
+                        for (Map.Entry attr : attributes.entrySet()) {
+                            headers.put(attr.getKey().toString(), attr.getValue().toString());
+                        }
+                        return headers;
+                    }
+                }
+            } finally {
+                is.close();
+            }
+            throw new IllegalArgumentException("Resource " + url + " does not contain a manifest");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
index 46580da..0aa5eb5 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
@@ -75,7 +75,8 @@ import javax.xml.bind.annotation.XmlType;
     "bundle",
     "conditional",
     "capability",
-    "requirement"
+    "requirement",
+    "scoping"
 })
 public class Feature extends Content implements org.apache.karaf.features.Feature {
     public static String SPLIT_FOR_NAME_AND_VERSION = "/";
@@ -95,11 +96,10 @@ public class Feature extends Content implements org.apache.karaf.features.Featur
     protected String install;
     @XmlAttribute(name = "start-level")
     protected Integer startLevel;
-    @XmlAttribute
-    protected String region;
     protected List<Conditional> conditional;
     protected List<Capability> capability;
     protected List<Requirement> requirement;
+    protected Scoping scoping;
 
     public Feature() {
     }
@@ -280,15 +280,6 @@ public class Feature extends Content implements org.apache.karaf.features.Featur
         this.startLevel = value;
     }
 
-
-    public String getRegion() {
-        return region;
-    }
-
-    public void setRegion(String region) {
-        this.region = region;
-    }
-
     /**
      * Gets the value of the conditional property.
      * <p/>
@@ -330,6 +321,14 @@ public class Feature extends Content implements org.apache.karaf.features.Featur
         return this.requirement;
     }
 
+    public Scoping getScoping() {
+        return scoping;
+    }
+
+    public void setScoping(Scoping scoping) {
+        this.scoping = scoping;
+    }
+
     public String toString() {
     	return getId();
     }

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java
index 2036452..9b5d7b6 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java
@@ -41,6 +41,8 @@ import javax.xml.validation.SchemaFactory;
 
 import org.apache.karaf.features.FeaturesNamespaces;
 import org.apache.karaf.util.XmlUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
@@ -53,6 +55,7 @@ import org.xml.sax.helpers.XMLFilterImpl;
 
 public class JaxbUtil {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(JaxbUtil.class);
     private static final JAXBContext FEATURES_CONTEXT;
     static {
         try {
@@ -112,11 +115,16 @@ public class JaxbUtil {
                 doc = XmlUtils.parse(uri);
             }
 
-            Schema schema = getSchema(doc.getDocumentElement().getNamespaceURI());
-            try {
-                schema.newValidator().validate(new DOMSource(doc));
-            } catch (SAXException e) {
-                throw new IllegalArgumentException("Unable to validate " + uri, e);
+            String nsuri = doc.getDocumentElement().getNamespaceURI();
+            if (nsuri == null) {
+                LOGGER.warn("Old style feature file without namespace found (URI: {}). This format is deprecated and support for it will soon be removed", uri);
+            } else {
+                Schema schema = getSchema(nsuri);
+                try {
+                    schema.newValidator().validate(new DOMSource(doc));
+                } catch (SAXException e) {
+                    throw new IllegalArgumentException("Unable to validate " + uri, e);
+                }
             }
 
             fixDom(doc, doc.getDocumentElement());
@@ -162,12 +170,14 @@ public class JaxbUtil {
 
 
     private static void fixDom(Document doc, Node node) {
-        if (node.getNamespaceURI() != null && !FeaturesNamespaces.URI_CURRENT.equals(node.getNamespaceURI())) {
-            doc.renameNode(node, FeaturesNamespaces.URI_CURRENT, node.getLocalName());
-        }
-        NodeList children = node.getChildNodes();
-        for (int i = 0; i < children.getLength(); i++) {
-            fixDom(doc, children.item(i));
+        if (node.getNodeType() == Node.ELEMENT_NODE) {
+            if (!FeaturesNamespaces.URI_CURRENT.equals(node.getNamespaceURI())) {
+                doc.renameNode(node, FeaturesNamespaces.URI_CURRENT, node.getLocalName());
+            }
+            NodeList children = node.getChildNodes();
+            for (int i = 0; i < children.getLength(); i++) {
+                fixDom(doc, children.item(i));
+            }
         }
     }
 
@@ -177,7 +187,6 @@ public class JaxbUtil {
             XMLFilter xmlFilter = new NoSourceAndNamespaceFilter(XmlUtils.xmlReader());
             xmlFilter.setContentHandler(unmarshaller.getUnmarshallerHandler());
 
-
             InputSource is = new InputSource(uri);
             if (stream != null) {
                 is.setByteStream(stream);

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/model/ScopeFilter.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/ScopeFilter.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/ScopeFilter.java
new file mode 100644
index 0000000..4b81fed
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/ScopeFilter.java
@@ -0,0 +1,56 @@
+/*
+ * 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.model;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlValue;
+
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "scopeFilter", propOrder = {
+        "value"
+})
+public class ScopeFilter implements org.apache.karaf.features.ScopeFilter {
+
+    @XmlAttribute(required = true)
+    protected String namespace;
+    @XmlValue
+    protected String value;
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String getFilter() {
+        return getValue();
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/model/Scoping.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Scoping.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Scoping.java
new file mode 100644
index 0000000..6773995
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/Scoping.java
@@ -0,0 +1,70 @@
+/*
+ * 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.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlType;
+
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "scoping", propOrder = {
+        "imports",
+        "exports"
+})
+public class Scoping implements org.apache.karaf.features.Scoping {
+
+    @XmlAttribute
+    boolean acceptDependencies;
+    @XmlElement(name = "import")
+    List<ScopeFilter> imports;
+    @XmlElement(name = "export")
+    List<ScopeFilter> exports;
+
+    public List<ScopeFilter> getImport() {
+        if (imports == null) {
+            imports = new ArrayList<ScopeFilter>();
+        }
+        return imports;
+    }
+
+    public List<ScopeFilter> getExport() {
+        if (exports == null) {
+            exports = new ArrayList<ScopeFilter>();
+        }
+        return exports;
+    }
+
+    @Override
+    public boolean acceptDependencies() {
+        return acceptDependencies;
+    }
+
+    @Override
+    public List<? extends org.apache.karaf.features.ScopeFilter> getImports() {
+        return getImport();
+    }
+
+    @Override
+    public List<? extends org.apache.karaf.features.ScopeFilter> getExports() {
+        return getExport();
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java b/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
index be0da05..0551d21 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
@@ -27,8 +27,10 @@ import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.Properties;
 
+import org.apache.felix.resolver.ResolverImpl;
 import org.apache.karaf.features.FeaturesListener;
 import org.apache.karaf.features.FeaturesService;
+import org.apache.karaf.features.internal.resolver.Slf4jResolverLog;
 import org.apache.karaf.features.internal.service.EventAdminListener;
 import org.apache.karaf.features.internal.service.FeatureConfigInstaller;
 import org.apache.karaf.features.internal.service.FeatureFinder;
@@ -36,25 +38,35 @@ import org.apache.karaf.features.internal.service.BootFeaturesInstaller;
 import org.apache.karaf.features.internal.service.FeaturesServiceImpl;
 import org.apache.karaf.features.internal.service.StateStorage;
 import org.apache.karaf.features.internal.management.FeaturesServiceMBeanImpl;
-import org.apache.karaf.features.RegionsPersistence;
 import org.apache.karaf.util.tracker.BaseActivator;
 import org.apache.karaf.util.tracker.SingleServiceTracker;
+import org.eclipse.equinox.internal.region.DigraphHelper;
+import org.eclipse.equinox.internal.region.StandardRegionDigraph;
+import org.eclipse.equinox.internal.region.management.StandardManageableRegionDigraph;
+import org.eclipse.equinox.region.RegionDigraph;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
+import org.osgi.framework.hooks.bundle.CollisionHook;
+import org.osgi.framework.hooks.resolver.ResolverHookFactory;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.cm.ManagedService;
+import org.osgi.service.resolver.Resolver;
 import org.osgi.service.url.URLStreamHandlerService;
 import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.LoggerFactory;
 
 public class Activator extends BaseActivator {
 
     public static final String FEATURES_REPOS_PID = "org.apache.karaf.features.repos";
     public static final String FEATURES_SERVICE_CONFIG_FILE = "org.apache.karaf.features.cfg";
 
+    private static final String STATE_FILE = "state.json";
+
     private ServiceTracker<FeaturesListener, FeaturesListener> featuresListenerTracker;
     private FeaturesServiceImpl featuresService;
-    private SingleServiceTracker<RegionsPersistence> regionsTracker;
+    private StandardRegionDigraph digraph;
+    private StandardManageableRegionDigraph digraphMBean;
 
     public Activator() {
         // Special case here, as we don't want the activator to wait for current job to finish,
@@ -87,32 +99,28 @@ public class Activator extends BaseActivator {
             return;
         }
 
+        // Resolver
+        register(Resolver.class, new ResolverImpl(new Slf4jResolverLog(LoggerFactory.getLogger(ResolverImpl.class))));
+
+        // RegionDigraph
+        digraph = DigraphHelper.loadDigraph(bundleContext);
+        register(ResolverHookFactory.class, digraph.getResolverHookFactory());
+        register(CollisionHook.class, DigraphHelper.getCollisionHook(digraph));
+        register(org.osgi.framework.hooks.bundle.FindHook.class, digraph.getBundleFindHook());
+        register(org.osgi.framework.hooks.bundle.EventHook.class, digraph.getBundleEventHook());
+        register(org.osgi.framework.hooks.service.FindHook.class, digraph.getServiceFindHook());
+        register(org.osgi.framework.hooks.service.EventHook.class, digraph.getServiceEventHook());
+        register(RegionDigraph.class, digraph);
+        digraphMBean = new StandardManageableRegionDigraph(digraph, "org.apache.karaf", bundleContext);
+        digraphMBean.registerMBean();
+
+
         FeatureFinder featureFinder = new FeatureFinder();
         Hashtable<String, Object> props = new Hashtable<String, Object>();
         props.put(Constants.SERVICE_PID, FEATURES_REPOS_PID);
         register(ManagedService.class, featureFinder, props);
 
-        // TODO: region support
-//        final BundleManager bundleManager = new BundleManager(bundleContext);
-//        regionsTracker = new SingleServiceTracker<RegionsPersistence>(bundleContext, RegionsPersistence.class,
-//                new SingleServiceTracker.SingleServiceListener() {
-//                    @Override
-//                    public void serviceFound() {
-//                        bundleManager.setRegionsPersistence(regionsTracker.getService());
-//                    }
-//                    @Override
-//                    public void serviceLost() {
-//                        serviceFound();
-//                    }
-//                    @Override
-//                    public void serviceReplaced() {
-//                        serviceFound();
-//                    }
-//                });
-//        regionsTracker.open();
-
-
-        FeatureConfigInstaller configInstaller = new FeatureConfigInstaller(configurationAdmin);
+       FeatureConfigInstaller configInstaller = new FeatureConfigInstaller(configurationAdmin);
         // TODO: honor respectStartLvlDuringFeatureStartup and respectStartLvlDuringFeatureUninstall
 //        boolean respectStartLvlDuringFeatureStartup = getBoolean("respectStartLvlDuringFeatureStartup", true);
 //        boolean respectStartLvlDuringFeatureUninstall = getBoolean("respectStartLvlDuringFeatureUninstall", true);
@@ -123,7 +131,7 @@ public class Activator extends BaseActivator {
         StateStorage stateStorage = new StateStorage() {
             @Override
             protected InputStream getInputStream() throws IOException {
-                File file = bundleContext.getDataFile("FeaturesServiceState.properties");
+                File file = bundleContext.getDataFile(STATE_FILE);
                 if (file.exists()) {
                     return new FileInputStream(file);
                 } else {
@@ -133,7 +141,7 @@ public class Activator extends BaseActivator {
 
             @Override
             protected OutputStream getOutputStream() throws IOException {
-                File file = bundleContext.getDataFile("FeaturesServiceState.properties");
+                File file = bundleContext.getDataFile(STATE_FILE);
                 return new FileOutputStream(file);
             }
         };
@@ -150,6 +158,7 @@ public class Activator extends BaseActivator {
                                 featureFinder,
                                 eventAdminListener,
                                 configInstaller,
+                                digraph,
                                 overrides,
                                 featureResolutionRange,
                                 bundleUpdateRange,
@@ -191,9 +200,9 @@ public class Activator extends BaseActivator {
     }
 
     protected void doStop() {
-        if (regionsTracker != null) {
-            regionsTracker.close();
-            regionsTracker = null;
+        if (digraphMBean != null) {
+            digraphMBean.unregisterMbean();
+            digraphMBean = null;
         }
         if (featuresListenerTracker != null) {
             featuresListenerTracker.close();
@@ -203,6 +212,14 @@ public class Activator extends BaseActivator {
         if (featuresService != null) {
             featuresService = null;
         }
+        if (digraph != null) {
+            try {
+                DigraphHelper.saveDigraph(bundleContext, digraph);
+            } catch (Exception e) {
+                // Ignore
+            }
+            digraph = null;
+        }
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/region/AbstractRegionDigraphVisitor.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/region/AbstractRegionDigraphVisitor.java b/features/core/src/main/java/org/apache/karaf/features/internal/region/AbstractRegionDigraphVisitor.java
new file mode 100644
index 0000000..3ad2325
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/region/AbstractRegionDigraphVisitor.java
@@ -0,0 +1,121 @@
+/*
+ * 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.region;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.equinox.region.Region;
+import org.eclipse.equinox.region.RegionDigraphVisitor;
+import org.eclipse.equinox.region.RegionFilter;
+
+/**
+ * {@link AbstractRegionDigraphVisitor} is an abstract base class for {@link RegionDigraphVisitor} implementations
+ */
+public abstract class AbstractRegionDigraphVisitor<C> implements RegionDigraphVisitor {
+
+    private final Collection<C> allCandidates;
+    private final Deque<Set<C>> allowedDeque = new ArrayDeque<Set<C>>();
+    private final Deque<Collection<C>> filteredDeque = new ArrayDeque<Collection<C>>();
+    private Set<C> allowed = new HashSet<C>();
+
+    public AbstractRegionDigraphVisitor(Collection<C> candidates) {
+        this.allCandidates = candidates;
+    }
+
+    public Collection<C> getAllowed() {
+        return allowed;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean visit(Region region) {
+        Collection<C> candidates = filteredDeque.isEmpty() ? allCandidates : filteredDeque.peek();
+        for (C candidate : candidates) {
+            if (contains(region, candidate)) {
+                allowed.add(candidate);
+            }
+        }
+        // there is no need to traverse edges of this region,
+        // it contains all the remaining filtered candidates
+        return !allowed.containsAll(candidates);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean preEdgeTraverse(RegionFilter regionFilter) {
+        // Find the candidates filtered by the previous edge
+        Collection<C> filtered = filteredDeque.isEmpty() ? allCandidates : filteredDeque.peek();
+        Collection<C> candidates = new ArrayList<C>(filtered);
+        // remove any candidates contained in the current region
+        candidates.removeAll(allowed);
+        // apply the filter across remaining candidates
+        Iterator<C> i = candidates.iterator();
+        while (i.hasNext()) {
+            C candidate = i.next();
+            if (!isAllowed(candidate, regionFilter)) {
+                i.remove();
+            }
+        }
+        if (candidates.isEmpty())
+            return false; // this filter does not apply; avoid traversing this edge
+        // push the filtered candidates for the next region
+        filteredDeque.push(candidates);
+        // push the allowed
+        allowedDeque.push(allowed);
+        allowed = new HashSet<C>();
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void postEdgeTraverse(RegionFilter regionFilter) {
+        filteredDeque.poll();
+        Collection<C> candidates = allowed;
+        allowed = allowedDeque.pop();
+        allowed.addAll(candidates);
+    }
+
+    /**
+     * Determines whether the given region contains the given candidate.
+     *
+     * @param region the {@link Region}
+     * @param candidate the candidate
+     * @return <code>true</code> if and only if the given region contains the given candidate
+     */
+    protected abstract boolean contains(Region region, C candidate);
+
+    /**
+     * Determines whether the given candidate is allowed by the given {@link RegionFilter}.
+     *
+     * @param candidate the candidate
+     * @param filter the filter
+     * @return <code>true</code> if and only if the given candidate is allowed by the given filter
+     */
+    protected abstract boolean isAllowed(C candidate, RegionFilter filter);
+}
\ No newline at end of file