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:54 UTC

[4/6] [KARAF-2923] Region support in features service

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/region/CandidateComparator.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/region/CandidateComparator.java b/features/core/src/main/java/org/apache/karaf/features/internal/region/CandidateComparator.java
new file mode 100644
index 0000000..74c014b
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/region/CandidateComparator.java
@@ -0,0 +1,125 @@
+/*
+ * 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.Comparator;
+
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.resource.Capability;
+
+public class CandidateComparator implements Comparator<Capability>
+{
+    public int compare(Capability cap1, Capability cap2)
+    {
+        int c = 0;
+        // Always prefer system bundle
+        if (cap1 instanceof BundleCapability && !(cap2 instanceof BundleCapability)) {
+            c = -1;
+        } else if (!(cap1 instanceof BundleCapability) && cap2 instanceof BundleCapability) {
+            c = 1;
+        }
+        // Compare revision capabilities.
+        if ((c == 0) && cap1.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE))
+        {
+            c = ((Comparable) cap1.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE))
+                    .compareTo(cap2.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE));
+            if (c == 0)
+            {
+                Version v1 = (!cap1.getAttributes().containsKey(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE))
+                        ? Version.emptyVersion
+                        : (Version) cap1.getAttributes().get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
+                Version v2 = (!cap2.getAttributes().containsKey(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE))
+                        ? Version.emptyVersion
+                        : (Version) cap2.getAttributes().get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
+                // Compare these in reverse order, since we want
+                // highest version to have priority.
+                c = compareVersions(v2, v1);
+            }
+        }
+        // Compare package capabilities.
+        else if ((c == 0) && cap1.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+        {
+            c = ((Comparable) cap1.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))
+                    .compareTo(cap2.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE));
+            if (c == 0)
+            {
+                Version v1 = (!cap1.getAttributes().containsKey(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE))
+                        ? Version.emptyVersion
+                        : (Version) cap1.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+                Version v2 = (!cap2.getAttributes().containsKey(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE))
+                        ? Version.emptyVersion
+                        : (Version) cap2.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+                // Compare these in reverse order, since we want
+                // highest version to have priority.
+                c = compareVersions(v2, v1);
+                // if same version, rather compare on the bundle version
+                if (c == 0)
+                {
+                    v1 = (!cap1.getAttributes().containsKey(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE))
+                            ? Version.emptyVersion
+                            : (Version) cap1.getAttributes().get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
+                    v2 = (!cap2.getAttributes().containsKey(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE))
+                            ? Version.emptyVersion
+                            : (Version) cap2.getAttributes().get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
+                    // Compare these in reverse order, since we want
+                    // highest version to have priority.
+                    c = compareVersions(v2, v1);
+                }
+            }
+        }
+        // Compare feature capabilities
+        else if ((c == 0) && cap1.getNamespace().equals(IdentityNamespace.IDENTITY_NAMESPACE))
+        {
+            c = ((Comparable) cap1.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE))
+                    .compareTo(cap2.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE));
+            if (c == 0)
+            {
+                Version v1 = (!cap1.getAttributes().containsKey(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE))
+                        ? Version.emptyVersion
+                        : (Version) cap1.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+                Version v2 = (!cap2.getAttributes().containsKey(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE))
+                        ? Version.emptyVersion
+                        : (Version) cap2.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+                // Compare these in reverse order, since we want
+                // highest version to have priority.
+                c = compareVersions(v2, v1);
+            }
+        }
+        return c;
+    }
+
+    private int compareVersions(Version v1, Version v2) {
+        int c = v1.getMajor() - v2.getMajor();
+        if (c != 0) {
+            return c;
+        }
+        c = v1.getMinor() - v2.getMinor();
+        if (c != 0) {
+            return c;
+        }
+        c = v1.getMicro() - v2.getMicro();
+        if (c != 0) {
+            return c;
+        }
+        return v1.getQualifier().compareTo(v2.getQualifier());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/region/ResourceComparator.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/region/ResourceComparator.java b/features/core/src/main/java/org/apache/karaf/features/internal/region/ResourceComparator.java
new file mode 100644
index 0000000..19ba529
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/region/ResourceComparator.java
@@ -0,0 +1,43 @@
+/*
+ * 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.Comparator;
+
+import org.apache.felix.resolver.Util;
+import org.osgi.framework.Version;
+import org.osgi.resource.Resource;
+
+public class ResourceComparator implements Comparator<Resource> {
+
+    @Override
+    public int compare(Resource o1, Resource o2) {
+        String bsn1 = Util.getSymbolicName(o1);
+        String bsn2 = Util.getSymbolicName(o2);
+        int c = bsn1.compareTo(bsn2);
+        if (c == 0) {
+            Version v1 = Util.getVersion(o1);
+            Version v2 = Util.getVersion(o2);
+            c = v1.compareTo(v2);
+            if (c == 0) {
+                c = o1.hashCode() - o2.hashCode();
+            }
+        }
+        return c;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java b/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java
new file mode 100644
index 0000000..8d55317
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java
@@ -0,0 +1,341 @@
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.felix.resolver.Util;
+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.ScopeFilter;
+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.resolver.FeatureResource;
+import org.apache.karaf.features.internal.resolver.RequirementImpl;
+import org.apache.karaf.features.internal.resolver.ResourceBuilder;
+import org.apache.karaf.features.internal.resolver.ResourceImpl;
+import org.apache.karaf.features.internal.resolver.ResourceUtils;
+import org.eclipse.equinox.region.RegionFilter;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Version;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+import static org.apache.karaf.features.internal.resolver.ResourceUtils.TYPE_FEATURE;
+import static org.apache.karaf.features.internal.resolver.ResourceUtils.TYPE_SUBSYSTEM;
+import static org.apache.karaf.features.internal.resolver.ResourceUtils.addIdentityRequirement;
+import static org.apache.karaf.features.internal.util.MapUtils.addToMapSet;
+import static org.osgi.framework.namespace.IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE;
+import static org.osgi.framework.namespace.IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE;
+import static org.osgi.framework.namespace.IdentityNamespace.IDENTITY_NAMESPACE;
+
+public class Subsystem extends ResourceImpl {
+
+    private static final String ALL_FILTER = "(|(!(all=*))(all=*))";
+
+    private static final String SUBSYSTEM_FILTER = String.format("(%s=%s)", CAPABILITY_TYPE_ATTRIBUTE, TYPE_SUBSYSTEM);
+
+    private static final String FEATURE_FILTER = String.format("(%s=%s)", CAPABILITY_TYPE_ATTRIBUTE, TYPE_FEATURE);
+
+    private static final String SUBSYSTEM_OR_FEATURE_FILTER = String.format("(|%s%s)", SUBSYSTEM_FILTER, FEATURE_FILTER);
+
+    // Everything is visible
+    private static final Map<String, Set<String>> SHARE_ALL_POLICY =
+            Collections.singletonMap(
+                    RegionFilter.VISIBLE_ALL_NAMESPACE,
+                    Collections.singleton(ALL_FILTER));
+
+    // Nothing (but systems) is visible
+    private static final Map<String, Set<String>> SHARE_NONE_POLICY =
+            Collections.singletonMap(
+                    IDENTITY_NAMESPACE,
+                    Collections.singleton(SUBSYSTEM_FILTER));
+
+    private final String name;
+    private final boolean acceptDependencies;
+    private final Subsystem parent;
+    private final Feature feature;
+    private final List<Subsystem> children = new ArrayList<Subsystem>();
+    private final Map<String, Set<String>> importPolicy;
+    private final Map<String, Set<String>> exportPolicy;
+    private final List<Resource> installable = new ArrayList<Resource>();
+    private final Map<String, DependencyInfo> dependencies = new HashMap<String, DependencyInfo>();
+
+    public Subsystem(String name) {
+        super(name, TYPE_SUBSYSTEM, Version.emptyVersion);
+        this.name = name;
+        this.parent = null;
+        this.acceptDependencies = true;
+        this.feature = null;
+        this.importPolicy = SHARE_NONE_POLICY;
+        this.exportPolicy = SHARE_NONE_POLICY;
+    }
+
+    public Subsystem(String name, Feature feature, Subsystem parent) {
+        super(name, TYPE_SUBSYSTEM, Version.emptyVersion);
+        this.name = name;
+        this.parent = parent;
+        this.acceptDependencies = feature.getScoping() != null && feature.getScoping().acceptDependencies();
+        this.feature = feature;
+        if (feature.getScoping() != null) {
+            this.importPolicy = createPolicy(feature.getScoping().getImports());
+            this.importPolicy.put(IDENTITY_NAMESPACE, Collections.singleton(SUBSYSTEM_OR_FEATURE_FILTER));
+            this.exportPolicy = createPolicy(feature.getScoping().getExports());
+            this.exportPolicy.put(IDENTITY_NAMESPACE, Collections.singleton(SUBSYSTEM_OR_FEATURE_FILTER));
+        } else {
+            this.importPolicy = SHARE_ALL_POLICY;
+            this.exportPolicy = SHARE_ALL_POLICY;
+        }
+
+        Map<String, String> dirs = new HashMap<String, String>();
+        Map<String, Object> attrs = new HashMap<String, Object>();
+        attrs.put(IDENTITY_NAMESPACE, feature.getName());
+        attrs.put(CAPABILITY_TYPE_ATTRIBUTE, TYPE_FEATURE);
+        attrs.put(CAPABILITY_VERSION_ATTRIBUTE, new VersionRange(VersionTable.getVersion(feature.getVersion()), true));
+        Requirement requirement = new RequirementImpl(this, IDENTITY_NAMESPACE, dirs, attrs);
+        addRequirement(requirement);
+    }
+
+    public Subsystem(String name, Subsystem parent, boolean acceptDependencies) {
+        super(name, TYPE_SUBSYSTEM, Version.emptyVersion);
+        this.name = name;
+        this.parent = parent;
+        this.acceptDependencies = acceptDependencies;
+        this.feature = null;
+        this.importPolicy = SHARE_ALL_POLICY;
+        this.exportPolicy = SHARE_NONE_POLICY;
+    }
+
+    public List<Resource> getInstallable() {
+        return installable;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Subsystem getParent() {
+        return parent;
+    }
+
+    public Collection<Subsystem> getChildren() {
+        return children;
+    }
+
+    public Subsystem getChild(String name) {
+        for (Subsystem child : children) {
+            if (child.getName().equals(name)) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+    public boolean isAcceptDependencies() {
+        return acceptDependencies;
+    }
+
+    public Map<String, Set<String>> getImportPolicy() {
+        return importPolicy;
+    }
+
+    public Map<String, Set<String>> getExportPolicy() {
+        return exportPolicy;
+    }
+
+    public Feature getFeature() {
+        return feature;
+    }
+
+    public Subsystem createSubsystem(String name, boolean acceptDependencies) {
+        if (feature != null) {
+            throw new UnsupportedOperationException("Can not create application subsystems inside a feature subsystem");
+        }
+        // Create subsystem
+        Subsystem as = new Subsystem(getName() + "/" + name, this, acceptDependencies);
+        children.add(as);
+        // Add a requirement to force its resolution
+        Capability identity = as.getCapabilities(IDENTITY_NAMESPACE).iterator().next();
+        Object bsn = identity.getAttributes().get(IDENTITY_NAMESPACE);
+        Requirement requirement = new RequirementImpl(this, IDENTITY_NAMESPACE,
+                Collections.<String,String>emptyMap(),
+                Collections.singletonMap(IDENTITY_NAMESPACE, bsn));
+        addRequirement(requirement);
+        // Add it to repo
+        installable.add(as);
+        return as;
+    }
+
+    public void addSystemResource(Resource resource) {
+        installable.add(resource);
+    }
+
+    public void requireFeature(String name, String range) {
+        ResourceUtils.addIdentityRequirement(this, name, TYPE_FEATURE, range);
+    }
+
+    @SuppressWarnings("InfiniteLoopStatement")
+    public void preResolve(Collection<Feature> features,
+                           DownloadManager manager,
+                           String featureResolutionRange) throws Exception {
+        for (Subsystem child : children) {
+            child.preResolve(features, manager, featureResolutionRange);
+        }
+        List<Requirement> processed = new ArrayList<Requirement>();
+        while (true) {
+            List<Requirement> requirements = getRequirements(IDENTITY_NAMESPACE);
+            requirements.removeAll(processed);
+            if (requirements.isEmpty()) {
+                break;
+            }
+            for (Requirement requirement : requirements) {
+                String name = (String) requirement.getAttributes().get(IDENTITY_NAMESPACE);
+                String type = (String) requirement.getAttributes().get(CAPABILITY_TYPE_ATTRIBUTE);
+                VersionRange range = (VersionRange) requirement.getAttributes().get(CAPABILITY_VERSION_ATTRIBUTE);
+                if (TYPE_FEATURE.equals(type)) {
+                    for (Feature feature : features) {
+                        if (feature.getName().equals(name)
+                                && (range == null || range.contains(VersionTable.getVersion(feature.getVersion())))) {
+                            if (feature != this.feature) {
+                                String ssName = this.name + "#" + (feature.hasVersion() ? feature.getName() + "-" + feature.getVersion() : feature.getName());
+                                Subsystem fs = getChild(ssName);
+                                if (fs == null) {
+                                    fs = new Subsystem(ssName, feature, this);
+                                    fs.preResolve(features, manager, featureResolutionRange);
+                                    installable.add(fs);
+                                    children.add(fs);
+                                }
+                            }
+                        }
+                    }
+                }
+                processed.add(requirement);
+            }
+        }
+        if (feature != null) {
+            final Map<String, Resource> bundles = new ConcurrentHashMap<String, Resource>();
+            final Downloader downloader = manager.createDownloader();
+            final Map<BundleInfo, Boolean> infos = new HashMap<BundleInfo, Boolean>();
+            for (Conditional cond : feature.getConditional()) {
+                for (final BundleInfo bi : cond.getBundles()) {
+                    infos.put(bi, false);
+                }
+            }
+            for (BundleInfo bi : feature.getBundles()) {
+                infos.put(bi, true);
+            }
+            for (Map.Entry<BundleInfo, Boolean> entry : infos.entrySet()) {
+                final BundleInfo bi = entry.getKey();
+                final String loc = bi.getLocation();
+                final boolean mandatory = entry.getValue();
+                downloader.download(loc, new DownloadCallback() {
+                    @Override
+                    public void downloaded(StreamProvider provider) throws Exception {
+                        ResourceImpl res = createResource(loc, provider.getMetadata());
+                        bundles.put(loc, res);
+                        if (bi.isDependency()) {
+                            addDependency(res, false);
+                        } else {
+                            doAddDependency(res, mandatory);
+                        }
+                    }
+                });
+            }
+            downloader.await();
+            for (Dependency dep : feature.getDependencies()) {
+                Subsystem ss = this;
+                while (!ss.isAcceptDependencies()) {
+                    ss = ss.getParent();
+                }
+                ss.requireFeature(dep.getName(), dep.getVersion());
+            }
+            for (Conditional cond : feature.getConditional()) {
+                FeatureResource resCond = FeatureResource.build(feature, cond, featureResolutionRange, bundles);
+                addIdentityRequirement(this, resCond, false);
+                installable.add(resCond);
+            }
+
+            FeatureResource res = FeatureResource.build(feature, featureResolutionRange, bundles);
+            addIdentityRequirement(res, this);
+            installable.add(res);
+        }
+        // Compute dependencies
+        for (DependencyInfo info : dependencies.values()) {
+            installable.add(info.resource);
+            addIdentityRequirement(info.resource, this, info.mandatory);
+       }
+    }
+
+    void addDependency(ResourceImpl resource, boolean mandatory) {
+        if (isAcceptDependencies()) {
+            doAddDependency(resource, mandatory);
+        } else {
+            parent.addDependency(resource, mandatory);
+        }
+    }
+
+    private void doAddDependency(ResourceImpl resource, boolean mandatory) {
+        String id = Util.getSymbolicName(resource) + "|" + Util.getVersion(resource);
+        DependencyInfo info = dependencies.get(id);
+        if (info == null) {
+            info = new DependencyInfo();
+            dependencies.put(id, info);
+        }
+        info.resource = resource;
+        info.mandatory |= mandatory;
+    }
+
+    class DependencyInfo {
+        ResourceImpl resource;
+        boolean mandatory;
+    }
+
+    Map<String, Set<String>> createPolicy(List<? extends ScopeFilter> filters) {
+        Map<String, Set<String>> policy = new HashMap<String, Set<String>>();
+        for (ScopeFilter filter : filters) {
+            addToMapSet(policy, filter.getNamespace(), filter.getFilter());
+        }
+        return policy;
+    }
+
+    ResourceImpl createResource(String uri, Map<String, String> headers) throws Exception {
+        try {
+            return ResourceBuilder.build(uri, headers);
+        } catch (BundleException e) {
+            throw new Exception("Unable to create resource for bundle " + uri, e);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java
new file mode 100644
index 0000000..e5c6532
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java
@@ -0,0 +1,188 @@
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.resolver.Util;
+import org.apache.karaf.features.internal.repository.StaticRepository;
+import org.eclipse.equinox.region.Region;
+import org.eclipse.equinox.region.RegionDigraph;
+import org.eclipse.equinox.region.RegionFilter;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+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.Wiring;
+import org.osgi.service.repository.Repository;
+import org.osgi.service.resolver.HostedCapability;
+import org.osgi.service.resolver.ResolveContext;
+
+public class SubsystemResolveContext extends ResolveContext {
+
+    private final Subsystem root;
+    private final RegionDigraph digraph;
+    private final CandidateComparator candidateComparator = new CandidateComparator();
+
+    private final Map<Resource, Subsystem> resToSub = new HashMap<Resource, Subsystem>();
+    private final Repository repository;
+
+
+    public SubsystemResolveContext(Subsystem root, RegionDigraph digraph) throws BundleException {
+        this.root = root;
+        this.digraph = digraph;
+
+        prepare(root);
+        repository = new StaticRepository(resToSub.keySet());
+    }
+
+    void prepare(Subsystem subsystem) {
+        resToSub.put(subsystem, subsystem);
+        for (Resource res : subsystem.getInstallable()) {
+            resToSub.put(res, subsystem);
+        }
+        for (Subsystem child : subsystem.getChildren()) {
+            prepare(child);
+        }
+    }
+
+    @Override
+    public Collection<Resource> getMandatoryResources() {
+        return Collections.<Resource>singleton(root);
+    }
+
+    @Override
+    public List<Capability> findProviders(Requirement requirement) {
+        List<Capability> caps = new ArrayList<Capability>();
+        Region requirerRegion = getRegion(requirement.getResource());
+        if (requirerRegion != null) {
+            Map<Requirement, Collection<Capability>> resMap =
+                    repository.findProviders(Collections.singleton(requirement));
+            Collection<Capability> res = resMap != null ? resMap.get(requirement) : null;
+            if (res != null) {
+                caps.addAll(res);
+            }
+            // Use the digraph to prune non visible capabilities
+            Visitor visitor = new Visitor(caps);
+            requirerRegion.visitSubgraph(visitor);
+            Collection<Capability> allowed = visitor.getAllowed();
+            caps.retainAll(allowed);
+            // Handle cases where the same bundle is requested from both
+            // a subsystem and one of its ascendant.  In such cases, we
+            // need to remove the one from the child if it can view
+            // the parent one
+            if (caps.size() > 1) {
+                Map<String, Resource> providers = new HashMap<String, Resource>();
+                for (Capability cap : caps) {
+                    Resource resource = cap.getResource();
+                    String id = Util.getSymbolicName(resource) + "|" + Util.getVersion(resource);
+                    Resource prev = providers.get(id);
+                    if (prev != null && prev != resource) {
+                        String r1 = getRegion(prev).getName();
+                        String r2 = getRegion(resource).getName();
+                        int c = r1.compareTo(r2);
+                        if (c == 0) {
+                            // This should never happen because resource have been
+                            // de-duplicated during the pre-resolution phase.
+                            throw new IllegalStateException();
+                        }
+                        resource = c < 0 ? prev : resource;
+                    }
+                    providers.put(id, resource);
+                }
+                for (Iterator<Capability> it = caps.iterator(); it.hasNext();) {
+                    Capability cap = it.next();
+                    if (!providers.values().contains(cap.getResource())) {
+                        it.remove();
+                    }
+                }
+            }
+            // Sort caps
+            Collections.sort(caps, candidateComparator);
+        }
+        return caps;
+    }
+
+    private Subsystem getSubsystem(Resource resource) {
+        return resToSub.get(resource);
+    }
+
+    private Region getRegion(Resource resource) {
+        return digraph.getRegion(getSubsystem(resource).getName());
+    }
+
+    @Override
+    public int insertHostedCapability(List<Capability> capabilities, HostedCapability hostedCapability) {
+        int idx = Collections.binarySearch(capabilities, hostedCapability, candidateComparator);
+        if (idx < 0) {
+            idx = Math.abs(idx + 1);
+        }
+        capabilities.add(idx, hostedCapability);
+        return idx;
+    }
+
+    @Override
+    public boolean isEffective(Requirement requirement) {
+        String resolution = requirement.getDirectives().get(Constants.RESOLUTION_DIRECTIVE);
+        return requirement.getNamespace().equals(IdentityNamespace.IDENTITY_NAMESPACE)
+                || !Constants.RESOLUTION_OPTIONAL.equals(resolution);
+    }
+
+    @Override
+    public Map<Resource, Wiring> getWirings() {
+        return Collections.emptyMap();
+    }
+
+    class Visitor extends AbstractRegionDigraphVisitor<Capability> {
+
+        Visitor(Collection<Capability> candidates) {
+            super(candidates);
+        }
+
+        @Override
+        protected boolean contains(Region region, Capability candidate) {
+            return region.equals(getRegion(candidate.getResource()));
+        }
+
+        @Override
+        protected boolean isAllowed(Capability candidate, RegionFilter filter) {
+            if (filter.isAllowed(candidate.getNamespace(), candidate.getAttributes())) {
+                return true;
+            }
+            Resource resource = candidate.getResource();
+            List<Capability> identities = resource.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE);
+            if (identities != null && !identities.isEmpty()) {
+                Capability identity = identities.iterator().next();
+                Map<String, Object> attrs = new HashMap<String, Object>();
+                attrs.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, identity.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE));
+                attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, identity.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE));
+                return filter.isAllowed(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, attrs);
+            }
+            return false;
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
new file mode 100644
index 0000000..8a98328
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
@@ -0,0 +1,317 @@
+/*
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.felix.resolver.ResolverImpl;
+import org.apache.felix.resolver.Util;
+import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.Repository;
+import org.apache.karaf.features.internal.download.DownloadManager;
+import org.apache.karaf.features.internal.download.StreamProvider;
+import org.apache.karaf.features.internal.download.simple.SimpleDownloader;
+import org.apache.karaf.features.internal.resolver.CapabilitySet;
+import org.apache.karaf.features.internal.resolver.SimpleFilter;
+import org.apache.karaf.features.internal.resolver.Slf4jResolverLog;
+import org.eclipse.equinox.internal.region.StandardRegionDigraph;
+import org.eclipse.equinox.region.Region;
+import org.eclipse.equinox.region.RegionDigraph;
+import org.eclipse.equinox.region.RegionFilterBuilder;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.InvalidSyntaxException;
+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.Resolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.karaf.features.internal.resolver.ResourceUtils.TYPE_FEATURE;
+import static org.apache.karaf.features.internal.resolver.ResourceUtils.TYPE_SUBSYSTEM;
+import static org.apache.karaf.features.internal.util.MapUtils.invert;
+import static org.osgi.framework.namespace.IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE;
+import static org.osgi.framework.namespace.IdentityNamespace.IDENTITY_NAMESPACE;
+import static org.osgi.framework.namespace.IdentityNamespace.TYPE_BUNDLE;
+import static org.osgi.framework.namespace.IdentityNamespace.TYPE_FRAGMENT;
+
+public class SubsystemResolver {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(SubsystemResolver.class);
+
+    private DownloadManager manager;
+    private RegionDigraph digraph;
+    private Subsystem root;
+    private Map<Resource, List<Wire>> wiring;
+
+    public SubsystemResolver() {
+        this(new SimpleDownloader());
+    }
+
+    public SubsystemResolver(DownloadManager manager) {
+        this.manager = manager;
+    }
+
+    public Map<Resource, List<Wire>> resolve(
+            List<Repository> repositories,
+            Map<String, Set<String>> features,
+            Collection<? extends Resource> system,
+            String featureResolutionRange
+    ) throws Exception {
+        // Build subsystems on the fly
+        for (Map.Entry<String, Set<String>> entry : features.entrySet()) {
+            String[] parts = entry.getKey().split("/");
+            if (root == null) {
+                root = new Subsystem(parts[0]);
+            } else if (!root.getName().equals(parts[0])) {
+                throw new IllegalArgumentException("Can not use multiple roots: " + root.getName() + ", " + parts[0]);
+            }
+            Subsystem ss = root;
+            for (int i = 1; i < parts.length; i++) {
+                ss = getOrCreateChild(ss, parts[i]);
+            }
+            for (String feature : entry.getValue()) {
+                String name, range;
+                int idx = feature.indexOf('/');
+                if (idx >= 0) {
+                    name = feature.substring(0, idx);
+                    range = feature.substring(idx + 1);
+                } else {
+                    name = feature;
+                    range = null;
+                }
+                ss.requireFeature(name, range);
+            }
+        }
+        if (root == null) {
+            return Collections.emptyMap();
+        }
+        // Pre-resolve
+        List<Feature> allFeatures = new ArrayList<Feature>();
+        for (Repository repo : repositories) {
+            allFeatures.addAll(Arrays.asList(repo.getFeatures()));
+        }
+        root.preResolve(allFeatures, manager, featureResolutionRange);
+
+        // Add system resources
+        for (Resource res : system) {
+            root.addSystemResource(res);
+        }
+
+        // Populate digraph and resolve
+        digraph = new StandardRegionDigraph(null, null);
+        populateDigraph(digraph, root);
+
+        Resolver resolver = new ResolverImpl(new Slf4jResolverLog(LOGGER));
+        wiring = resolver.resolve(new SubsystemResolveContext(root, digraph));
+
+        // Fragments are always wired to their host only, so create fake wiring to
+        // the subsystem the host is wired to
+        associateFragments();
+
+        return wiring;
+    }
+
+    public Map<String, StreamProvider> getProviders() {
+        return manager.getProviders();
+    }
+
+    public RegionDigraph getDigraph() {
+        return digraph;
+    }
+
+    public Map<Resource, List<Wire>> getWiring() {
+        return wiring;
+    }
+
+    public Map<String, String> getFlatSubsystemsMap() {
+        Map<String, String> toFlatten = new HashMap<String, String>();
+        findSubsystemsToFlatten(root, toFlatten);
+        return toFlatten;
+    }
+
+    public Map<String, Set<Resource>> getBundlesPerRegions() {
+        return invert(getBundles());
+    }
+
+    public Map<Resource, String> getBundles() {
+        String filter = String.format("(&(%s=*)(|(%s=%s)(%s=%s)))",
+                            IDENTITY_NAMESPACE,
+                            CAPABILITY_TYPE_ATTRIBUTE, TYPE_BUNDLE,
+                            CAPABILITY_TYPE_ATTRIBUTE, TYPE_FRAGMENT);
+        SimpleFilter sf = SimpleFilter.parse(filter);
+        return getResourceMapping(sf);
+    }
+
+    public Map<String, Set<Resource>> getFeaturesPerRegions() {
+        return invert(getFeatures());
+    }
+
+    public Map<Resource, String> getFeatures() {
+        SimpleFilter sf = createFilter(IDENTITY_NAMESPACE, "*",
+                                       CAPABILITY_TYPE_ATTRIBUTE, TYPE_FEATURE);
+        return getResourceMapping(sf);
+    }
+
+    public Map<String, Set<Resource>> getResourcesPerRegion(SimpleFilter resourceFilter) {
+        return invert(getResourceMapping(resourceFilter));
+    }
+
+    public Map<Resource, String> getResourceMapping(SimpleFilter resourceFilter) {
+        Map<String, String> flats = getFlatSubsystemsMap();
+        Map<Resource, List<Wire>> wiring = getWiring();
+        Map<Resource, String> resources = new HashMap<Resource, String>();
+        SimpleFilter sf = createFilter(IDENTITY_NAMESPACE, "*",
+                                       CAPABILITY_TYPE_ATTRIBUTE, TYPE_SUBSYSTEM);
+        for (Resource resource : wiring.keySet()) {
+            if (findMatchingCapability(resourceFilter, resource.getCapabilities(null)) != null) {
+                // Find the subsystem where this feature is installed
+                Wire wire = findMatchingWire(sf, wiring.get(resource));
+                if (wire != null) {
+                    String region = (String) wire.getCapability().getAttributes().get(IDENTITY_NAMESPACE);
+                    region = flats.get(region);
+                    resources.put(resource, region);
+                }
+            }
+        }
+        return resources;
+    }
+
+    private void associateFragments() {
+        SimpleFilter sf = createFilter(IDENTITY_NAMESPACE, "*", CAPABILITY_TYPE_ATTRIBUTE, TYPE_SUBSYSTEM);
+        for (Map.Entry<Resource, List<Wire>> entry : wiring.entrySet()) {
+            final Resource resource = entry.getKey();
+            final Requirement requirement = getSubsystemRequirement(resource);
+            if (Util.isFragment(resource)) {
+                List<Wire> wires = entry.getValue();
+                final Resource host = wires.get(0).getProvider();
+                final Wire wire = findMatchingWire(sf, wiring.get(host));
+                if (wire != null) {
+                    wires.add(new Wire() {
+                        @Override
+                        public Capability getCapability() {
+                            return wire.getCapability();
+                        }
+
+                        @Override
+                        public Requirement getRequirement() {
+                            return requirement;
+                        }
+
+                        @Override
+                        public Resource getProvider() {
+                            return wire.getProvider();
+                        }
+
+                        @Override
+                        public Resource getRequirer() {
+                            return resource;
+                        }
+                    });
+                }
+            }
+        }
+    }
+
+    private Requirement getSubsystemRequirement(Resource resource) {
+        for (Requirement requirement : resource.getRequirements(null)) {
+            if (IDENTITY_NAMESPACE.equals(requirement.getNamespace())
+                    && TYPE_SUBSYSTEM.equals(requirement.getAttributes().get(CAPABILITY_TYPE_ATTRIBUTE))) {
+                return requirement;
+            }
+        }
+        return null;
+    }
+
+    private Capability findMatchingCapability(SimpleFilter filter, Collection<Capability> caps) {
+        for (Capability cap : caps) {
+            if (CapabilitySet.matches(cap, filter)) {
+                return cap;
+            }
+        }
+        return null;
+    }
+
+    private Wire findMatchingWire(SimpleFilter filter, Collection<Wire> wires) {
+        for (Wire wire : wires) {
+            Capability cap = wire.getCapability();
+            if (CapabilitySet.matches(cap, filter)) {
+                return wire;
+            }
+        }
+        return null;
+    }
+
+    private SimpleFilter createFilter(String... s) {
+        Map<String, Object> attrs = new HashMap<String, Object>();
+        for (int i = 0; i < s.length - 1; i += 2) {
+            attrs.put(s[i], s[i+1]);
+        }
+        return SimpleFilter.convert(attrs);
+
+    }
+
+    private void findSubsystemsToFlatten(Subsystem subsystem, Map<String, String> toFlatten) {
+        Subsystem nonFlat = subsystem;
+        while (isFlat(nonFlat)) {
+            nonFlat = nonFlat.getParent();
+        }
+        toFlatten.put(subsystem.getName(), nonFlat.getName());
+        for (Subsystem child : subsystem.getChildren()) {
+            findSubsystemsToFlatten(child, toFlatten);
+        }
+    }
+
+    private boolean isFlat(Subsystem subsystem) {
+        return subsystem.getFeature() != null && subsystem.getFeature().getScoping() == null;
+    }
+
+    private Subsystem getOrCreateChild(Subsystem ss, String name) {
+        Subsystem child = ss.getChild(name);
+        return child != null ? child : ss.createSubsystem(name, true);
+    }
+
+    private void populateDigraph(RegionDigraph digraph, Subsystem subsystem) throws BundleException, InvalidSyntaxException {
+        Region region = digraph.createRegion(subsystem.getName());
+        if (subsystem.getParent() != null) {
+            Region parent = digraph.getRegion(subsystem.getParent().getName());
+            digraph.connect(region, createRegionFilterBuilder(digraph, subsystem.getImportPolicy()).build(), parent);
+            digraph.connect(parent, createRegionFilterBuilder(digraph, subsystem.getExportPolicy()).build(), region);
+        }
+        for (Subsystem child : subsystem.getChildren()) {
+            populateDigraph(digraph, child);
+        }
+    }
+
+    private RegionFilterBuilder createRegionFilterBuilder(RegionDigraph digraph, Map<String, Set<String>> sharingPolicy) throws InvalidSyntaxException {
+        RegionFilterBuilder result = digraph.createRegionFilterBuilder();
+        for (Map.Entry<String, Set<String>> entry : sharingPolicy.entrySet())
+            for (String filter : entry.getValue())
+                result.allow(entry.getKey(), filter);
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CandidateComparator.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CandidateComparator.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CandidateComparator.java
deleted file mode 100644
index ad4cc85..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CandidateComparator.java
+++ /dev/null
@@ -1,129 +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.resolver;
-
-import java.util.Comparator;
-
-import org.osgi.framework.Version;
-import org.osgi.framework.namespace.BundleNamespace;
-import org.osgi.framework.namespace.PackageNamespace;
-import org.osgi.framework.wiring.BundleCapability;
-import org.osgi.resource.Capability;
-
-public class CandidateComparator implements Comparator<Capability>
-{
-    public int compare(Capability cap1, Capability cap2)
-    {
-        int c = 0;
-        // Always prefer system bundle
-        if (cap1 instanceof BundleCapability && !(cap2 instanceof BundleCapability)) {
-            c = -1;
-        } else if (!(cap1 instanceof BundleCapability) && cap2 instanceof BundleCapability) {
-            c = 1;
-        }
-        // Compare revision capabilities.
-        if ((c == 0) && cap1.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE))
-        {
-            c = ((Comparable) cap1.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE))
-                    .compareTo(cap2.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE));
-            if (c == 0)
-            {
-                Version v1 = (!cap1.getAttributes().containsKey(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE))
-                        ? Version.emptyVersion
-                        : (Version) cap1.getAttributes().get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
-                Version v2 = (!cap2.getAttributes().containsKey(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE))
-                        ? Version.emptyVersion
-                        : (Version) cap2.getAttributes().get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
-                // Compare these in reverse order, since we want
-                // highest version to have priority.
-                c = compareVersions(v2, v1);
-            }
-        }
-        // Compare package capabilities.
-        else if ((c == 0) && cap1.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
-        {
-            c = ((Comparable) cap1.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))
-                    .compareTo(cap2.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE));
-            if (c == 0)
-            {
-                Version v1 = (!cap1.getAttributes().containsKey(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE))
-                        ? Version.emptyVersion
-                        : (Version) cap1.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
-                Version v2 = (!cap2.getAttributes().containsKey(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE))
-                        ? Version.emptyVersion
-                        : (Version) cap2.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
-                // Compare these in reverse order, since we want
-                // highest version to have priority.
-                c = compareVersions(v2, v1);
-                // if same version, rather compare on the bundle version
-                if (c == 0)
-                {
-                    v1 = (!cap1.getAttributes().containsKey(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE))
-                            ? Version.emptyVersion
-                            : (Version) cap1.getAttributes().get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
-                    v2 = (!cap2.getAttributes().containsKey(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE))
-                            ? Version.emptyVersion
-                            : (Version) cap2.getAttributes().get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
-                    // Compare these in reverse order, since we want
-                    // highest version to have priority.
-                    c = compareVersions(v2, v1);
-                }
-            }
-        }
-        // Compare feature capabilities
-        else if ((c == 0) && cap1.getNamespace().equals(FeatureNamespace.FEATURE_NAMESPACE))
-        {
-            c = ((Comparable) cap1.getAttributes().get(FeatureNamespace.FEATURE_NAMESPACE))
-                    .compareTo(cap2.getAttributes().get(FeatureNamespace.FEATURE_NAMESPACE));
-            if (c == 0)
-            {
-                Version v1 = (!cap1.getAttributes().containsKey(FeatureNamespace.CAPABILITY_VERSION_ATTRIBUTE))
-                        ? Version.emptyVersion
-                        : (Version) cap1.getAttributes().get(FeatureNamespace.CAPABILITY_VERSION_ATTRIBUTE);
-                Version v2 = (!cap2.getAttributes().containsKey(FeatureNamespace.CAPABILITY_VERSION_ATTRIBUTE))
-                        ? Version.emptyVersion
-                        : (Version) cap2.getAttributes().get(FeatureNamespace.CAPABILITY_VERSION_ATTRIBUTE);
-                // Compare these in reverse order, since we want
-                // highest version to have priority.
-                c = compareVersions(v2, v1);
-            }
-        }
-        return c;
-    }
-
-    private int compareVersions(Version v1, Version v2) {
-        int c = v1.getMajor() - v2.getMajor();
-        if (c != 0) {
-            return c;
-        }
-        c = v1.getMinor() - v2.getMinor();
-        if (c != 0) {
-            return c;
-        }
-        c = v1.getMicro() - v2.getMicro();
-        if (c != 0) {
-            return c;
-        }
-        String q1 = cleanQualifierForComparison(v1.getQualifier());
-        String q2 = cleanQualifierForComparison(v2.getQualifier());
-        return q1.compareTo(q2);
-    }
-
-    private String cleanQualifierForComparison(String qualifier) {
-        return qualifier.replaceAll("(redhat-[0-9]{3})([0-9]{3})", "$1-$2");
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureNamespace.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureNamespace.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureNamespace.java
deleted file mode 100644
index e211618..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureNamespace.java
+++ /dev/null
@@ -1,72 +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.resolver;
-
-import java.util.List;
-
-import org.osgi.framework.Version;
-import org.osgi.resource.Capability;
-import org.osgi.resource.Namespace;
-import org.osgi.resource.Resource;
-
-/**
- */
-public final class FeatureNamespace extends Namespace {
-
-    public static final String FEATURE_NAMESPACE = "karaf.feature";
-
-    public static final String	CAPABILITY_VERSION_ATTRIBUTE	= "version";
-
-    /**
-     * The attribute value identifying the resource
-     * {@link org.osgi.framework.namespace.IdentityNamespace#CAPABILITY_TYPE_ATTRIBUTE type} as an OSGi bundle.
-     *
-     * @see org.osgi.framework.namespace.IdentityNamespace#CAPABILITY_TYPE_ATTRIBUTE
-     */
-    public static final String	TYPE_FEATURE = "karaf.feature";
-
-    public static String getName(Resource resource)
-    {
-        List<Capability> caps = resource.getCapabilities(null);
-        for (Capability cap : caps)
-        {
-            if (cap.getNamespace().equals(FEATURE_NAMESPACE))
-            {
-                return cap.getAttributes().get(FEATURE_NAMESPACE).toString();
-            }
-        }
-        return null;
-    }
-
-    public static Version getVersion(Resource resource)
-    {
-        List<Capability> caps = resource.getCapabilities(null);
-        for (Capability cap : caps)
-        {
-            if (cap.getNamespace().equals(FEATURE_NAMESPACE))
-            {
-                return (Version)
-                        cap.getAttributes().get(CAPABILITY_VERSION_ATTRIBUTE);
-            }
-        }
-        return null;
-    }
-
-
-    private FeatureNamespace() {
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java
index e3b0101..99e1253 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java
@@ -16,11 +16,9 @@
  */
 package org.apache.karaf.features.internal.resolver;
 
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-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;
@@ -28,22 +26,21 @@ import org.apache.karaf.features.Dependency;
 import org.apache.karaf.features.Feature;
 import org.apache.karaf.features.internal.util.Macro;
 import org.osgi.framework.BundleException;
-import org.osgi.framework.Constants;
-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 static org.apache.karaf.features.internal.resolver.ResourceUtils.TYPE_FEATURE;
+import static org.apache.karaf.features.internal.resolver.ResourceUtils.addIdentityRequirement;
+
 /**
 */
 public class FeatureResource extends ResourceImpl {
 
     private final Feature feature;
 
-    public static Resource build(Feature feature, Conditional conditional, String featureRange, Map<String, Resource> locToRes) throws BundleException {
+    public static FeatureResource build(Feature feature, Conditional conditional, String featureRange, Map<String, Resource> locToRes) throws BundleException {
         Feature fcond = conditional.asFeature(feature.getName(), feature.getVersion());
-        FeatureResource resource = (FeatureResource) build(fcond, featureRange, locToRes);
+        FeatureResource resource = build(fcond, featureRange, locToRes);
         for (String cond : conditional.getCondition()) {
             if (cond.startsWith("req:")) {
                 cond = cond.substring("req:".length());
@@ -66,29 +63,15 @@ public class FeatureResource extends ResourceImpl {
         return resource;
     }
 
-    public static Resource build(Feature feature, String featureRange, Map<String, Resource> locToRes) throws BundleException {
+    public static FeatureResource build(Feature feature, String featureRange, Map<String, Resource> locToRes) throws BundleException {
         FeatureResource resource = new FeatureResource(feature);
-        Map<String, String> dirs = new HashMap<String, String>();
-        Map<String, Object> attrs = new HashMap<String, Object>();
-        attrs.put(FeatureNamespace.FEATURE_NAMESPACE, feature.getName());
-        attrs.put(FeatureNamespace.CAPABILITY_VERSION_ATTRIBUTE, VersionTable.getVersion(feature.getVersion()));
-        resource.addCapability(new CapabilityImpl(resource, FeatureNamespace.FEATURE_NAMESPACE, dirs, attrs));
         for (BundleInfo info : feature.getBundles()) {
             if (!info.isDependency()) {
                 Resource res = locToRes.get(info.getLocation());
                 if (res == null) {
                     throw new IllegalStateException("Resource not found for url " + info.getLocation());
                 }
-                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");
-                }
-                dirs = new HashMap<String, String>();
-                attrs = new HashMap<String, Object>();
-                attrs.put(IdentityNamespace.IDENTITY_NAMESPACE, caps.get(0).getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE));
-                attrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, caps.get(0).getAttributes().get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE));
-                attrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, new VersionRange((Version) caps.get(0).getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE), true));
-                resource.addRequirement(new RequirementImpl(resource, IdentityNamespace.IDENTITY_NAMESPACE, dirs, attrs));
+                addIdentityRequirement(resource, res);
             }
         }
         for (Dependency dep : feature.getDependencies()) {
@@ -104,8 +87,6 @@ public class FeatureResource extends ResourceImpl {
     }
 
     protected static void addDependency(FeatureResource resource, Dependency dep, String featureRange) {
-        Map<String, String> dirs;
-        Map<String, Object> attrs;
         String name = dep.getName();
         String version = dep.getVersion();
         if (version.equals("0.0.0")) {
@@ -113,17 +94,11 @@ public class FeatureResource extends ResourceImpl {
         } else if (!version.startsWith("[") && !version.startsWith("(")) {
             version = Macro.transform(featureRange, version);
         }
-        dirs = new HashMap<String, String>();
-        attrs = new HashMap<String, Object>();
-        attrs.put(FeatureNamespace.FEATURE_NAMESPACE, name);
-        if (version != null) {
-            attrs.put(FeatureNamespace.CAPABILITY_VERSION_ATTRIBUTE, new VersionRange(version));
-        }
-        resource.addRequirement(new RequirementImpl(resource, FeatureNamespace.FEATURE_NAMESPACE, dirs, attrs));
+        addIdentityRequirement(resource, name, TYPE_FEATURE, version);
     }
 
     public FeatureResource(Feature feature) {
-        super(feature.getName(), FeatureNamespace.TYPE_FEATURE, VersionTable.getVersion(feature.getVersion()));
+        super(feature.getName(), TYPE_FEATURE, VersionTable.getVersion(feature.getVersion()));
         this.feature = feature;
     }
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/resolver/IdentityCapability.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/IdentityCapability.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/IdentityCapability.java
index cdc00d1..20e94fa 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/IdentityCapability.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/IdentityCapability.java
@@ -24,7 +24,7 @@ import org.osgi.framework.namespace.IdentityNamespace;
 import org.osgi.resource.Capability;
 import org.osgi.resource.Resource;
 
-class IdentityCapability extends BaseClause implements Capability
+public class IdentityCapability extends BaseClause implements Capability
 {
     private final Resource m_resource;
     private final Map<String, String> m_dirs;

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResolveContextImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResolveContextImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResolveContextImpl.java
deleted file mode 100644
index e2ff793..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResolveContextImpl.java
+++ /dev/null
@@ -1,102 +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.resolver;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.osgi.framework.Constants;
-import org.osgi.resource.Capability;
-import org.osgi.resource.Requirement;
-import org.osgi.resource.Resource;
-import org.osgi.resource.Wiring;
-import org.osgi.service.repository.Repository;
-import org.osgi.service.resolver.HostedCapability;
-import org.osgi.service.resolver.ResolveContext;
-
-/**
-*/
-public class ResolveContextImpl extends ResolveContext {
-
-    private final Set<Resource> mandatory;
-    private final Set<Resource> optional;
-    private final Repository repository;
-    private final Map<Resource, Wiring> wirings;
-    private final boolean resolveOptional;
-
-    private final CandidateComparator candidateComparator = new CandidateComparator();
-
-    public ResolveContextImpl(Set<Resource> mandatory,
-                              Set<Resource> optional,
-                              Repository repository,
-                              boolean resolveOptional) {
-        this.mandatory = mandatory;
-        this.optional = optional;
-        this.repository = repository;
-        this.wirings = new HashMap<Resource, Wiring>();
-        this.resolveOptional = resolveOptional;
-    }
-
-    @Override
-    public Collection<Resource> getMandatoryResources() {
-        return mandatory;
-    }
-
-    @Override
-    public Collection<Resource> getOptionalResources() {
-        return optional;
-    }
-
-    @Override
-    public List<Capability> findProviders(Requirement requirement) {
-        List<Capability> caps = new ArrayList<Capability>();
-        Map<Requirement, Collection<Capability>> resMap =
-                repository.findProviders(Collections.singleton(requirement));
-        Collection<Capability> res = resMap != null ? resMap.get(requirement) : null;
-        if (res != null) {
-            caps.addAll(res);
-        }
-        Collections.sort(caps, candidateComparator);
-        return caps;
-    }
-    @Override
-    public int insertHostedCapability(List capabilities, HostedCapability hostedCapability) {
-        for (int i=0; i < capabilities.size(); i++) {
-            Capability cap = (Capability) capabilities.get(i);
-            if (candidateComparator.compare(hostedCapability, cap) <= 0) {
-                capabilities.add(i, hostedCapability);
-                return i;
-            }
-        }
-        capabilities.add(hostedCapability);
-        return capabilities.size() - 1;
-    }
-    @Override
-    public boolean isEffective(Requirement requirement) {
-        return resolveOptional ||
-                !Constants.RESOLUTION_OPTIONAL.equals(requirement.getDirectives().get(Constants.RESOLUTION_DIRECTIVE));
-    }
-    @Override
-    public Map<Resource, Wiring> getWirings() {
-        return wirings;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java
index cb2c36a..941ed8c 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java
@@ -35,12 +35,13 @@ import org.osgi.framework.wiring.BundleRevision;
 import org.osgi.resource.Capability;
 import org.osgi.resource.Requirement;
 import org.osgi.resource.Resource;
+import org.osgi.service.repository.ContentNamespace;
 
 public class ResourceBuilder {
 
     public static final String RESOLUTION_DYNAMIC = "dynamic";
 
-    public static Resource build(String uri, Map<String, String> headerMap)
+    public static ResourceImpl build(String uri, Map<String, String> headerMap)
             throws BundleException {
 
         // Verify that only manifest version 2 is specified.
@@ -74,8 +75,8 @@ public class ResourceBuilder {
         ResourceImpl resource = new ResourceImpl(bundleSymbolicName, type, bundleVersion);
         if (uri != null) {
             Map<String, Object> attrs = new HashMap<String, Object>();
-            attrs.put(UriNamespace.URI_NAMESPACE, uri);
-            resource.addCapability(new CapabilityImpl(resource, UriNamespace.URI_NAMESPACE, Collections.<String, String>emptyMap(), attrs));
+            attrs.put(ContentNamespace.CAPABILITY_URL_ATTRIBUTE, uri);
+            resource.addCapability(new CapabilityImpl(resource, ContentNamespace.CONTENT_NAMESPACE, Collections.<String, String>emptyMap(), attrs));
         }
 
         // Add a bundle and host capability to all

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java
new file mode 100644
index 0000000..03d4fd9
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java
@@ -0,0 +1,103 @@
+/*
+ * 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.resolver;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.utils.version.VersionRange;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+import static org.osgi.framework.namespace.IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE;
+import static org.osgi.framework.namespace.IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE;
+import static org.osgi.framework.namespace.IdentityNamespace.IDENTITY_NAMESPACE;
+import static org.osgi.service.repository.ContentNamespace.CAPABILITY_URL_ATTRIBUTE;
+import static org.osgi.service.repository.ContentNamespace.CONTENT_NAMESPACE;
+
+public class ResourceUtils {
+
+    public static final String TYPE_SUBSYSTEM = "karaf.subsystem";
+
+    public static final String TYPE_FEATURE = "karaf.feature";
+
+    public static String getUri(Resource resource) {
+        List<Capability> caps = resource.getCapabilities(null);
+        for (Capability cap : caps) {
+            if (cap.getNamespace().equals(CONTENT_NAMESPACE)) {
+                return cap.getAttributes().get(CAPABILITY_URL_ATTRIBUTE).toString();
+            }
+        }
+        return null;
+    }
+
+    public static String getFeatureId(Resource resource) {
+        List<Capability> caps = resource.getCapabilities(null);
+        for (Capability cap : caps) {
+            if (cap.getNamespace().equals(IDENTITY_NAMESPACE)) {
+                Map<String, Object> attributes = cap.getAttributes();
+                if (TYPE_FEATURE.equals(attributes.get(CAPABILITY_TYPE_ATTRIBUTE))) {
+                    String name = (String) attributes.get(IDENTITY_NAMESPACE);
+                    Version version = (Version) attributes.get(CAPABILITY_VERSION_ATTRIBUTE);
+                    return version != null ? name + "/" + version : name;
+                }
+            }
+        }
+        return null;
+    }
+
+    public static void addIdentityRequirement(ResourceImpl resource, String name, String type, String range) {
+        Map<String, String> dirs = new HashMap<String, String>();
+        Map<String, Object> attrs = new HashMap<String, Object>();
+        if (name != null) {
+            attrs.put(IDENTITY_NAMESPACE, name);
+        }
+        if (type != null) {
+            attrs.put(CAPABILITY_TYPE_ATTRIBUTE, type);
+        }
+        if (range != null) {
+            attrs.put(CAPABILITY_VERSION_ATTRIBUTE, new VersionRange(range));
+        }
+        resource.addRequirement(new RequirementImpl(resource, IDENTITY_NAMESPACE, dirs, attrs));
+    }
+
+    public static void addIdentityRequirement(ResourceImpl resource, Resource required) {
+        addIdentityRequirement(resource, required, true);
+    }
+
+    public static void addIdentityRequirement(ResourceImpl resource, Resource required, boolean mandatory) {
+        for (Capability cap : required.getCapabilities(null)) {
+            if (cap.getNamespace().equals(IDENTITY_NAMESPACE)) {
+                Map<String, Object> attributes = cap.getAttributes();
+                Map<String, String> dirs = new HashMap<String, String>();
+                dirs.put(Constants.RESOLUTION_DIRECTIVE, mandatory ? Constants.RESOLUTION_MANDATORY : Constants.RESOLUTION_OPTIONAL);
+                Map<String, Object> attrs = new HashMap<String, Object>();
+                attrs.put(IDENTITY_NAMESPACE, attributes.get(IDENTITY_NAMESPACE));
+                attrs.put(CAPABILITY_TYPE_ATTRIBUTE, attributes.get(CAPABILITY_TYPE_ATTRIBUTE));
+                Version version = (Version) attributes.get(CAPABILITY_VERSION_ATTRIBUTE);
+                if (version != null) {
+                    attrs.put(CAPABILITY_VERSION_ATTRIBUTE, new VersionRange(version, true));
+                }
+                resource.addRequirement(new RequirementImpl(resource, IDENTITY_NAMESPACE, dirs, attrs));
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/resolver/UriNamespace.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/UriNamespace.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/UriNamespace.java
deleted file mode 100644
index b5158bf..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/UriNamespace.java
+++ /dev/null
@@ -1,47 +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.resolver;
-
-import java.util.List;
-
-import org.osgi.resource.Capability;
-import org.osgi.resource.Namespace;
-import org.osgi.resource.Resource;
-
-/**
- */
-public final class UriNamespace extends Namespace {
-
-    public static final String URI_NAMESPACE = "karaf.uri";
-
-    public static String getUri(Resource resource)
-    {
-        List<Capability> caps = resource.getCapabilities(null);
-        for (Capability cap : caps)
-        {
-            if (cap.getNamespace().equals(UriNamespace.URI_NAMESPACE))
-            {
-                return cap.getAttributes().get(UriNamespace.URI_NAMESPACE).toString();
-            }
-        }
-        return null;
-    }
-
-
-    private UriNamespace() {
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2705ad88/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java
index 0e9038d..258b44b 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java
@@ -45,6 +45,26 @@ public class FeatureConfigInstaller {
 		this.configAdmin = configAdmin;
 	}
 
+    public void installFeatureConfigs(Feature feature) throws IOException, InvalidSyntaxException {
+        for (String config : feature.getConfigurations().keySet()) {
+            Dictionary<String,String> props = new Hashtable<String, String>(feature.getConfigurations().get(config));
+            String[] pid = parsePid(config);
+            Configuration cfg = findExistingConfiguration(configAdmin, pid[0], pid[1]);
+            if (cfg == null) {
+                cfg = createConfiguration(configAdmin, pid[0], pid[1]);
+                String key = createConfigurationKey(pid[0], pid[1]);
+                props.put(CONFIG_KEY, key);
+                if (cfg.getBundleLocation() != null) {
+                    cfg.setBundleLocation(null);
+                }
+                cfg.update(props);
+            }
+        }
+        for (ConfigFileInfo configFile : feature.getConfigurationFiles()) {
+            installConfigurationFile(configFile.getLocation(), configFile.getFinalname(), configFile.isOverride());
+        }
+    }
+
     private String[] parsePid(String pid) {
         int n = pid.indexOf('-');
         if (n > 0) {
@@ -81,26 +101,6 @@ public class FeatureConfigInstaller {
         return null;
     }
 
-    void installFeatureConfigs(Feature feature) throws IOException, InvalidSyntaxException {
-        for (String config : feature.getConfigurations().keySet()) {
-            Dictionary<String,String> props = new Hashtable<String, String>(feature.getConfigurations().get(config));
-            String[] pid = parsePid(config);
-            Configuration cfg = findExistingConfiguration(configAdmin, pid[0], pid[1]);
-            if (cfg == null) {
-                cfg = createConfiguration(configAdmin, pid[0], pid[1]);
-                String key = createConfigurationKey(pid[0], pid[1]);
-                props.put(CONFIG_KEY, key);
-                if (cfg.getBundleLocation() != null) {
-                    cfg.setBundleLocation(null);
-                }
-                cfg.update(props);
-            }
-        }
-        for (ConfigFileInfo configFile : feature.getConfigurationFiles()) {
-            installConfigurationFile(configFile.getLocation(), configFile.getFinalname(), configFile.isOverride());
-        }
-    }
-
     private String createConfigurationKey(String pid, String factoryPid) {
         return factoryPid == null ? pid : pid + "-" + factoryPid;
     }