cziegeler closed pull request #6: SLING-7512 Order features based on their 
dependencies.
URL: https://github.com/apache/sling-whiteboard/pull/6
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/featuremodel/feature-analyser/pom.xml 
b/featuremodel/feature-analyser/pom.xml
index 75533a8..735290e 100644
--- a/featuremodel/feature-analyser/pom.xml
+++ b/featuremodel/feature-analyser/pom.xml
@@ -129,17 +129,17 @@
                    <version>0.1.0-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
-      <!-- Testing -->
-        <dependency>
-               <groupId>junit</groupId>
-               <artifactId>junit</artifactId>
-        </dependency>
-
         <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.configurator</artifactId>
             <version>0.0.1-SNAPSHOT</version>
-            <scope>test</scope>
+            <scope>provided</scope>
+        </dependency>
+        
+        <!-- Testing -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
         </dependency>
     </dependencies>
 </project>
diff --git 
a/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/Descriptor.java
 
b/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/Descriptor.java
index 145344d..d6237f8 100644
--- 
a/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/Descriptor.java
+++ 
b/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/Descriptor.java
@@ -20,9 +20,9 @@
 import java.util.HashSet;
 import java.util.Set;
 
-import org.apache.sling.feature.Capability;
-import org.apache.sling.feature.Requirement;
 import org.apache.sling.feature.support.util.PackageInfo;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 /**
  * A descriptor holds information about requirements and capabilities
diff --git 
a/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/BundleDescriptorImpl.java
 
b/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/BundleDescriptorImpl.java
index 1ed0ce2..abe2a97 100644
--- 
a/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/BundleDescriptorImpl.java
+++ 
b/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/BundleDescriptorImpl.java
@@ -22,13 +22,13 @@
 import java.util.jar.Manifest;
 
 import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.Capability;
-import org.apache.sling.feature.Requirement;
 import org.apache.sling.feature.analyser.BundleDescriptor;
 import org.apache.sling.feature.support.util.ManifestParser;
 import org.apache.sling.feature.support.util.ManifestUtil;
 import org.apache.sling.feature.support.util.PackageInfo;
 import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 /**
  * Information about a bundle
diff --git 
a/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckRequirementsCapabilities.java
 
b/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckRequirementsCapabilities.java
index 0ea2799..6029a13 100644
--- 
a/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckRequirementsCapabilities.java
+++ 
b/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckRequirementsCapabilities.java
@@ -23,12 +23,12 @@
 import java.util.TreeMap;
 import java.util.stream.Collectors;
 
-import org.apache.sling.feature.Requirement;
 import org.apache.sling.feature.analyser.ArtifactDescriptor;
 import org.apache.sling.feature.analyser.BundleDescriptor;
 import org.apache.sling.feature.analyser.task.AnalyserTask;
 import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
 import org.apache.sling.feature.support.util.CapabilityMatcher;
+import org.osgi.resource.Requirement;
 
 public class CheckRequirementsCapabilities implements AnalyserTask {
     private final String format = "Artifact %s:%s requires %s in start level 
%d but %s";
diff --git 
a/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/scanner/impl/FelixFrameworkScanner.java
 
b/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/scanner/impl/FelixFrameworkScanner.java
index eb2124a..ac8afa1 100644
--- 
a/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/scanner/impl/FelixFrameworkScanner.java
+++ 
b/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/scanner/impl/FelixFrameworkScanner.java
@@ -39,13 +39,13 @@
 import org.apache.sling.commons.osgi.ManifestHeader;
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Capability;
 import org.apache.sling.feature.KeyValueMap;
 import org.apache.sling.feature.analyser.BundleDescriptor;
 import org.apache.sling.feature.scanner.FrameworkScanner;
 import org.apache.sling.feature.support.util.PackageInfo;
 import org.apache.sling.feature.support.util.SubstVarUtil;
 import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
 
 public class FelixFrameworkScanner implements FrameworkScanner {
 
diff --git 
a/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/scanner/impl/RepoInitScanner.java
 
b/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/scanner/impl/RepoInitScanner.java
index 7b4e221..d3cf11c 100644
--- 
a/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/scanner/impl/RepoInitScanner.java
+++ 
b/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/scanner/impl/RepoInitScanner.java
@@ -17,13 +17,15 @@
 package org.apache.sling.feature.scanner.impl;
 
 import java.io.IOException;
+import java.util.Collections;
 
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.ExtensionType;
-import org.apache.sling.feature.Requirement;
+import org.apache.sling.feature.OSGiRequirement;
 import org.apache.sling.feature.analyser.ContainerDescriptor;
 import org.apache.sling.feature.scanner.ExtensionScanner;
 import org.apache.sling.feature.support.ArtifactManager;
+import org.osgi.resource.Requirement;
 
 public class RepoInitScanner implements ExtensionScanner {
 
@@ -50,9 +52,8 @@ public ContainerDescriptor scan(final Extension extension,
 
         final ContainerDescriptor cd = new ContainerDescriptor() {};
 
-        final Requirement req = new Requirement("osgi.implementation");
-        req.getDirectives().put("filter",
-                
"(&(osgi.implementation=org.apache.sling.jcr.repoinit)(version>=1.0)(!(version>=2.0)))");
+        final Requirement req = new OSGiRequirement("osgi.implementation", 
Collections.emptyMap(),
+            Collections.singletonMap("filter", 
"(&(osgi.implementation=org.apache.sling.jcr.repoinit)(version>=1.0)(!(version>=2.0)))"));
         cd.getRequirements().add(req);
 
         cd.lock();
diff --git 
a/featuremodel/feature-analyser/src/test/java/org/apache/sling/feature/analyser/AnalyserTest.java
 
b/featuremodel/feature-analyser/src/test/java/org/apache/sling/feature/analyser/AnalyserTest.java
index 872ec3c..d92398a 100644
--- 
a/featuremodel/feature-analyser/src/test/java/org/apache/sling/feature/analyser/AnalyserTest.java
+++ 
b/featuremodel/feature-analyser/src/test/java/org/apache/sling/feature/analyser/AnalyserTest.java
@@ -20,6 +20,7 @@
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.analyser.service.Analyser;
 import org.apache.sling.feature.analyser.service.Scanner;
+import org.apache.sling.feature.process.FeatureResolver;
 import org.apache.sling.feature.support.ArtifactManager;
 import org.apache.sling.feature.support.ArtifactManagerConfig;
 import org.apache.sling.feature.support.FeatureUtil;
@@ -28,6 +29,7 @@
 
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.util.List;
 
 import static junit.framework.TestCase.fail;
 
@@ -40,7 +42,8 @@ public void testAnalyserWithCompleteFeature() throws 
Exception {
                 "UTF-8") ) {
             Feature feature = FeatureJSONReader.read(reader, "feature");
 
-            Application app = FeatureUtil.assembleApplication(null, 
ArtifactManager.getArtifactManager(new ArtifactManagerConfig()), feature);
+            Application app = FeatureUtil.assembleApplication(null, 
ArtifactManager.getArtifactManager(new ArtifactManagerConfig()),
+                    getTestResolver(), feature);
 
             analyser.analyse(app);
         }
@@ -54,7 +57,8 @@ public void testAnalyserWithInCompleteFeature() throws 
Exception {
                 "UTF-8") ) {
             Feature feature = FeatureJSONReader.read(reader, "feature");
 
-            Application app = FeatureUtil.assembleApplication(null, 
ArtifactManager.getArtifactManager(new ArtifactManagerConfig()), feature);
+            Application app = FeatureUtil.assembleApplication(null, 
ArtifactManager.getArtifactManager(new ArtifactManagerConfig()),
+                    getTestResolver(), feature);
 
             try {
                 analyser.analyse(app);
@@ -66,4 +70,17 @@ public void testAnalyserWithInCompleteFeature() throws 
Exception {
             }
         }
     }
+
+    private FeatureResolver getTestResolver() {
+        return new FeatureResolver() {
+            @Override
+            public void close() throws Exception {
+            }
+
+            @Override
+            public List<Feature> orderFeatures(List<Feature> features) {
+                return features;
+            }
+        };
+    }
 }
diff --git a/featuremodel/feature-applicationbuilder/pom.xml 
b/featuremodel/feature-applicationbuilder/pom.xml
index c30dc54..a61c49f 100644
--- a/featuremodel/feature-applicationbuilder/pom.xml
+++ b/featuremodel/feature-applicationbuilder/pom.xml
@@ -100,6 +100,12 @@
             <version>0.0.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.resolver</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature.support</artifactId>
@@ -118,6 +124,13 @@
             <version>0.1.0-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.configurator</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+                
       <!-- Testing -->
         <dependency>
                <groupId>junit</groupId>
diff --git 
a/featuremodel/feature-applicationbuilder/src/main/java/org/apache/sling/feature/applicationbuilder/impl/Main.java
 
b/featuremodel/feature-applicationbuilder/src/main/java/org/apache/sling/feature/applicationbuilder/impl/Main.java
index 9964a3e..db733cb 100644
--- 
a/featuremodel/feature-applicationbuilder/src/main/java/org/apache/sling/feature/applicationbuilder/impl/Main.java
+++ 
b/featuremodel/feature-applicationbuilder/src/main/java/org/apache/sling/feature/applicationbuilder/impl/Main.java
@@ -19,6 +19,7 @@
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.util.Collections;
 import java.util.stream.Stream;
 
 import org.apache.commons.cli.CommandLine;
@@ -29,6 +30,8 @@
 import org.apache.commons.cli.ParseException;
 import org.apache.sling.feature.Application;
 import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.process.FeatureResolver;
+import org.apache.sling.feature.resolver.FrameworkResolver;
 import org.apache.sling.feature.support.ArtifactManager;
 import org.apache.sling.feature.support.ArtifactManagerConfig;
 import org.apache.sling.feature.support.FeatureUtil;
@@ -118,6 +121,10 @@ private static ArtifactManager getArtifactManager() {
         return null;
     }
 
+    private static FeatureResolver getFeatureResolver(ArtifactManager am) {
+        return new FrameworkResolver(am, Collections.emptyMap());
+    }
+
     public static void main(final String[] args) {
         // setup logging
         System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "info");
@@ -132,11 +139,6 @@ public static void main(final String[] args) {
 
         parseArgs(args);
 
-        final ArtifactManagerConfig amConfig = new ArtifactManagerConfig();
-        if ( repoUrls != null ) {
-            amConfig.setRepositoryUrls(repoUrls.split(","));
-        }
-
         final ArtifactManager am = getArtifactManager();
 
 
@@ -160,12 +162,15 @@ public static void main(final String[] args) {
             System.exit(1);
         }
 
-        try {
-            
writeApplication(buildApplication(FeatureUtil.assembleApplication(null, am, 
files)), output == null ? "application.json" : output);
+        try (FeatureResolver fr = getFeatureResolver(am)) {
+            
writeApplication(buildApplication(FeatureUtil.assembleApplication(null, am, fr, 
files)), output == null ? "application.json" : output);
 
         } catch ( final IOException ioe) {
             LOGGER.error("Unable to read feature/application files " + 
ioe.getMessage(), ioe);
             System.exit(1);
+        } catch ( final Exception e) {
+            LOGGER.error("Problem generating application", e);
+            System.exit(1);
         }
     }
 
diff --git a/featuremodel/feature-launcher/pom.xml 
b/featuremodel/feature-launcher/pom.xml
index 6bf9943..8e1d828 100644
--- a/featuremodel/feature-launcher/pom.xml
+++ b/featuremodel/feature-launcher/pom.xml
@@ -93,6 +93,11 @@
             <artifactId>org.apache.sling.feature</artifactId>
             <version>0.0.1-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.resolver</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature.support</artifactId>
diff --git 
a/featuremodel/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
 
b/featuremodel/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
index 16c244b..d4e70e2 100644
--- 
a/featuremodel/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
+++ 
b/featuremodel/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
@@ -20,6 +20,7 @@
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -29,6 +30,8 @@
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.ExtensionType;
 import org.apache.sling.feature.launcher.impl.LauncherConfig.StartupMode;
+import org.apache.sling.feature.process.FeatureResolver;
+import org.apache.sling.feature.resolver.FrameworkResolver;
 import org.apache.sling.feature.support.ArtifactHandler;
 import org.apache.sling.feature.support.ArtifactManager;
 import org.apache.sling.feature.support.FeatureUtil;
@@ -46,7 +49,7 @@
     public static Application createApplication(final LauncherConfig config,
             final ArtifactManager artifactManager)
     throws IOException {
-        final Application app;
+        Application app = null;
         if ( config.getApplicationFile() != null ) {
             String absoluteArg = config.getApplicationFile();
             if ( absoluteArg.indexOf(":") < 2 ) {
@@ -59,7 +62,13 @@ public static Application createApplication(final 
LauncherConfig config,
             }
 
         } else {
-           app = FeatureUtil.assembleApplication(null, artifactManager, 
FeatureUtil.getFeatureFiles(config.getHomeDirectory(), 
config.getFeatureFiles()).toArray(new String[0]));
+            try (FeatureResolver resolver = new 
FrameworkResolver(artifactManager, Collections.emptyMap())) {
+                app = FeatureUtil.assembleApplication(null, artifactManager, 
resolver,
+                       FeatureUtil.getFeatureFiles(config.getHomeDirectory(), 
config.getFeatureFiles()).toArray(new String[0]));
+            } catch (Exception ex) {
+                Main.LOG().error("Error while assembling application: {}", 
ex.getMessage(), ex);
+                System.exit(1);
+            }
         }
 
         // write application back
diff --git a/featuremodel/feature-modelconverter/pom.xml 
b/featuremodel/feature-modelconverter/pom.xml
index 64dbec0..93d619b 100644
--- a/featuremodel/feature-modelconverter/pom.xml
+++ b/featuremodel/feature-modelconverter/pom.xml
@@ -100,6 +100,12 @@
             <version>0.0.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.resolver</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature.support</artifactId>
diff --git 
a/featuremodel/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/Main.java
 
b/featuremodel/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/Main.java
index 9f5aaf9..ccc3f85 100644
--- 
a/featuremodel/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/Main.java
+++ 
b/featuremodel/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/Main.java
@@ -43,6 +43,8 @@
 import org.apache.sling.feature.ExtensionType;
 import org.apache.sling.feature.Extensions;
 import org.apache.sling.feature.KeyValueMap;
+import org.apache.sling.feature.process.FeatureResolver;
+import org.apache.sling.feature.resolver.FrameworkResolver;
 import org.apache.sling.feature.support.ArtifactHandler;
 import org.apache.sling.feature.support.ArtifactManager;
 import org.apache.sling.feature.support.ArtifactManagerConfig;
@@ -165,6 +167,10 @@ private static ArtifactManager getArtifactManager() {
         return null;
     }
 
+    private static FeatureResolver getFeatureResolver(ArtifactManager am) {
+        return new FrameworkResolver(am, Collections.emptyMap());
+    }
+
     public static void main(final String[] args) {
         // setup logging
         System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "info");
@@ -241,7 +247,7 @@ public static void main(final String[] args) {
             if ( output == null ) {
                 output = createApp ? "application.txt" : "feature.txt";
             }
-            try {
+            try (FeatureResolver fr = getFeatureResolver(am)) {
                 if ( createApp ) {
                     // each file is an application
                     int index = 1;
@@ -253,7 +259,7 @@ public static void main(final String[] args) {
                         index++;
                     }
                 } else {
-                    final Application app = 
FeatureUtil.assembleApplication(null, am, files.stream()
+                    final Application app = 
FeatureUtil.assembleApplication(null, am, fr, files.stream()
                             .map(File::getAbsolutePath)
                             .toArray(String[]::new));
                     convert(app, 0);
@@ -261,6 +267,9 @@ public static void main(final String[] args) {
             } catch ( final IOException ioe) {
                 LOGGER.error("Unable to read feature/application files " + 
ioe.getMessage(), ioe);
                 System.exit(1);
+            } catch ( final Exception e) {
+                LOGGER.error("Problem generating application", e);
+                System.exit(1);
             }
         }
     }
diff --git a/featuremodel/feature-resolver/pom.xml 
b/featuremodel/feature-resolver/pom.xml
new file mode 100644
index 0000000..c753af4
--- /dev/null
+++ b/featuremodel/feature-resolver/pom.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+    <!--
+        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.
+    -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>32</version>
+        <relativePath />
+    </parent>
+
+    <artifactId>org.apache.sling.feature.resolver</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+    
+    <name>Apache Sling Feature Resolver</name>
+    <description>
+        Resolver integration of the Feature Model
+    </description>
+
+    <properties>
+        <sling.java.version>8</sling.java.version>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.analyser</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.support</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.resolver</artifactId>
+            <version>1.0.1</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- Testing -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>2.8.9</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework</artifactId>
+            <version>5.6.10</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git 
a/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/FrameworkResolver.java
 
b/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/FrameworkResolver.java
new file mode 100644
index 0000000..f62c3db
--- /dev/null
+++ 
b/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/FrameworkResolver.java
@@ -0,0 +1,224 @@
+/*
+ * 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.sling.feature.resolver;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.Set;
+
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.analyser.BundleDescriptor;
+import org.apache.sling.feature.analyser.impl.BundleDescriptorImpl;
+import org.apache.sling.feature.process.FeatureResolver;
+import org.apache.sling.feature.resolver.impl.BundleResourceImpl;
+import org.apache.sling.feature.resolver.impl.ResolveContextImpl;
+import org.apache.sling.feature.support.ArtifactManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.launch.FrameworkFactory;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.HostNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.framework.wiring.BundleRevision;
+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.Resolver;
+
+public class FrameworkResolver implements FeatureResolver {
+    private final ArtifactManager artifactManager;
+    private final Resolver resolver;
+    private final Resource frameworkResource;
+    private final Framework framework;
+
+    public FrameworkResolver(ArtifactManager am, Map<String, String> 
frameworkProperties) {
+        artifactManager = am;
+
+        Resolver r = null;
+        // Launch an OSGi framework and obtain its resolver
+        try {
+            FrameworkFactory fwf = 
ServiceLoader.load(FrameworkFactory.class).iterator().next();
+            framework = fwf.newFramework(frameworkProperties);
+            framework.init();
+            framework.start();
+            BundleContext ctx = framework.getBundleContext();
+
+            // Create a resource representing the framework
+            BundleRevision br = framework.adapt(BundleRevision.class);
+            List<Capability> caps = 
br.getCapabilities(PackageNamespace.PACKAGE_NAMESPACE);
+            frameworkResource = new BundleResourceImpl(
+                    
Collections.singletonMap(PackageNamespace.PACKAGE_NAMESPACE, caps), 
Collections.emptyMap());
+
+            int i=0;
+            while (i < 20) {
+                ServiceReference<Resolver> ref = 
ctx.getServiceReference(Resolver.class);
+                if (ref != null) {
+                    r = ctx.getService(ref);
+                    break;
+                }
+
+                // The service isn't there yet, let's wait a little and try 
again
+                Thread.sleep(500);
+                i++;
+            }
+        } catch (BundleException | InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+        resolver = r;
+    }
+
+    @Override
+    public void close() throws Exception {
+        framework.stop();
+    }
+
+    @Override
+    public List<Feature> orderFeatures(List<Feature> features) {
+        try {
+            return internalOrderFeatures(features);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public List<Feature> internalOrderFeatures(List<Feature> features) throws 
IOException {
+        Map<Resource, Feature> bundleMap = new HashMap<>();
+        for (Feature f : features) {
+            for (Artifact b : f.getBundles()) {
+                BundleDescriptor bd = getBundleDescriptor(artifactManager, b);
+                Resource r = new BundleResourceImpl(bd);
+                bundleMap.put(r, f);
+            }
+        }
+
+        Set<Resource> availableBundles = new HashSet<>(bundleMap.keySet());
+        // Add these to the available features
+        Artifact lpa = new 
Artifact(ArtifactId.parse("org.apache.sling/org.apache.sling.launchpad.api/1.2.0"));
+        availableBundles.add(new 
BundleResourceImpl(getBundleDescriptor(artifactManager, lpa)));
+        availableBundles.add(frameworkResource);
+
+        List<Resource> orderedBundles = new LinkedList<>();
+        try {
+            for (Resource bundle : bundleMap.keySet()) {
+                if (orderedBundles.contains(bundle)) {
+                    // Already handled
+                    continue;
+                }
+                Map<Resource, List<Wire>> deps = resolver.resolve(new 
ResolveContextImpl(bundle, availableBundles));
+
+                for (Map.Entry<Resource, List<Wire>> entry : deps.entrySet()) {
+                    Resource curBundle = entry.getKey();
+
+                    if (!bundleMap.containsKey(curBundle)) {
+                        // This is some synthesized bundle. Ignoring.
+                        continue;
+                    }
+
+                    if (!orderedBundles.contains(curBundle)) {
+                        orderedBundles.add(curBundle);
+                    }
+
+                    for (Wire w : entry.getValue()) {
+                        Resource provBundle = w.getProvider();
+                        int curBundleIdx = orderedBundles.indexOf(curBundle);
+                        int newBundleIdx = orderedBundles.indexOf(provBundle);
+                        if (newBundleIdx >= 0) {
+                            if (curBundleIdx < newBundleIdx) {
+                                // If the list already contains the providing 
but after the current bundle, remove it there to move it before the current 
bundle
+                                orderedBundles.remove(provBundle);
+                            } else {
+                                // If the providing bundle is already before 
the current bundle, then no need to change anything
+                                continue;
+                            }
+                        }
+                        orderedBundles.add(curBundleIdx, provBundle);
+                    }
+                }
+            }
+        } catch (ResolutionException e) {
+            throw new RuntimeException(e);
+        }
+
+        // Sort the fragments so that fragments are started before the host 
bundle
+        for (int i=0; i<orderedBundles.size(); i++) {
+            Resource r = orderedBundles.get(i);
+            List<Requirement> reqs = 
r.getRequirements(HostNamespace.HOST_NAMESPACE);
+            if (reqs.size() > 0) {
+                // This is a fragment
+                Requirement req = reqs.iterator().next(); // TODO handle more 
host requirements
+                String bsn = 
req.getAttributes().get(HostNamespace.HOST_NAMESPACE).toString(); // TODO this 
is not valid, should obtain from filter
+                int idx = getBundleIndex(orderedBundles, bsn); // TODO check 
for filter too
+                if (idx < i) {
+                    // the fragment is after the host, and should be moved to 
be before the host
+                    Resource frag = orderedBundles.remove(i);
+                    orderedBundles.add(idx, frag);
+                }
+            }
+        }
+
+        List<Feature> orderedFeatures = new ArrayList<>();
+        for (Resource r : orderedBundles) {
+            Feature f = bundleMap.get(r);
+            if (f != null) {
+                if (!orderedFeatures.contains(f)) {
+                    orderedFeatures.add(f);
+                }
+            }
+        }
+        return orderedFeatures;
+    }
+
+    private static int getBundleIndex(List<Resource> bundles, String 
bundleSymbolicName) {
+        for (int i=0; i<bundles.size(); i++) {
+            Resource b = bundles.get(i);
+            if (bundleSymbolicName.equals(getBundleSymbolicName(b))) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private static String getBundleSymbolicName(Resource b) {
+        for (Capability cap : 
b.getCapabilities(BundleNamespace.BUNDLE_NAMESPACE)) {
+            return 
cap.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE).toString();
+        }
+        return null;
+    }
+
+    private static BundleDescriptor getBundleDescriptor(ArtifactManager 
artifactManager, Artifact b) throws IOException {
+        final File file = 
artifactManager.getArtifactHandler(b.getId().toMvnUrl()).getFile();
+        if ( file == null ) {
+            throw new IOException("Unable to find file for " + b.getId());
+        }
+
+        return new BundleDescriptorImpl(b, file, -1);
+    }
+}
diff --git 
a/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/impl/BundleResourceImpl.java
 
b/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/impl/BundleResourceImpl.java
new file mode 100644
index 0000000..16cc812
--- /dev/null
+++ 
b/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/impl/BundleResourceImpl.java
@@ -0,0 +1,191 @@
+/*
+ * 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.sling.feature.resolver.impl;
+
+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.stream.Collectors;
+
+import org.apache.sling.feature.OSGiCapability;
+import org.apache.sling.feature.OSGiRequirement;
+import org.apache.sling.feature.analyser.BundleDescriptor;
+import org.apache.sling.feature.support.util.PackageInfo;
+import org.osgi.framework.Version;
+import org.osgi.framework.VersionRange;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+/**
+ * Implementation of the OSGi Resource interface.
+ */
+public class BundleResourceImpl implements Resource {
+    final String hint;
+    final Map<String, List<Capability>> capabilities;
+    final Map<String, List<Requirement>> requirements;
+
+    /**
+     * Create a resource based on a BundleDescriptor.
+     * @param bd The BundleDescriptor to represent.
+     */
+    public BundleResourceImpl(BundleDescriptor bd) {
+        hint = bd.getBundleSymbolicName() + " " + bd.getBundleVersion();
+        Map<String, List<Capability>> caps = new HashMap<>();
+        for (Capability c : bd.getCapabilities()) {
+            List<Capability> l = caps.get(c.getNamespace());
+            if (l == null) {
+                l = new ArrayList<>();
+                caps.put(c.getNamespace(), l);
+            }
+            l.add(new OSGiCapability(this, c));
+        }
+
+        // Add the package capabilities (export package)
+        List<Capability> pkgCaps = new ArrayList<>();
+        for(PackageInfo exported : bd.getExportedPackages()) {
+            Map<String, Object> attrs = new HashMap<>();
+            attrs.put(PackageNamespace.PACKAGE_NAMESPACE, exported.getName());
+            attrs.put(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE, 
exported.getPackageVersion());
+            
attrs.put(PackageNamespace.CAPABILITY_BUNDLE_SYMBOLICNAME_ATTRIBUTE, 
bd.getBundleSymbolicName());
+            attrs.put(PackageNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE, 
new Version(bd.getBundleVersion()));
+            pkgCaps.add(new OSGiCapability(this, 
PackageNamespace.PACKAGE_NAMESPACE, attrs, Collections.emptyMap()));
+        }
+        caps.put(PackageNamespace.PACKAGE_NAMESPACE, 
Collections.unmodifiableList(pkgCaps));
+
+        // Add the bundle capability
+        Map<String, Object> battrs = new HashMap<>();
+        battrs.put(BundleNamespace.BUNDLE_NAMESPACE, 
bd.getBundleSymbolicName());
+        battrs.put(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE, new 
Version(bd.getBundleVersion()));
+        OSGiCapability bundleCap = new OSGiCapability(this, 
BundleNamespace.BUNDLE_NAMESPACE, battrs, Collections.emptyMap());
+        caps.put(BundleNamespace.BUNDLE_NAMESPACE, 
Collections.singletonList(bundleCap));
+        capabilities = Collections.unmodifiableMap(caps);
+
+        Map<String, List<Requirement>> reqs = new HashMap<>();
+        for (Requirement r : bd.getRequirements()) {
+            List<Requirement> l = reqs.get(r.getNamespace());
+            if (l == null) {
+                l = new ArrayList<>();
+                reqs.put(r.getNamespace(), l);
+            }
+            // Add the requirement and associate with this resource
+            l.add(new OSGiRequirement(this, r));
+        }
+
+        // TODO What do we do with the execution environment?
+        
reqs.remove(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE);
+
+        // Add the package requirements (import package)
+        List<Requirement> pkgReqs = new ArrayList<>();
+        for(PackageInfo imported : bd.getImportedPackages()) {
+            Map<String, String> dirs = new HashMap<>();
+            VersionRange range = imported.getPackageVersionRange();
+            String rangeFilter;
+            if (range != null) {
+                rangeFilter = 
range.toFilterString(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+            } else {
+                rangeFilter = "";
+            }
+            dirs.put(PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE,
+                "(&(" + PackageNamespace.PACKAGE_NAMESPACE + "=" + 
imported.getName() + ")" + rangeFilter + ")");
+            if (imported.isOptional())
+                dirs.put(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE,
+                    PackageNamespace.RESOLUTION_OPTIONAL);
+            pkgReqs.add(new OSGiRequirement(this, 
PackageNamespace.PACKAGE_NAMESPACE, Collections.emptyMap(), dirs));
+        }
+        reqs.put(PackageNamespace.PACKAGE_NAMESPACE, 
Collections.unmodifiableList(pkgReqs));
+        requirements = Collections.unmodifiableMap(reqs);
+    }
+
+    /**
+     * Constructor. Create a resource based on capabilties and requirements.
+     * @param hnt
+     * @param caps The capabilities of the resource.
+     * @param reqs The requirements of the resource.
+     */
+    public BundleResourceImpl(Map<String, List<Capability>> caps, Map<String, 
List<Requirement>> reqs) {
+        hint = "" + System.identityHashCode(this);
+        capabilities = caps;
+        requirements = reqs;
+    }
+
+    @Override
+    public List<Capability> getCapabilities(String namespace) {
+        if (namespace == null) {
+            return 
capabilities.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
+        }
+
+        List<Capability> caps = capabilities.get(namespace);
+        if (caps == null)
+            return Collections.emptyList();
+        return caps;
+    }
+
+    @Override
+    public List<Requirement> getRequirements(String namespace) {
+        if (namespace == null) {
+            return 
requirements.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
+        }
+
+        List<Requirement> reqs = requirements.get(namespace);
+        if (reqs == null)
+            return Collections.emptyList();
+        return reqs;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((capabilities == null) ? 0 : 
capabilities.hashCode());
+        result = prime * result + ((requirements == null) ? 0 : 
requirements.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        BundleResourceImpl other = (BundleResourceImpl) obj;
+        if (capabilities == null) {
+            if (other.capabilities != null)
+                return false;
+        } else if (!capabilities.equals(other.capabilities))
+            return false;
+        if (requirements == null) {
+            if (other.requirements != null)
+                return false;
+        } else if (!requirements.equals(other.requirements))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "BundleResourceImpl [" + hint + "]";
+    }
+}
diff --git 
a/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/impl/ResolveContextImpl.java
 
b/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/impl/ResolveContextImpl.java
new file mode 100644
index 0000000..91f4183
--- /dev/null
+++ 
b/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/impl/ResolveContextImpl.java
@@ -0,0 +1,96 @@
+/*
+ * 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.sling.feature.resolver.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wiring;
+import org.osgi.service.resolver.HostedCapability;
+import org.osgi.service.resolver.ResolveContext;
+
+/**
+ * Implementation of the OSGi ResolveContext for use with the OSGi Resolver.
+ */
+public class ResolveContextImpl extends ResolveContext {
+    private final Resource bundle;
+    private final Collection<Resource> availableResources;
+
+    /**
+     * Constructor.
+     * @param mainResource The main resource to resolve.
+     * @param available The available resources to provide dependencies.
+     */
+    public ResolveContextImpl(Resource mainResource, Collection<Resource> 
available) {
+        bundle = mainResource;
+        availableResources = available;
+    }
+
+    @Override
+    public Collection<Resource> getMandatoryResources() {
+        return Collections.singleton(bundle);
+    }
+
+    @Override
+    public List<Capability> findProviders(Requirement requirement) {
+        List<Capability> providers = new ArrayList<>();
+
+        String f = requirement.getDirectives().get("filter");
+        try {
+            Filter filter = FrameworkUtil.createFilter(f);
+            for (Resource r : availableResources) {
+                for (Capability c : 
r.getCapabilities(requirement.getNamespace())) {
+                    if (filter.matches(c.getAttributes())) {
+                        providers.add(c);
+                    }
+                }
+            }
+        } catch (InvalidSyntaxException e) {
+            throw new RuntimeException("Invalid filter " + f + " in 
requirement " + requirement);
+        }
+
+        return providers;
+    }
+
+    @Override
+    public int insertHostedCapability(List<Capability> capabilities, 
HostedCapability hostedCapability) {
+        capabilities.add(0, hostedCapability);
+        return 0;
+    }
+
+    @Override
+    public boolean isEffective(Requirement requirement) {
+        String eff = requirement.getDirectives().get("effective");
+        if (eff == null)
+            return true; // resolve is the default
+        return "resolve".equals(eff.trim());
+    }
+
+    @Override
+    public Map<Resource, Wiring> getWirings() {
+        return Collections.emptyMap();
+    }
+}
diff --git 
a/featuremodel/feature-resolver/src/test/java/org/apache/sling/feature/resolver/FrameworkResolverTest.java
 
b/featuremodel/feature-resolver/src/test/java/org/apache/sling/feature/resolver/FrameworkResolverTest.java
new file mode 100644
index 0000000..3f28644
--- /dev/null
+++ 
b/featuremodel/feature-resolver/src/test/java/org/apache/sling/feature/resolver/FrameworkResolverTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.sling.feature.resolver;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.FileReader;
+import java.net.URL;
+import java.nio.file.FileVisitOption;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.process.FeatureResolver;
+import org.apache.sling.feature.support.ArtifactHandler;
+import org.apache.sling.feature.support.ArtifactManager;
+import org.apache.sling.feature.support.ArtifactManagerConfig;
+import org.apache.sling.feature.support.json.FeatureJSONReader;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+
+public class FrameworkResolverTest {
+    private Path tempDir;
+
+    @Before
+    public void setup() throws Exception {
+        tempDir = Files.createTempDirectory(getClass().getSimpleName());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Delete the temp dir again
+        Files.walk(tempDir, FileVisitOption.FOLLOW_LINKS)
+            .sorted(Comparator.reverseOrder())
+            .map(Path::toFile)
+            .forEach(File::delete);
+    }
+
+    private Map<String, String> getFrameworkProps() {
+        return Collections.singletonMap(Constants.FRAMEWORK_STORAGE, 
tempDir.toFile().getAbsolutePath());
+    }
+
+    @Test
+    public void testResolveEmptyFeatureList() throws Exception {
+        ArtifactManager am = ArtifactManager.getArtifactManager(new 
ArtifactManagerConfig());
+        try (FeatureResolver fr = new FrameworkResolver(am, 
getFrameworkProps())) {
+            assertEquals(Collections.emptyList(),
+                    fr.orderFeatures(Collections.emptyList()));
+        }
+    }
+
+    @Test
+    public void testOrderFeatures() throws Exception {
+        ArtifactManager am = ArtifactManager.getArtifactManager(new 
ArtifactManagerConfig());
+
+        Feature f1 = readFeature("/feature1.json", am);
+        Feature f2 = readFeature("/feature2.json", am);
+        Feature f3 = readFeature("/feature3.json", am);
+
+        try (FeatureResolver fr = new FrameworkResolver(am, 
getFrameworkProps())) {
+            List<Feature> ordered = fr.orderFeatures(Arrays.asList(f1, f2, 
f3));
+            List<Feature> expected = Arrays.asList(f3, f2, f1);
+            assertEquals(expected, ordered);
+        }
+    }
+
+    private Feature readFeature(final String res,
+            final ArtifactManager artifactManager) throws Exception {
+        URL url = getClass().getResource(res);
+        String file = new File(url.toURI()).getAbsolutePath();
+        final ArtifactHandler featureArtifact = 
artifactManager.getArtifactHandler(file);
+
+        try (final FileReader r = new FileReader(featureArtifact.getFile())) {
+            final Feature f = FeatureJSONReader.read(r, 
featureArtifact.getUrl());
+            return f;
+        }
+    }
+}
diff --git 
a/featuremodel/feature-resolver/src/test/java/org/apache/sling/feature/resolver/impl/BundleResourceImplTest.java
 
b/featuremodel/feature-resolver/src/test/java/org/apache/sling/feature/resolver/impl/BundleResourceImplTest.java
new file mode 100644
index 0000000..08deb2c
--- /dev/null
+++ 
b/featuremodel/feature-resolver/src/test/java/org/apache/sling/feature/resolver/impl/BundleResourceImplTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.sling.feature.resolver.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+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 org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.OSGiCapability;
+import org.apache.sling.feature.OSGiRequirement;
+import org.apache.sling.feature.analyser.BundleDescriptor;
+import org.apache.sling.feature.analyser.Descriptor;
+import org.apache.sling.feature.analyser.impl.BundleDescriptorImpl;
+import org.apache.sling.feature.support.util.PackageInfo;
+import org.junit.Test;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+public class BundleResourceImplTest {
+    @Test
+    public void testResource() {
+        Map<String, List<Capability>> caps = new HashMap<>();
+
+        Capability c1 = new OSGiCapability("ns.1",
+                Collections.singletonMap("ns.1", "c1"), 
Collections.emptyMap());
+        Capability c2 = new OSGiCapability("ns.1",
+                Collections.singletonMap("ns.1", "c2"), 
Collections.emptyMap());
+        List<Capability> capLst1 = Arrays.asList(c1, c2);
+        caps.put("ns.1", capLst1);
+        Capability c3 = new OSGiCapability("ns.2",
+                Collections.singletonMap("ns.2", "c3"), 
Collections.emptyMap());
+        List<Capability> capLst2 = Collections.singletonList(c3);
+        caps.put("ns.2", capLst2);
+
+        Requirement r1 = new OSGiRequirement("ns.1",
+                Collections.emptyMap(), Collections.singletonMap("mydir", 
"myvalue"));
+        List<Requirement> reqList = Collections.singletonList(r1);
+        Resource res = new BundleResourceImpl(caps,
+                Collections.singletonMap("ns.1", reqList));
+
+        assertEquals(0, res.getCapabilities("nonexistent").size());
+        assertEquals(0, res.getRequirements("ns.2").size());
+        assertEquals(capLst1, res.getCapabilities("ns.1"));
+        assertEquals(reqList, res.getRequirements("ns.1"));
+
+        List<Capability> mergedCaps = res.getCapabilities(null);
+        assertEquals(3, mergedCaps.size());
+        assertTrue(mergedCaps.containsAll(capLst1));
+        assertTrue(mergedCaps.containsAll(capLst2));
+        assertEquals(reqList, res.getRequirements(null));
+    }
+
+    @Test
+    public void testBundleResource() throws Exception {
+        ArtifactId id = new ArtifactId("grp", "art", "1.2.3", null, null);
+        Artifact artifact = new Artifact(id);
+
+        PackageInfo ex1 = new PackageInfo("org.foo.a", "0.0.1.SNAPSHOT", 
false);
+        Set<PackageInfo> pkgs = Collections.singleton(ex1);
+        Set<Requirement> reqs = Collections.emptySet();
+        Set<Capability> caps = Collections.emptySet();
+        BundleDescriptor bd = new BundleDescriptorImpl(artifact, pkgs, reqs, 
caps);
+
+        setField(Descriptor.class, "locked", bd, false); // Unlock the Bundle 
Descriptor for the test
+        PackageInfo im1 = new PackageInfo("org.bar", "[1,2)", false);
+        PackageInfo im2 = new PackageInfo("org.tar", null, true);
+        bd.getImportedPackages().add(im1);
+        bd.getImportedPackages().add(im2);
+
+        Resource res = new BundleResourceImpl(bd);
+        assertNotNull(
+                getCapAttribute(res, BundleNamespace.BUNDLE_NAMESPACE, 
BundleNamespace.BUNDLE_NAMESPACE));
+        assertEquals(new Version("1.2.3"),
+                getCapAttribute(res, BundleNamespace.BUNDLE_NAMESPACE, 
BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE));
+
+        List<Capability> exports = 
res.getCapabilities(PackageNamespace.PACKAGE_NAMESPACE);
+        assertEquals(1, exports.size());
+        assertEquals("org.foo.a",
+                getCapAttribute(res, PackageNamespace.PACKAGE_NAMESPACE, 
PackageNamespace.PACKAGE_NAMESPACE));
+        assertEquals(new Version("0.0.1.SNAPSHOT"),
+                getCapAttribute(res, PackageNamespace.PACKAGE_NAMESPACE, 
PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE));
+        assertEquals(getCapAttribute(res, BundleNamespace.BUNDLE_NAMESPACE, 
BundleNamespace.BUNDLE_NAMESPACE),
+                getCapAttribute(res, PackageNamespace.PACKAGE_NAMESPACE, 
PackageNamespace.CAPABILITY_BUNDLE_SYMBOLICNAME_ATTRIBUTE));
+        assertEquals(new Version("1.2.3"),
+                getCapAttribute(res, PackageNamespace.PACKAGE_NAMESPACE, 
PackageNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE));
+
+        List<Requirement> requirements = 
res.getRequirements(PackageNamespace.PACKAGE_NAMESPACE);
+        assertEquals(2, requirements.size());
+
+        Requirement reqBar = null;
+        Requirement reqTar = null;
+        for (Requirement req : requirements) {
+            if (req.getDirectives().get("filter").contains("org.bar"))
+                reqBar = req;
+            else
+                reqTar = req;
+        }
+
+        assertEquals(1, reqBar.getDirectives().size());
+        
assertEquals("(&(osgi.wiring.package=org.bar)(&(version>=1.0.0)(!(version>=2.0.0))))",
+                
reqBar.getDirectives().get(PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE));
+
+        assertEquals(2, reqTar.getDirectives().size());
+        assertEquals("(&(osgi.wiring.package=org.tar))",
+                
reqTar.getDirectives().get(PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE));
+        assertEquals(PackageNamespace.RESOLUTION_OPTIONAL,
+                
reqTar.getDirectives().get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE));
+    }
+
+    @Test
+    public void testBundleResourceGenericCapReq() throws Exception {
+        ArtifactId id = new ArtifactId("org.apache", 
"org.apache.someartifact", "0.0.0", null, null);
+        Artifact artifact = new Artifact(id);
+
+        Capability cap = new OSGiCapability("org.example.cap1",
+                Collections.singletonMap("intAttr", 999),
+                Collections.singletonMap("somedir", "mydir"));
+        Set<Capability> caps = Collections.singleton(cap);
+
+        Requirement req1 = new OSGiRequirement("org.example.req1",
+                Collections.singletonMap("boolAttr", true),
+                Collections.singletonMap("adir", "aval"));
+        Requirement req2 = new OSGiRequirement("org.example.req2",
+                Collections.singletonMap("boolAttr", false),
+                Collections.singletonMap("adir", "aval2"));
+        Set<Requirement> reqs = new HashSet<>(Arrays.asList(req1, req2));
+        BundleDescriptorImpl bd = new BundleDescriptorImpl(artifact, 
Collections.emptySet(), reqs, caps);
+
+        Resource res = new BundleResourceImpl(bd);
+
+        assertEquals(caps, new 
HashSet<>(res.getCapabilities("org.example.cap1")));
+        assertEquals(Collections.singleton(req1),
+                new HashSet<>(res.getRequirements("org.example.req1")));
+        assertEquals(Collections.singleton(req2),
+                new HashSet<>(res.getRequirements("org.example.req2")));
+        assertEquals(reqs, new HashSet<>(res.getRequirements(null)));
+    }
+
+    private Object getCapAttribute(Resource res, String ns, String attr) {
+        List<Capability> caps = res.getCapabilities(ns);
+        if (caps.size() == 0)
+            return null;
+
+        Capability cap = caps.iterator().next();
+        return cap.getAttributes().get(attr);
+    }
+
+    private void setField(Class<?> cls, String field, Object obj, Object val) 
throws Exception {
+        Field f = cls.getDeclaredField(field);
+        f.setAccessible(true);
+        f.set(obj, val);
+    }
+}
diff --git 
a/featuremodel/feature-resolver/src/test/java/org/apache/sling/feature/resolver/impl/ResolveContextImplTest.java
 
b/featuremodel/feature-resolver/src/test/java/org/apache/sling/feature/resolver/impl/ResolveContextImplTest.java
new file mode 100644
index 0000000..b63d14b
--- /dev/null
+++ 
b/featuremodel/feature-resolver/src/test/java/org/apache/sling/feature/resolver/impl/ResolveContextImplTest.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.sling.feature.resolver.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sling.feature.OSGiCapability;
+import org.apache.sling.feature.OSGiRequirement;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.service.resolver.HostedCapability;
+import org.osgi.service.resolver.ResolveContext;
+
+public class ResolveContextImplTest {
+    @Test
+    public void testMandatory() {
+        Resource mainRes = new BundleResourceImpl(Collections.emptyMap(), 
Collections.emptyMap());
+        List<Resource> available = Arrays.asList();
+        ResolveContext ctx = new ResolveContextImpl(mainRes, available);
+
+        assertEquals(Collections.singleton(mainRes), 
ctx.getMandatoryResources());
+    }
+
+    @Test
+    public void testFindProviders() {
+        Resource res1 = exportBundle("org.foo", "2");
+        Resource res2 = exportBundle("org.bar", "1.2");
+        Resource res3 = exportBundle("org.foo", "1.0.0.TESTING");
+        Resource res4 = exportBundle("org.foo", "1.9");
+
+        Resource mainRes = new BundleResourceImpl(Collections.emptyMap(), 
Collections.emptyMap());
+        List<Resource> available = Arrays.asList(res1, res2, res3, res4);
+        ResolveContext ctx = new ResolveContextImpl(mainRes, available);
+
+        Requirement req = new 
OSGiRequirement(PackageNamespace.PACKAGE_NAMESPACE,
+                Collections.emptyMap(),
+                Collections.singletonMap("filter",
+                        
"(&(osgi.wiring.package=org.foo)(&(version>=1.0.0)(!(version>=2.0.0))))"));
+
+        List<Capability> expected = new ArrayList<>();
+        expected.addAll(res3.getCapabilities(null));
+        expected.addAll(res4.getCapabilities(null));
+        List<Capability> providers = ctx.findProviders(req);
+        assertEquals(expected, providers);
+    }
+
+    private Resource exportBundle(String pkg, String version) {
+        Map<String, Object> attrs = new HashMap<>();
+        attrs.put(PackageNamespace.PACKAGE_NAMESPACE, pkg);
+        attrs.put(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE, new 
Version(version));
+        Capability cap = new OSGiCapability(PackageNamespace.PACKAGE_NAMESPACE,
+                attrs, Collections.emptyMap());
+        return new BundleResourceImpl(
+                Collections.singletonMap(PackageNamespace.PACKAGE_NAMESPACE,
+                        Collections.singletonList(cap)),
+                Collections.emptyMap());
+    }
+
+    @Test
+    public void testInsertHostedCapability() {
+        ResolveContext ctx = new 
ResolveContextImpl(Mockito.mock(Resource.class),
+                Collections.emptyList());
+
+        Capability cap1 =
+                new OSGiCapability("abc1", Collections.emptyMap(), 
Collections.emptyMap());
+        Capability cap2 =
+                new OSGiCapability("abc2", Collections.emptyMap(), 
Collections.emptyMap());
+        List<Capability> caps = new ArrayList<>();
+        caps.add(cap1);
+        caps.add(cap2);
+
+        HostedCapability hc = Mockito.mock(HostedCapability.class);
+        int idx = ctx.insertHostedCapability(caps, hc);
+        assertSame(hc, caps.get(idx));
+        assertEquals(3, caps.size());
+    }
+
+    @Test
+    public void testEffectiveRequirement() {
+        ResolveContext ctx = new 
ResolveContextImpl(Mockito.mock(Resource.class),
+                Collections.emptyList());
+
+        Map<String, String> dirs = new HashMap<>();
+        dirs.put("filter", "(somekey=someval)");
+        dirs.put("effective", "resolve ");
+        Requirement ereq1 = new 
OSGiRequirement(PackageNamespace.PACKAGE_NAMESPACE,
+                Collections.emptyMap(), dirs);
+        assertTrue(ctx.isEffective(ereq1));
+
+        Requirement ereq2 = new 
OSGiRequirement(PackageNamespace.PACKAGE_NAMESPACE,
+                Collections.emptyMap(),
+                Collections.singletonMap("filter", "(a=b)"));
+        assertTrue(ctx.isEffective(ereq2));
+
+        Requirement req3 = new 
OSGiRequirement(PackageNamespace.PACKAGE_NAMESPACE,
+                Collections.emptyMap(),
+                Collections.singletonMap("effective", "active"));
+        assertFalse(ctx.isEffective(req3));
+    }
+}
\ No newline at end of file
diff --git a/featuremodel/feature-resolver/src/test/resources/feature1.json 
b/featuremodel/feature-resolver/src/test/resources/feature1.json
new file mode 100644
index 0000000..80288e5
--- /dev/null
+++ b/featuremodel/feature-resolver/src/test/resources/feature1.json
@@ -0,0 +1,5 @@
+{
+    "id": "org.apache.sling.test.features/feature1/1.0.0",
+    "bundles": 
+        ["org.apache.sling/org.apache.sling.commons.logservice/1.0.6"]
+}
\ No newline at end of file
diff --git a/featuremodel/feature-resolver/src/test/resources/feature2.json 
b/featuremodel/feature-resolver/src/test/resources/feature2.json
new file mode 100644
index 0000000..53a8f98
--- /dev/null
+++ b/featuremodel/feature-resolver/src/test/resources/feature2.json
@@ -0,0 +1,5 @@
+{
+    "id": "org.apache.sling.test.features/feature2/1.0.0",
+    "bundles": 
+        ["org.slf4j/slf4j-api/1.7.25"]
+}
\ No newline at end of file
diff --git a/featuremodel/feature-resolver/src/test/resources/feature3.json 
b/featuremodel/feature-resolver/src/test/resources/feature3.json
new file mode 100644
index 0000000..33a09a8
--- /dev/null
+++ b/featuremodel/feature-resolver/src/test/resources/feature3.json
@@ -0,0 +1,5 @@
+{
+    "id": "org.apache.sling.test.features/feature3/1.0.0",
+    "bundles": 
+        ["org.slf4j/slf4j-simple/1.7.25"]
+}
\ No newline at end of file
diff --git 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/FeatureUtil.java
 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/FeatureUtil.java
index 69b245a..86bd08c 100644
--- 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/FeatureUtil.java
+++ 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/FeatureUtil.java
@@ -31,10 +31,10 @@
 import org.apache.sling.feature.process.ApplicationBuilder;
 import org.apache.sling.feature.process.BuilderContext;
 import org.apache.sling.feature.process.FeatureProvider;
+import org.apache.sling.feature.process.FeatureResolver;
 import org.apache.sling.feature.support.json.FeatureJSONReader;
 
 public class FeatureUtil {
-
     /**
      * Get an artifact id for the Apache Felix framework
      * @param version The version to use or {@code null} for the default 
version
@@ -211,13 +211,14 @@ private static void processFile(final List<String> paths, 
final File f)
      * @param app The optional application to use as a base.
      * @param featureFiles The feature files.
      * @param artifactManager The artifact manager
+     * @param fr
      * @return The assembled application
      * @throws IOException If a feature can't be read or no feature is found.
      * @see #getFeatureFiles(File, String...)
      */
     public static Application assembleApplication(
             Application app,
-            final ArtifactManager artifactManager, final String... 
featureFiles)
+            final ArtifactManager artifactManager, FeatureResolver fr, final 
String... featureFiles)
     throws IOException {
         final List<Feature> features = new ArrayList<>();
         for(final String initFile : featureFiles) {
@@ -225,12 +226,12 @@ public static Application assembleApplication(
             features.add(f);
         }
 
-        return assembleApplication(app, artifactManager, features.toArray(new 
Feature[0]));
+        return assembleApplication(app, artifactManager, fr, 
features.toArray(new Feature[0]));
     }
 
     public static Application assembleApplication(
             Application app,
-            final ArtifactManager artifactManager, final Feature... features)
+            final ArtifactManager artifactManager, FeatureResolver fr, final 
Feature... features)
     throws IOException {
         if ( features.length == 0 ) {
             throw new IOException("No features found.");
@@ -252,7 +253,7 @@ public Feature provide(final ArtifactId id) {
                 }
                 return null;
             }
-        }), features);
+        }), fr, features);
 
         // check framework
         if ( app.getFramework() == null ) {
diff --git 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONReader.java
 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONReader.java
index d865398..9c50a41 100644
--- 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONReader.java
+++ 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONReader.java
@@ -24,6 +24,7 @@
 import java.io.Reader;
 import java.io.StringReader;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -32,10 +33,12 @@
 
 import org.apache.felix.configurator.impl.json.JSONUtil;
 import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Capability;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.Include;
-import org.apache.sling.feature.Requirement;
+import org.apache.sling.feature.OSGiCapability;
+import org.apache.sling.feature.OSGiRequirement;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 /**
  * This class offers a method to read a {@code Feature} using a {@code Reader} 
instance.
@@ -255,22 +258,24 @@ private void readRequirements(Map<String, Object> map) 
throws IOException {
                 }
                 checkType("Requirement namespace", 
obj.get(JSONConstants.REQCAP_NAMESPACE), String.class);
 
-                final Requirement r = new 
Requirement(obj.get(JSONConstants.REQCAP_NAMESPACE).toString());
-                feature.getRequirements().add(r);
-
+                Map<String, Object> attrMap = new HashMap<>();
                 if ( obj.containsKey(JSONConstants.REQCAP_ATTRIBUTES) ) {
                     checkType("Requirement attributes", 
obj.get(JSONConstants.REQCAP_ATTRIBUTES), Map.class);
                     @SuppressWarnings("unchecked")
                     final Map<String, Object> attrs = (Map<String, 
Object>)obj.get(JSONConstants.REQCAP_ATTRIBUTES);
-                    attrs.forEach(rethrowBiConsumer((key, value) -> 
unmarshalAttribute(key, value, r.getAttributes()::put)));
+                    attrs.forEach(rethrowBiConsumer((key, value) -> 
unmarshalAttribute(key, value, attrMap::put)));
                 }
 
+                Map<String, String> dirMap = new HashMap<>();
                 if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
                     checkType("Requirement directives", 
obj.get(JSONConstants.REQCAP_DIRECTIVES), Map.class);
                     @SuppressWarnings("unchecked")
                     final Map<String, Object> dirs = (Map<String, 
Object>)obj.get(JSONConstants.REQCAP_DIRECTIVES);
-                    dirs.forEach(rethrowBiConsumer((key, value) -> 
unmarshalDirective(key, value, r.getDirectives()::put)));
+                    dirs.forEach(rethrowBiConsumer((key, value) -> 
unmarshalDirective(key, value, dirMap::put)));
                 }
+
+                final Requirement r = new 
OSGiRequirement(obj.get(JSONConstants.REQCAP_NAMESPACE).toString(), attrMap, 
dirMap);
+                feature.getRequirements().add(r);
             }
         }
     }
@@ -292,22 +297,24 @@ private void readCapabilities(Map<String, Object> map) 
throws IOException {
                 }
                 checkType("Capability namespace", 
obj.get(JSONConstants.REQCAP_NAMESPACE), String.class);
 
-                final Capability c = new 
Capability(obj.get(JSONConstants.REQCAP_NAMESPACE).toString());
-                feature.getCapabilities().add(c);
-
+                Map<String, Object> attrMap = new HashMap<>();
                 if ( obj.containsKey(JSONConstants.REQCAP_ATTRIBUTES) ) {
                     checkType("Capability attributes", 
obj.get(JSONConstants.REQCAP_ATTRIBUTES), Map.class);
                     @SuppressWarnings("unchecked")
                     final Map<String, Object> attrs = (Map<String, 
Object>)obj.get(JSONConstants.REQCAP_ATTRIBUTES);
-                    attrs.forEach(rethrowBiConsumer((key, value) -> 
unmarshalAttribute(key, value, c.getAttributes()::put)));
+                    attrs.forEach(rethrowBiConsumer((key, value) -> 
unmarshalAttribute(key, value, attrMap::put)));
                 }
 
+                Map<String, String> dirMap = new HashMap<>();
                 if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
                     checkType("Capability directives", 
obj.get(JSONConstants.REQCAP_DIRECTIVES), Map.class);
                     @SuppressWarnings("unchecked")
                     final Map<String, Object> dirs = (Map<String, Object>) 
obj.get(JSONConstants.REQCAP_DIRECTIVES);
-                    dirs.forEach(rethrowBiConsumer((key, value) -> 
unmarshalDirective(key, value, c.getDirectives()::put)));
+                    dirs.forEach(rethrowBiConsumer((key, value) -> 
unmarshalDirective(key, value, dirMap::put)));
                 }
+
+                final Capability c = new 
OSGiCapability(obj.get(JSONConstants.REQCAP_NAMESPACE).toString(), attrMap, 
dirMap);
+                feature.getCapabilities().add(c);
             }
         }
     }
diff --git 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONWriter.java
 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONWriter.java
index 13c82f1..3c64391 100644
--- 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONWriter.java
+++ 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONWriter.java
@@ -28,13 +28,12 @@
 import javax.json.stream.JsonGenerator;
 
 import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Capability;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Configurations;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.Include;
-import org.apache.sling.feature.Requirement;
-
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 /**
  * Simple JSON writer for a feature
diff --git 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/CapabilityMatcher.java
 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/CapabilityMatcher.java
index 74b1466..cca0bdd 100644
--- 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/CapabilityMatcher.java
+++ 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/CapabilityMatcher.java
@@ -1,9 +1,9 @@
 package org.apache.sling.feature.support.util;
 
-import org.apache.sling.feature.Capability;
-import org.apache.sling.feature.Requirement;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
@@ -497,7 +497,7 @@ private static List convertArrayToList(Object array)
 
     public static  boolean matches(Capability capability, Requirement 
requirement) {
         if (requirement.getNamespace().equals(capability.getNamespace())) {
-            String filter = (String) 
requirement.getDirectives().get(Constants.FILTER_DIRECTIVE);
+            String filter = 
requirement.getDirectives().get(Constants.FILTER_DIRECTIVE);
             if (filter != null) {
                 return matches(capability, SimpleFilter.parse(filter));
             }
@@ -512,7 +512,7 @@ public static boolean isOptional(Requirement requirement) {
 
     public static boolean isAttributeMandatory(Capability capability, String 
name)
     {
-        String value = (String) 
capability.getDirectives().get(Constants.MANDATORY_DIRECTIVE);
+        String value = 
capability.getDirectives().get(Constants.MANDATORY_DIRECTIVE);
         if (value != null)
         {
             return parseDelimitedString(value, ",").contains(name);
diff --git 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestParser.java
 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestParser.java
index d4b0030..522eaf5 100644
--- 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestParser.java
+++ 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestParser.java
@@ -25,8 +25,8 @@
 import java.util.Map.Entry;
 import java.util.jar.Manifest;
 
-import org.apache.sling.feature.Capability;
-import org.apache.sling.feature.Requirement;
+import org.apache.sling.feature.OSGiCapability;
+import org.apache.sling.feature.OSGiRequirement;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
@@ -34,6 +34,8 @@
 import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
 import org.osgi.framework.namespace.IdentityNamespace;
 import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 public class ManifestParser
 {
@@ -104,7 +106,7 @@ public ManifestParser(Manifest m)
                 capList.add(bundleCap);
                 // A non-fragment bundle can choose to not have a host 
capability.
                 String attachment =
-                        (String) 
bundleCap.getDirectives().get(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
+                        
bundleCap.getDirectives().get(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
                 attachment = (attachment == null)
                         ? Constants.FRAGMENT_ATTACHMENT_RESOLVETIME
                         : attachment;
@@ -114,9 +116,7 @@ public ManifestParser(Manifest m)
                             new HashMap<>(bundleCap.getAttributes());
                     Object value = 
hostAttrs.remove(BundleRevision.BUNDLE_NAMESPACE);
                     hostAttrs.put(BundleRevision.HOST_NAMESPACE, value);
-                    Capability cap = new 
Capability(BundleRevision.HOST_NAMESPACE);
-                    cap.getAttributes().putAll(hostAttrs);
-                    cap.getDirectives().putAll(bundleCap.getDirectives());
+                    Capability cap = new 
OSGiCapability(BundleRevision.HOST_NAMESPACE, hostAttrs, 
bundleCap.getDirectives());
                     capList.add(cap);
                 }
             }
@@ -197,9 +197,7 @@ public ManifestParser(Manifest m)
                             + "' namespace.");
                 }
 
-                Requirement req = new Requirement(path);
-                req.getAttributes().putAll(clause.m_attrs);
-                req.getDirectives().putAll(clause.m_dirs);
+                Requirement req = new OSGiRequirement(path, clause.m_attrs, 
clause.m_dirs);
                 // Create requirement and add to requirement list.
                 reqList.add(req);
             }
@@ -329,9 +327,7 @@ else if (listType.equals("Long"))
                             + "' namespace.");
                 }
 
-                Capability capability = new Capability(path);
-                capability.getAttributes().putAll(clause.m_attrs);
-                capability.getDirectives().putAll(clause.m_dirs);
+                Capability capability = new OSGiCapability(path, 
clause.m_attrs, clause.m_dirs);
                 // Create package capability and add to capability list.
                 capList.add(capability);
             }
@@ -437,9 +433,7 @@ else if (clauses.get(0).m_paths.size() > 1)
             String symName = clauses.get(0).m_paths.get(0);
             clauses.get(0).m_attrs.put(BundleRevision.BUNDLE_NAMESPACE, 
symName);
             clauses.get(0).m_attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, 
bundleVersion);
-            Capability cap = new Capability(BundleRevision.BUNDLE_NAMESPACE);
-            cap.getAttributes().putAll(clauses.get(0).m_attrs);
-            cap.getAttributes().putAll(clauses.get(0).m_dirs);
+            Capability cap = new 
OSGiCapability(BundleRevision.BUNDLE_NAMESPACE, clauses.get(0).m_attrs, 
clauses.get(0).m_dirs);
 
             return cap;
         }
@@ -486,15 +480,13 @@ private static Capability addIdentityCapability(Manifest 
headerMap, Capability b
         if (bundleCap.getDirectives().get(Constants.SINGLETON_DIRECTIVE) != 
null)
         {
             dirs = 
Collections.singletonMap(IdentityNamespace.CAPABILITY_SINGLETON_DIRECTIVE,
-                    (String) 
bundleCap.getDirectives().get(Constants.SINGLETON_DIRECTIVE));
+                    
bundleCap.getDirectives().get(Constants.SINGLETON_DIRECTIVE));
         }
         else
         {
             dirs = Collections.emptyMap();
         }
-        Capability cap = new Capability(IdentityNamespace.IDENTITY_NAMESPACE);
-        cap.getAttributes().putAll(attrs);
-        cap.getDirectives().putAll(dirs);
+        Capability cap = new 
OSGiCapability(IdentityNamespace.IDENTITY_NAMESPACE, attrs, dirs);
         return cap;
     }
 
@@ -565,9 +557,7 @@ else if (clauses.get(0).m_paths.size() > 1)
                         Constants.FILTER_DIRECTIVE,
                         sf.toString());
 
-                Requirement req = new 
Requirement(BundleRevision.HOST_NAMESPACE);
-                req.getAttributes().putAll(newAttrs);
-                req.getDirectives().putAll(newDirs);
+                Requirement req = new 
OSGiRequirement(BundleRevision.HOST_NAMESPACE, newAttrs, newDirs);
                 reqs.add(req);
             }
         }
@@ -686,8 +676,8 @@ else if 
(headerMap.getMainAttributes().getValue(Constants.FRAGMENT_HOST) != null
             }
 
             SimpleFilter sf = SimpleFilter.parse(reqFilter);
-            Requirement req = new 
Requirement(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE);
-            
req.getDirectives().put(ExecutionEnvironmentNamespace.REQUIREMENT_FILTER_DIRECTIVE,
 reqFilter);
+            Requirement req = new 
OSGiRequirement(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE, 
Collections.emptyMap(),
+                    
Collections.singletonMap(ExecutionEnvironmentNamespace.REQUIREMENT_FILTER_DIRECTIVE,
 reqFilter));
             return Collections.<Requirement>singletonList(req);
         }
     }
@@ -762,9 +752,7 @@ private static String getBreeVersionClause(Version ver) {
                         Constants.FILTER_DIRECTIVE,
                         sf.toString());
 
-                Requirement req = new 
Requirement(BundleRevision.BUNDLE_NAMESPACE);
-                req.getAttributes().putAll(newAttrs);
-                req.getDirectives().putAll(newDirs);
+                Requirement req = new 
OSGiRequirement(BundleRevision.BUNDLE_NAMESPACE, newAttrs, newDirs);
                 reqList.add(req);
             }
         }
diff --git 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestUtil.java
 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestUtil.java
index 4158ff3..cacc4c3 100644
--- 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestUtil.java
+++ 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestUtil.java
@@ -17,10 +17,10 @@
 package org.apache.sling.feature.support.util;
 
 import org.apache.sling.commons.osgi.ManifestHeader;
-import org.apache.sling.feature.Capability;
-import org.apache.sling.feature.Requirement;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 import java.io.File;
 import java.io.IOException;
@@ -105,11 +105,11 @@ public static void unmarshalAttribute(String key, Object 
value, BiConsumer<Strin
         unmarshal(key + "=" + value, Capability::getAttributes, sink);
     }
 
-    public static void unmarshalDirective(String key, Object value, 
BiConsumer<String, Object> sink) throws IOException {
+    public static void unmarshalDirective(String key, Object value, 
BiConsumer<String, String> sink) throws IOException {
         unmarshal(key + ":=" + value, Capability::getDirectives, sink);
     }
 
-    private static void unmarshal(String header, Function<Capability, 
Map<String, Object>> lookup, BiConsumer<String, Object> sink) throws 
IOException {
+    private static <T> void unmarshal(String header, Function<Capability, 
Map<String, T>> lookup, BiConsumer<String, T> sink) throws IOException {
         try {
             convertProvideCapabilities(
                     normalizeCapabilityClauses(parseStandardHeader("foo;" + 
header), "2"))
diff --git 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/PackageInfo.java
 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/PackageInfo.java
index 83d4e79..725906d 100644
--- 
a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/PackageInfo.java
+++ 
b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/PackageInfo.java
@@ -44,10 +44,16 @@ public boolean isOptional() {
     }
 
     public Version getPackageVersion() {
+        if (this.version == null)
+            return null;
+
         return new Version(this.version);
     }
 
     public VersionRange getPackageVersionRange() {
+        if (this.version == null)
+            return null;
+
         return new VersionRange(this.version);
     }
 
diff --git 
a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/json/FeatureJSONReaderTest.java
 
b/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/json/FeatureJSONReaderTest.java
index f84838b..238d50d 100644
--- 
a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/json/FeatureJSONReaderTest.java
+++ 
b/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/json/FeatureJSONReaderTest.java
@@ -16,18 +16,16 @@
  */
 package org.apache.sling.feature.support.json;
 
-import org.apache.sling.feature.Capability;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Feature;
 import org.junit.Test;
+import org.osgi.resource.Capability;
 
 import java.util.Arrays;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
 
 public class FeatureJSONReaderTest {
 
diff --git 
a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/json/U.java
 
b/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/json/U.java
index fd3007f..6122b01 100644
--- 
a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/json/U.java
+++ 
b/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/json/U.java
@@ -16,10 +16,10 @@
  */
 package org.apache.sling.feature.support.json;
 
-import org.apache.sling.feature.Capability;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.Requirement;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 import java.io.InputStreamReader;
 import java.io.Reader;
diff --git 
a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/util/CapabilityMatcherTest.java
 
b/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/util/CapabilityMatcherTest.java
index 1b88562..99567eb 100644
--- 
a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/util/CapabilityMatcherTest.java
+++ 
b/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/util/CapabilityMatcherTest.java
@@ -19,15 +19,15 @@
 import static junit.framework.TestCase.assertTrue;
 
 import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.Requirement;
 import org.apache.sling.feature.support.json.U;
 import org.junit.Test;
+import org.osgi.resource.Requirement;
 
 public class CapabilityMatcherTest {
     @Test public void testCapabilityMatching() throws Exception {
         Feature feature = U.readFeature("test");
         Requirement requirement = U.findRequirement(feature.getRequirements(), 
"osgi.contract");
         
assertTrue(CapabilityMatcher.matches(U.findCapability(feature.getCapabilities(),
 "osgi.contract"),
-                SimpleFilter.parse((String) 
requirement.getDirectives().get("filter"))));
+                
SimpleFilter.parse(requirement.getDirectives().get("filter"))));
     }
 }
diff --git 
a/featuremodel/feature/src/main/java/org/apache/sling/feature/AbstractCapabilityRequirement.java
 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/AbstractCapabilityRequirement.java
new file mode 100644
index 0000000..200cb92
--- /dev/null
+++ 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/AbstractCapabilityRequirement.java
@@ -0,0 +1,96 @@
+/*
+ * 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.sling.feature;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.resource.Resource;
+
+abstract class AbstractCapabilityRequirement {
+    private final Resource resource;
+    private final String namespace;
+    private final Map<String, Object> attributes;
+    private final Map<String, String> directives;
+
+    AbstractCapabilityRequirement(Resource res, String ns, Map<String, Object> 
attrs, Map<String, String> dirs) {
+        resource = res;
+        namespace = ns;
+        attributes = Collections.unmodifiableMap(new HashMap<>(attrs));
+        directives = Collections.unmodifiableMap(new HashMap<>(dirs));
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    public Map<String, String> getDirectives() {
+        return directives;
+    }
+
+    public Resource getResource() {
+        return resource;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((attributes == null) ? 0 : 
attributes.hashCode());
+        result = prime * result + ((directives == null) ? 0 : 
directives.hashCode());
+        result = prime * result + ((namespace == null) ? 0 : 
namespace.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        AbstractCapabilityRequirement other = (AbstractCapabilityRequirement) 
obj;
+        if (attributes == null) {
+            if (other.attributes != null)
+                return false;
+        } else if (!attributes.equals(other.attributes))
+            return false;
+        if (directives == null) {
+            if (other.directives != null)
+                return false;
+        } else if (!directives.equals(other.directives))
+            return false;
+        if (namespace == null) {
+            if (other.namespace != null)
+                return false;
+        } else if (!namespace.equals(other.namespace))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " [resource=" + resource + ", 
namespace=" + namespace + ", attributes=" + attributes
+                + ", directives=" + directives + "]";
+    }
+}
diff --git 
a/featuremodel/feature/src/main/java/org/apache/sling/feature/Capability.java 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/Capability.java
deleted file mode 100644
index 53c9c3b..0000000
--- 
a/featuremodel/feature/src/main/java/org/apache/sling/feature/Capability.java
+++ /dev/null
@@ -1,104 +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.sling.feature;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * A capability of a feature.
- * The capability is modeled after an OSGi capability: it
- * belongs to a namespace and might have attributes and / or
- * directives.
- */
-public class Capability {
-
-    /** The namspace. */
-    private final String namespace;
-
-    /** Map of attributes. */
-    private final Map<String, Object> attributes = new ConcurrentHashMap<>();
-
-    /** Map of directives. */
-    private final Map<String, Object> directives = new ConcurrentHashMap<>();
-
-    /**
-     * Create a new Capability.
-     * @param namespace The namespace
-     * @throws IllegalArgumentException If namespace is {@code null}.
-     */
-    public Capability(final String namespace) {
-        if ( namespace == null ) {
-            throw new IllegalArgumentException("namespace must not be null.");
-        }
-        this.namespace = namespace;
-    }
-
-    /**
-     * The namespace
-     * @return The namespace
-     */
-    public String getNamespace() {
-        return namespace;
-    }
-
-    /**
-     * Get the map of attributes.
-     * The map is modifiable.
-     * @return The map of attributes.
-     */
-    public Map<String, Object> getAttributes() {
-        return attributes;
-    }
-
-    /**
-     * Get the map of directives.
-     * The map is modifiable.
-     * @return The map of directives.
-     */
-    public Map<String, Object> getDirectives() {
-        return directives;
-    }
-
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + attributes.hashCode();
-        result = prime * result + directives.hashCode();
-        result = prime * result + namespace.hashCode();
-        return result;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        final Capability other = (Capability) obj;
-        if (!attributes.equals(other.attributes)
-            || !directives.equals(other.directives)
-            || !namespace.equals(other.namespace)) {
-            return false;
-        }
-        return true;
-    }
-}
diff --git 
a/featuremodel/feature/src/main/java/org/apache/sling/feature/Feature.java 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/Feature.java
index 64c2488..874790d 100644
--- a/featuremodel/feature/src/main/java/org/apache/sling/feature/Feature.java
+++ b/featuremodel/feature/src/main/java/org/apache/sling/feature/Feature.java
@@ -20,6 +20,9 @@
 import java.util.Enumeration;
 import java.util.List;
 
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+
 /**
  * A feature consists of
  * <ul>
@@ -296,17 +299,13 @@ public Feature copy(final ArtifactId id) {
 
         // requirements
         for(final Requirement r : this.getRequirements()) {
-            final Requirement c = new Requirement(r.getNamespace());
-            c.getAttributes().putAll(r.getAttributes());
-            c.getDirectives().putAll(r.getDirectives());
+            final Requirement c = new OSGiRequirement(r.getNamespace(), 
r.getAttributes(), r.getDirectives());
             result.getRequirements().add(c);
         }
 
         // capabilities
         for(final Capability r : this.getCapabilities()) {
-            final Capability c = new Capability(r.getNamespace());
-            c.getAttributes().putAll(r.getAttributes());
-            c.getDirectives().putAll(r.getDirectives());
+            final Capability c = new OSGiCapability(r.getNamespace(), 
r.getAttributes(), r.getDirectives());
             result.getCapabilities().add(c);
         }
 
diff --git 
a/featuremodel/feature/src/main/java/org/apache/sling/feature/OSGiCapability.java
 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/OSGiCapability.java
new file mode 100644
index 0000000..5c2000d
--- /dev/null
+++ 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/OSGiCapability.java
@@ -0,0 +1,59 @@
+/*
+ * 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.sling.feature;
+
+import java.util.Map;
+
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+/**
+ * Implementation of the OSGi Capability interface.
+ */
+public class OSGiCapability extends AbstractCapabilityRequirement implements 
Capability {
+    /**
+     * Create a capability that is not associated with a resource.
+     * @param res The resource associated with the capability. May be null.
+     * @param ns The namespace of the capability.
+     * @param attrs The attributes of the capability.
+     * @param dirs The directives of the capability.
+     */
+    public OSGiCapability(String ns, Map<String, Object> attrs, Map<String, 
String> dirs) {
+        this(null, ns, attrs, dirs);
+    }
+
+    /**
+     * Create a capability.
+     * @param res The resource associated with the capability. May be null.
+     * @param ns The namespace of the capability.
+     * @param attrs The attributes of the capability.
+     * @param dirs The directives of the capability.
+     */
+    public OSGiCapability(Resource res, String ns, Map<String, Object> attrs, 
Map<String, String> dirs) {
+        super(res, ns, attrs, dirs);
+    }
+
+    /**
+     * Create a capability based on an existing capability, providing the 
resource.
+     * The namespace, attributes and directives are copied from the provided 
capability.
+     * @param resource The resource to be associated with the capability
+     * @param capability The capability to base the new requirement on.
+     */
+    public OSGiCapability(Resource resource, Capability capability) {
+        this(resource, capability.getNamespace(), capability.getAttributes(), 
capability.getDirectives());
+    }
+}
diff --git 
a/featuremodel/feature/src/main/java/org/apache/sling/feature/OSGiRequirement.java
 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/OSGiRequirement.java
new file mode 100644
index 0000000..2727379
--- /dev/null
+++ 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/OSGiRequirement.java
@@ -0,0 +1,59 @@
+/*
+ * 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.sling.feature;
+
+import java.util.Map;
+
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+/**
+ * Implementation of the OSGi Requirement interface.
+ */
+public class OSGiRequirement extends AbstractCapabilityRequirement implements 
Requirement {
+    /**
+     * Create a requirement that is not associated with a resource.
+     * @param res The resource associated with the requirement.
+     * @param ns The namespace of the requirement.
+     * @param attrs The attributes of the requirement.
+     * @param dirs The directives of the requirement.
+     */
+    public OSGiRequirement(String ns, Map<String, Object> attrs, Map<String, 
String> dirs) {
+        this(null, ns, attrs, dirs);
+    }
+
+    /**
+     * Create a requirement.
+     * @param res The resource associated with the requirement.
+     * @param ns The namespace of the requirement.
+     * @param attrs The attributes of the requirement.
+     * @param dirs The directives of the requirement.
+     */
+    public OSGiRequirement(Resource res, String ns, Map<String, Object> attrs, 
Map<String, String> dirs) {
+        super(res, ns, attrs, dirs);
+    }
+
+    /**
+     * Create a requirement based on an existing requirement, providing the 
resource.
+     * The namespace, attributes and directives are copied from the provided 
requirement.
+     * @param resource The resource to be associated with the requirement
+     * @param requirement The requirement to base the new requirement on.
+     */
+    public OSGiRequirement(Resource resource, Requirement requirement) {
+        this(resource, requirement.getNamespace(), 
requirement.getAttributes(), requirement.getDirectives());
+    }
+}
diff --git 
a/featuremodel/feature/src/main/java/org/apache/sling/feature/Requirement.java 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/Requirement.java
deleted file mode 100644
index 3409e51..0000000
--- 
a/featuremodel/feature/src/main/java/org/apache/sling/feature/Requirement.java
+++ /dev/null
@@ -1,113 +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.sling.feature;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * A requirement for a feature.
- * The requirement is modeled after an OSGi requirement: it
- * belongs to a namespace and might have attributes and / or
- * directives.
- */
-public class Requirement {
-
-    /*public static final String RESOLUTION_OPTIONAL = "optional";
-
-    public static final String RESOLUTION_DIRECTIVE = "resolution";*/
-
-    /** The namspace. */
-    private final String namespace;
-
-    /** Map of attributes. */
-    private final Map<String, Object> attributes = new ConcurrentHashMap<>();
-
-    /** Map of directives. */
-    private final Map<String, Object> directives = new ConcurrentHashMap<>();
-
-    /**
-     * Create a new Requirement.
-     * @param namespace The namespace
-     * @throws IllegalArgumentException If namespace is {@code null}.
-     */
-    public Requirement(final String namespace) {
-        if ( namespace == null ) {
-            throw new IllegalArgumentException("namespace must not be null.");
-        }
-        this.namespace = namespace;
-    }
-
-    /**
-     * The namespace
-     * @return The namespace
-     */
-    public String getNamespace() {
-        return namespace;
-    }
-
-    /**
-     * Get the map of attributes.
-     * The map is modifiable.
-     * @return The map of attributes.
-     */
-    public Map<String,Object> getAttributes() {
-        return attributes;
-    }
-
-    /**
-     * Get the map of directives.
-     * The map is modifiable.
-     * @return The map of directives.
-     */
-    public Map<String,Object> getDirectives() {
-        return directives;
-    }
-
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + attributes.hashCode();
-        result = prime * result + directives.hashCode();
-        result = prime * result + namespace.hashCode();
-        return result;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        final Requirement other = (Requirement) obj;
-        if (!attributes.equals(other.attributes)
-            || !directives.equals(other.directives)
-            || !namespace.equals(other.namespace)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        return "[Requirement namespace='" + namespace + "' attributes=" + 
attributes + " directives=" + directives + "]";
-    }
-}
diff --git 
a/featuremodel/feature/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java
 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java
index 1a555e1..49c17b8 100644
--- 
a/featuremodel/feature/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java
+++ 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java
@@ -17,7 +17,6 @@
 package org.apache.sling.feature.process;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 import org.apache.sling.feature.Application;
@@ -38,6 +37,7 @@
      *
      * @param app The optional application to use as a base.
      * @param context The builder context
+     * @param resolver The Feature Resolver to use
      * @param featureIds The feature ids
      * @return The application
      * throws IllegalArgumentException If context or featureIds is {@code null}
@@ -45,6 +45,7 @@
      */
     public static Application assemble(final Application app,
             final BuilderContext context,
+            final FeatureResolver resolver,
             final String... featureIds) {
         if ( featureIds == null || context == null ) {
             throw new IllegalArgumentException("Features and/or context must 
not be null");
@@ -59,7 +60,7 @@ public static Application assemble(final Application app,
             }
             index++;
         }
-        return assemble(app, context, features);
+        return assemble(app, context, resolver, features);
     }
 
     /**
@@ -70,6 +71,7 @@ public static Application assemble(final Application app,
      *
      * @param app The optional application to use as a base.
      * @param context The builder context
+     * @param resolver The Feature Resolver to use
      * @param features The features
      * @return The application
      * throws IllegalArgumentException If context or featureIds is {@code null}
@@ -78,7 +80,7 @@ public static Application assemble(final Application app,
     public static Application assemble(
             Application app,
             final BuilderContext context,
-            final Feature... features) {
+            final FeatureResolver resolver, final Feature... features) {
         if ( features == null || context == null ) {
             throw new IllegalArgumentException("Features and/or context must 
not be null");
         }
@@ -89,7 +91,7 @@ public static Application assemble(
 
         // Created sorted feature list
         // Remove duplicate features by selecting the one with the highest 
version
-        final List<Feature> sortedFeatureList = new ArrayList<>();
+        List<Feature> sortedFeatureList = new ArrayList<>();
         for(final Feature f : features) {
             Feature found = null;
             for(final Feature s : sortedFeatureList) {
@@ -116,8 +118,8 @@ public static Application assemble(
             }
         }
 
-        // sort
-        Collections.sort(sortedFeatureList);
+        // order by dependency chain
+        sortedFeatureList = resolver.orderFeatures(sortedFeatureList);
 
         // assemble
         for(final Feature f : sortedFeatureList) {
diff --git 
a/featuremodel/feature/src/main/java/org/apache/sling/feature/process/BuilderUtil.java
 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/process/BuilderUtil.java
index 377a5be..90da88e 100644
--- 
a/featuremodel/feature/src/main/java/org/apache/sling/feature/process/BuilderUtil.java
+++ 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/process/BuilderUtil.java
@@ -31,13 +31,13 @@
 import org.apache.sling.feature.Application;
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.Bundles;
-import org.apache.sling.feature.Capability;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Configurations;
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.KeyValueMap;
-import org.apache.sling.feature.Requirement;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 /**
  * Utility methods for the builders
diff --git 
a/featuremodel/feature/src/main/java/org/apache/sling/feature/process/FeatureResolver.java
 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/process/FeatureResolver.java
new file mode 100644
index 0000000..b3eaa35
--- /dev/null
+++ 
b/featuremodel/feature/src/main/java/org/apache/sling/feature/process/FeatureResolver.java
@@ -0,0 +1,38 @@
+/*
+ * 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.sling.feature.process;
+
+import java.util.List;
+
+import org.apache.sling.feature.Feature;
+
+/**
+ * A resolver that can perform operations on the feature model.
+ */
+public interface FeatureResolver extends AutoCloseable {
+    /**
+     * Order the features by their dependency chain. Each feature and its
+     * components are resolved and each other feature providing the 
capabilities
+     * needed by the feature is placed before the requiring feature in the
+     * result.
+     *
+     * @param features
+     *            The features to order.
+     * @return The ordered features.
+     */
+    List<Feature> orderFeatures(List<Feature> features);
+}
diff --git 
a/featuremodel/feature/src/test/java/org/apache/sling/feature/CapabilityRequirementTest.java
 
b/featuremodel/feature/src/test/java/org/apache/sling/feature/CapabilityRequirementTest.java
new file mode 100644
index 0000000..e834bad
--- /dev/null
+++ 
b/featuremodel/feature/src/test/java/org/apache/sling/feature/CapabilityRequirementTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.sling.feature;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+public class CapabilityRequirementTest {
+    @Test
+    public void testCapability() {
+        Map<String, Object> attrs = new HashMap<>();
+        attrs.put("org.foo", "1234");
+        attrs.put("bar", 456);
+        Map<String, String> dirs = new HashMap<>();
+        dirs.put("my_dir", "my_value");
+        Capability c = new OSGiCapability("org.foo", attrs, dirs);
+        assertEquals("org.foo", c.getNamespace());
+        assertEquals(attrs, c.getAttributes());
+        assertEquals(dirs, c.getDirectives());
+        assertNull(c.getResource());
+    }
+
+    @Test
+    public void testRequirement() {
+        Resource tr = new TestResource();
+        Requirement r = new OSGiRequirement(tr, "testing",
+                Collections.emptyMap(), Collections.emptyMap());
+        assertEquals(tr, r.getResource());
+        assertEquals(0, r.getAttributes().size());
+        assertEquals(0, r.getDirectives().size());
+    }
+
+    private static class TestResource implements Resource {
+        @Override
+        public List<Capability> getCapabilities(String namespace) {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public List<Requirement> getRequirements(String namespace) {
+            return Collections.emptyList();
+        }
+    }
+}
diff --git 
a/featuremodel/feature/src/test/java/org/apache/sling/feature/process/FeatureBuilderTest.java
 
b/featuremodel/feature/src/test/java/org/apache/sling/feature/process/FeatureBuilderTest.java
index f990aec..ca27cf5 100644
--- 
a/featuremodel/feature/src/test/java/org/apache/sling/feature/process/FeatureBuilderTest.java
+++ 
b/featuremodel/feature/src/test/java/org/apache/sling/feature/process/FeatureBuilderTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertTrue;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -29,13 +30,15 @@
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.BundlesTest;
-import org.apache.sling.feature.Capability;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.Include;
-import org.apache.sling.feature.Requirement;
+import org.apache.sling.feature.OSGiCapability;
+import org.apache.sling.feature.OSGiRequirement;
 import org.junit.Test;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 public class FeatureBuilderTest {
 
@@ -193,18 +196,19 @@ private void equals(final Feature expected, final Feature 
actuals) {
     @Test public void testNoIncludesNoUpgrade() throws Exception {
         final Feature base = new 
Feature(ArtifactId.parse("org.apache.sling/test-feature/1.1"));
 
-        final Requirement r1 = new Requirement("osgi.contract");
-        r1.getDirectives().put("filter", 
"(&(osgi.contract=JavaServlet)(version=3.1))");
+        final Requirement r1 = new OSGiRequirement("osgi.contract",
+                Collections.emptyMap(), Collections.singletonMap("filter", 
"(&(osgi.contract=JavaServlet)(version=3.1))"));
         base.getRequirements().add(r1);
 
-        final Capability c1 = new Capability("osgi.implementation");
-        c1.getAttributes().put("osgi.implementation", "osgi.http");
-        c1.getAttributes().put("version:Version", "1.1");
-        c1.getDirectives().put("uses", 
"javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard");
+        Map<String, Object> attrs = new HashMap<>();
+        attrs.put("osgi.implementation", "osgi.http");
+        attrs.put("version:Version", "1.1");
+        final Capability c1 = new OSGiCapability("osgi.implementation", attrs,
+                Collections.singletonMap("uses", 
"javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard"));
         base.getCapabilities().add(c1);
-        final Capability c2 = new Capability("osgi.service");
-        c2.getAttributes().put("objectClass:List<String>", 
"org.osgi.service.http.runtime.HttpServiceRuntime");
-        c2.getDirectives().put("uses", 
"org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto");
+        final Capability c2 = new OSGiCapability("osgi.service",
+                Collections.singletonMap("objectClass:List<String>", 
"org.osgi.service.http.runtime.HttpServiceRuntime"),
+                Collections.singletonMap("uses", 
"org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto"));
         base.getCapabilities().add(c2);
 
         base.getFrameworkProperties().put("foo", "1");
@@ -241,14 +245,15 @@ private void equals(final Feature expected, final Feature 
actuals) {
         final Include i1 = new Include(ArtifactId.parse("g/a/1"));
         base.getIncludes().add(i1);
 
-        final Requirement r1 = new Requirement("osgi.contract");
-        r1.getDirectives().put("filter", 
"(&(osgi.contract=JavaServlet)(version=3.1))");
+        final Requirement r1 = new OSGiRequirement("osgi.contract",
+                Collections.emptyMap(), Collections.singletonMap("filter", 
"(&(osgi.contract=JavaServlet)(version=3.1))"));
         base.getRequirements().add(r1);
 
-        final Capability c1 = new Capability("osgi.implementation");
-        c1.getAttributes().put("osgi.implementation", "osgi.http");
-        c1.getAttributes().put("version:Version", "1.1");
-        c1.getDirectives().put("uses", 
"javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard");
+        Map<String, Object> attrs = new HashMap<>();
+        attrs.put("osgi.implementation", "osgi.http");
+        attrs.put("version:Version", "1.1");
+        final Capability c1 = new OSGiCapability("osgi.implementation", attrs,
+                Collections.singletonMap("uses", 
"javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard"));
         base.getCapabilities().add(c1);
 
         base.getFrameworkProperties().put("foo", "1");
diff --git a/featuremodel/pom.xml b/featuremodel/pom.xml
index 110bd53..b50abd0 100644
--- a/featuremodel/pom.xml
+++ b/featuremodel/pom.xml
@@ -63,6 +63,7 @@
         <module>feature-analyser</module>
         <module>feature-launcher</module>
         <module>feature-support</module>       
+        <module>feature-resolver</module>
         <module>feature-applicationbuilder</module>
     </modules>
 


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to