You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@sling.apache.org by GitBox <gi...@apache.org> on 2018/03/07 09:09:09 UTC

[GitHub] cziegeler closed pull request #9: SLING-7521 Order bundles in the generated app based on feature order and start order

cziegeler closed pull request #9: SLING-7521 Order bundles in the generated app based on feature order and start order
URL: https://github.com/apache/sling-whiteboard/pull/9
 
 
   

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/example/sling/oak.json b/featuremodel/example/sling/oak.json
index 1a1eed5..9a65d86 100644
--- a/featuremodel/example/sling/oak.json
+++ b/featuremodel/example/sling/oak.json
@@ -3,35 +3,35 @@
     "bundles": [
         {
             "id": "org.apache.felix/org.apache.felix.jaas/1.0.2",
-            "startOrder" : 10
+            "start-order" : 10
         },
         {
             "id": "org.apache.jackrabbit/oak-blob/1.6.4",
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.jackrabbit/oak-commons/1.6.4",
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.jackrabbit/oak-core/1.6.4",
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.jackrabbit/oak-jcr/1.6.4", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.jackrabbit/oak-lucene/1.6.4",
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.jackrabbit/oak-segment-tar/1.6.4",
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.sling/org.apache.sling.jcr.oak.server/1.1.4",
-            "startOrder" : 16
+            "start-order" : 16
         }
     ],
     "configurations": {
diff --git a/featuremodel/example/sling/sling.json b/featuremodel/example/sling/sling.json
index e3b5ff5..75fd227 100644
--- a/featuremodel/example/sling/sling.json
+++ b/featuremodel/example/sling/sling.json
@@ -3,415 +3,415 @@
     "bundles": [
         {
             "id": "commons-fileupload/commons-fileupload/1.3.2",
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "commons-io/commons-io/2.5",
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.aries.jmx/org.apache.aries.jmx.api/1.1.5",
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.aries.jmx/org.apache.aries.jmx.core/1.1.7",
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.aries.jmx/org.apache.aries.jmx.whiteboard/1.1.5",
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.felix/org.apache.felix.bundlerepository/1.6.4",
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.felix/org.apache.felix.inventory/1.0.4",
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.felix/org.apache.felix.prefs/1.1.0", 
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.felix/org.apache.felix.webconsole.plugins.ds/2.0.6", 
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.felix/org.apache.felix.webconsole.plugins.event/1.1.6", 
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.felix/org.apache.felix.webconsole.plugins.memoryusage/1.0.6", 
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.felix/org.apache.felix.webconsole.plugins.obr/1.0.4", 
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.felix/org.apache.felix.webconsole.plugins.packageadmin/1.0.4", 
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.felix/org.apache.felix.webconsole/4.3.4", 
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.sling/org.apache.sling.commons.johnzon/1.1.0", 
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.sling/org.apache.sling.commons.log.webconsole/1.0.0", 
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.sling/org.apache.sling.extensions.threaddump/0.2.2", 
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.sling/org.apache.sling.extensions.webconsolebranding/1.0.2", 
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.sling/org.apache.sling.extensions.webconsolesecurityprovider/1.0.0", 
-            "startOrder" : 5
+            "start-order" : 5
         },
         {
             "id": "org.apache.felix/org.apache.felix.http.sslfilter/1.2.2", 
-            "startOrder" : 10
+            "start-order" : 10
         },
         {
             "id": "org.apache.felix/org.apache.felix.metatype/1.1.4", 
-            "startOrder" : 10
+            "start-order" : 10
         },
         {
             "id": "org.apache.felix/org.apache.felix.scr/2.0.12", 
-            "startOrder" : 10
+            "start-order" : 10
         },
         {
             "id": "org.apache.pdfbox/fontbox/2.0.7", 
-            "startOrder" : 10
+            "start-order" : 10
         },
         {
             "id": "org.apache.pdfbox/jempbox/1.8.13", 
-            "startOrder" : 10
+            "start-order" : 10
         },
         {
             "id": "org.apache.pdfbox/pdfbox/2.0.7", 
-            "startOrder" : 10
+            "start-order" : 10
         },
         {
             "id": "org.apache.tika/tika-core/1.14", 
-            "startOrder" : 10
+            "start-order" : 10
         },
         {
             "id": "org.apache.tika/tika-parsers/1.14", 
-            "startOrder" : 10
+            "start-order" : 10
         },
         {
             "id": "com.google.guava/guava/15.0",
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "io.dropwizard.metrics/metrics-core/3.2.3",
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.jackrabbit/jackrabbit-api/2.14.3", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.jackrabbit/jackrabbit-data/2.14.3", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.jackrabbit/jackrabbit-jcr-commons/2.14.3", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.jackrabbit/jackrabbit-jcr-rmi/2.14.3", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.jackrabbit/jackrabbit-spi-commons/2.14.3", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.jackrabbit/jackrabbit-spi/2.14.3", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.jackrabbit/jackrabbit-webdav/2.14.3", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.sling/org.apache.sling.commons.metrics/1.2.0", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.sling/org.apache.sling.jcr.api/2.4.0", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.sling/org.apache.sling.jcr.base/3.0.4", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.sling/org.apache.sling.jcr.davex/1.3.8", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.sling/org.apache.sling.jcr.jackrabbit.accessmanager/3.0.0", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.sling/org.apache.sling.jcr.jackrabbit.usermanager/2.2.6", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.sling/org.apache.sling.jcr.jcr-wrapper/2.0.0", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.sling/org.apache.sling.jcr.registration/1.0.2", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.sling/org.apache.sling.jcr.webconsole/1.0.2", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "org.apache.sling/org.apache.sling.jcr.webdav/2.3.8", 
-            "startOrder" : 15
+            "start-order" : 15
         },
         {
             "id": "commons-codec/commons-codec/1.9", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "commons-collections/commons-collections/3.2.2", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "commons-lang/commons-lang/2.6", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "javax.mail/mail/1.4.7", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.commons/commons-collections4/4.1", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.commons/commons-lang3/3.5", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.commons/commons-math/2.2", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.felix/org.apache.felix.http.whiteboard/3.0.0", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.geronimo.bundles/commons-httpclient/3.1_1", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.geronimo.bundles/jstl/1.2_1", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.httpcomponents/httpclient-osgi/4.4.1", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.httpcomponents/httpcore-osgi/4.4.1", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.servicemix.bundles/org.apache.servicemix.bundles.rhino/1.7.7.1_1", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.adapter/2.1.10", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.api/2.16.2", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.auth.core/1.4.0", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.auth.form/1.0.8", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.bundleresource.impl/2.2.0", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.commons.classloader/1.4.0", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.commons.compiler/2.3.0", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.commons.fsclassloader/1.0.6", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.commons.mime/2.1.10", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.commons.osgi/2.4.0", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.commons.scheduler/2.6.2", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.commons.threads/3.2.6", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.engine/2.6.8", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.fsresource/2.1.8", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.hc.api/1.0.0", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.hc.core/1.2.8", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.hc.webconsole/1.1.2", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.i18n/2.5.8", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.installer.console/1.0.2", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.installer.provider.jcr/3.1.26", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.jcr.contentloader/2.2.4", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.jcr.resource/3.0.4", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.launchpad.content/2.0.12", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.models.api/1.3.4", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.models.impl/1.4.2", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.resourceresolver/1.5.30", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.scripting.api/2.2.0", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.scripting.core/2.0.46", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.scripting.el-api/1.0.0", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.scripting.javascript/3.0.2", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.scripting.jsp-api/1.0.0", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.scripting.jsp.taglib/2.2.6", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.scripting.jsp/2.3.2", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.scripting.sightly.compiler.java/1.0.12", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.scripting.sightly.compiler/1.0.12", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.scripting.sightly.js.provider/1.0.24", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.scripting.sightly.models.provider/1.0.6", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.scripting.sightly.repl/1.0.4", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.scripting.sightly/1.0.40", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.serviceusermapper/1.3.4", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.servlets.get/2.1.26", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.servlets.post/2.3.22", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.servlets.resolver/2.4.14", 
-            "startOrder" : 20
+            "start-order" : 20
         },
         {
             "id": "org.apache.sling/org.apache.sling.xss/2.0.0", 
-            "startOrder" : 20
+            "start-order" : 20
         }
     ],
     "configurations": {
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 d92398a..bff0af9 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
@@ -17,7 +17,10 @@
 package org.apache.sling.feature.analyser;
 
 import org.apache.sling.feature.Application;
+import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.FeatureResource;
+import org.apache.sling.feature.analyser.impl.BundleDescriptorImpl;
 import org.apache.sling.feature.analyser.service.Analyser;
 import org.apache.sling.feature.analyser.service.Scanner;
 import org.apache.sling.feature.process.FeatureResolver;
@@ -27,8 +30,11 @@
 import org.apache.sling.feature.support.json.FeatureJSONReader;
 import org.junit.Test;
 
+import java.io.File;
+import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.util.ArrayList;
 import java.util.List;
 
 import static junit.framework.TestCase.fail;
@@ -78,8 +84,31 @@ public void close() throws Exception {
             }
 
             @Override
-            public List<Feature> orderFeatures(List<Feature> features) {
-                return features;
+            public List<FeatureResource> orderResources(List<Feature> features) {
+                try {
+                    // Just return the resources in the same order as they are listed in the features
+                    List<FeatureResource> l = new ArrayList<>();
+
+                    for (Feature f : features) {
+                        for (Artifact a : f.getBundles()) {
+                            BundleDescriptor bd = getBundleDescriptor(ArtifactManager.getArtifactManager(new ArtifactManagerConfig()), a);
+                            l.add(new TestBundleResourceImpl(bd, f));
+                        }
+                    }
+
+                    return l;
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+
+            private 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-analyser/src/test/java/org/apache/sling/feature/analyser/TestBundleResourceImpl.java b/featuremodel/feature-analyser/src/test/java/org/apache/sling/feature/analyser/TestBundleResourceImpl.java
new file mode 100644
index 0000000..c06affe
--- /dev/null
+++ b/featuremodel/feature-analyser/src/test/java/org/apache/sling/feature/analyser/TestBundleResourceImpl.java
@@ -0,0 +1,231 @@
+/*
+ * 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.analyser;
+
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.FeatureResource;
+import org.apache.sling.feature.OSGiCapability;
+import org.apache.sling.feature.OSGiRequirement;
+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 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;
+
+/**
+ * Implementation of the OSGi Resource interface, used by the test
+ */
+public class TestBundleResourceImpl implements FeatureResource {
+    final Artifact artifact;
+    final String bsn;
+    final Version version;
+    final Map<String, List<Capability>> capabilities;
+    final Map<String, List<Requirement>> requirements;
+    final Feature feature;
+
+    /**
+     * Create a resource based on a BundleDescriptor.
+     * @param bd The BundleDescriptor to represent.
+     */
+    public TestBundleResourceImpl(BundleDescriptor bd, Feature feat) {
+        artifact = bd.getArtifact();
+        bsn = bd.getBundleSymbolicName();
+        version = bd.getArtifact().getId().getOSGiVersion();
+        feature = feat;
+
+        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);
+    }
+
+    @Override
+    public Artifact getArtifact() {
+        return artifact;
+    }
+
+    @Override
+    public String getId() {
+        return bsn;
+    }
+
+    @Override
+    public Version getVersion() {
+        return version;
+    }
+
+    @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 Feature getFeature() {
+        return feature;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((artifact == null) ? 0 : artifact.hashCode());
+        result = prime * result + ((bsn == null) ? 0 : bsn.hashCode());
+        result = prime * result + ((capabilities == null) ? 0 : capabilities.hashCode());
+        result = prime * result + ((feature == null) ? 0 : feature.hashCode());
+        result = prime * result + ((requirements == null) ? 0 : requirements.hashCode());
+        result = prime * result + ((version == null) ? 0 : version.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;
+        TestBundleResourceImpl other = (TestBundleResourceImpl) obj;
+        if (artifact == null) {
+            if (other.artifact != null)
+                return false;
+        } else if (!artifact.equals(other.artifact))
+            return false;
+        if (bsn == null) {
+            if (other.bsn != null)
+                return false;
+        } else if (!bsn.equals(other.bsn))
+            return false;
+        if (capabilities == null) {
+            if (other.capabilities != null)
+                return false;
+        } else if (!capabilities.equals(other.capabilities))
+            return false;
+        if (feature == null) {
+            if (other.feature != null)
+                return false;
+        } else if (!feature.equals(other.feature))
+            return false;
+        if (requirements == null) {
+            if (other.requirements != null)
+                return false;
+        } else if (!requirements.equals(other.requirements))
+            return false;
+        if (version == null) {
+            if (other.version != null)
+                return false;
+        } else if (!version.equals(other.version))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "BundleResourceImpl [" + bsn + " " + version + "]";
+    }
+}
diff --git a/featuremodel/feature-analyser/src/test/resources/feature_complete.json b/featuremodel/feature-analyser/src/test/resources/feature_complete.json
index 63b8bd0..6271a9c 100644
--- a/featuremodel/feature-analyser/src/test/resources/feature_complete.json
+++ b/featuremodel/feature-analyser/src/test/resources/feature_complete.json
@@ -4,79 +4,79 @@
     "bundles" : [
         {
           "id" : "org.apache.sling/org.apache.sling.commons.log/5.0.0",
-          "startOrder" : 1
+          "start-order" : 1
         },
         {
           "id" : "org.apache.sling/org.apache.sling.commons.logservice/1.0.6",
-          "startOrder" : 1
+          "start-order" : 1
         },
         {
           "id" : "org.slf4j/jcl-over-slf4j/1.7.21",
-          "startOrder" : 1
+          "start-order" : 1
         },
         {
           "id" : "org.slf4j/log4j-over-slf4j/1.7.21",
-          "startOrder" : 1
+          "start-order" : 1
         },
         {
           "id" : "org.slf4j/slf4j-api/1.7.21",
-          "startOrder" : 1
+          "start-order" : 1
         },
         {
           "id" : "org.apache.felix/org.apache.felix.configadmin/1.8.14",
-          "startOrder" : 1
+          "start-order" : 1
         },
         {
           "id" : "org.apache.felix/org.apache.felix.eventadmin/1.4.8",
-          "startOrder" : 4
+          "start-order" : 4
         },
         {
           "id" : "org.apache.felix/org.apache.felix.metatype/1.1.2",
-          "startOrder" : 4
+          "start-order" : 4
         },
         {
           "id" : "org.apache.felix/org.apache.felix.scr/2.0.12",
-          "startOrder" : 4
+          "start-order" : 4
         },
         {
           "id" : "org.apache.felix/org.apache.felix.http.jetty/3.4.2",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.felix/org.apache.felix.http.servlet-api/1.1.2",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "commons-io/commons-io/2.5",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "commons-fileupload/commons-fileupload/1.3.2",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.felix/org.apache.felix.inventory/1.0.4",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.felix/org.apache.felix.webconsole.plugins.ds/2.0.6",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.felix/org.apache.felix.webconsole.plugins.event/1.1.6",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.felix/org.apache.felix.webconsole.plugins.packageadmin/1.0.4",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.felix/org.apache.felix.webconsole/4.3.4",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.sling/org.apache.sling.commons.log.webconsole/1.0.0",
-          "startOrder" : 5
+          "start-order" : 5
         }
     ]
 }
diff --git a/featuremodel/feature-analyser/src/test/resources/feature_incomplete.json b/featuremodel/feature-analyser/src/test/resources/feature_incomplete.json
index 3ab884c..514e878 100644
--- a/featuremodel/feature-analyser/src/test/resources/feature_incomplete.json
+++ b/featuremodel/feature-analyser/src/test/resources/feature_incomplete.json
@@ -4,79 +4,79 @@
     "bundles" : [
         {
           "id" : "org.apache.sling/org.apache.sling.commons.log/5.0.0",
-          "startOrder" : 1
+          "start-order" : 1
         },
         {
           "id" : "org.apache.sling/org.apache.sling.commons.logservice/1.0.6",
-          "startOrder" : 1
+          "start-order" : 1
         },
         {
           "id" : "org.slf4j/jcl-over-slf4j/1.7.21",
-          "startOrder" : 1
+          "start-order" : 1
         },
         {
           "id" : "org.slf4j/log4j-over-slf4j/1.7.21",
-          "startOrder" : 1
+          "start-order" : 1
         },
         {
           "id" : "org.slf4j/slf4j-api/1.7.21",
-          "startOrder" : 1
+          "start-order" : 1
         },
         {
           "id" : "org.apache.felix/org.apache.felix.configadmin/1.8.14",
-          "startOrder" : 1
+          "start-order" : 1
         },
         {
           "id" : "org.apache.felix/org.apache.felix.eventadmin/1.4.8",
-          "startOrder" : 4
+          "start-order" : 4
         },
         {
           "id" : "org.apache.felix/org.apache.felix.metatype/1.1.2",
-          "startOrder" : 4
+          "start-order" : 4
         },
         {
           "id" : "org.apache.felix/org.apache.felix.scr/2.0.12",
-          "startOrder" : 4
+          "start-order" : 4
         },
         {
           "id" : "org.apache.felix/org.apache.felix.http.servlet-api/1.1.2",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "commons-io/commons-io/2.5",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "commons-fileupload/commons-fileupload/1.3.2",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.felix/org.apache.felix.inventory/1.0.4",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.felix/org.apache.felix.webconsole.plugins.ds/2.0.6",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.felix/org.apache.felix.webconsole.plugins.event/1.1.6",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.felix/org.apache.felix.webconsole.plugins.packageadmin/1.0.4",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.felix/org.apache.felix.webconsole/4.3.4",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.sling/org.apache.sling.commons.log.webconsole/1.0.0",
-          "startOrder" : 5
+          "start-order" : 5
         },
         {
           "id" : "org.apache.sling/org.apache.sling.i18n/2.5.8",
-          "startOrder" : 6
+          "start-order" : 6
         }
     ]
 }
diff --git a/featuremodel/feature-applicationbuilder/pom.xml b/featuremodel/feature-applicationbuilder/pom.xml
index 9f0d02f..917e103 100644
--- a/featuremodel/feature-applicationbuilder/pom.xml
+++ b/featuremodel/feature-applicationbuilder/pom.xml
@@ -132,11 +132,36 @@
             <version>0.0.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.osgi</artifactId>
+            <version>2.4.0</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 -->
+        <!-- Testing -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
-        	<groupId>junit</groupId>
-        	<artifactId>junit</artifactId>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework</artifactId>
+            <version>5.6.10</version>
+            <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.analyser</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>        
     </dependencies>
 </project>
diff --git a/featuremodel/feature-applicationbuilder/src/test/java/org/apache/sling/feature/applicationbuilder/impl/ApplicationBuilderTest.java b/featuremodel/feature-applicationbuilder/src/test/java/org/apache/sling/feature/applicationbuilder/impl/ApplicationBuilderTest.java
new file mode 100644
index 0000000..414d426
--- /dev/null
+++ b/featuremodel/feature-applicationbuilder/src/test/java/org/apache/sling/feature/applicationbuilder/impl/ApplicationBuilderTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.applicationbuilder.impl;
+
+import org.apache.sling.feature.Application;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Feature;
+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.resolver.FrameworkResolver;
+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.ApplicationJSONWriter;
+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;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class ApplicationBuilderTest {
+    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)
+            .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 testBundleOrdering() throws Exception {
+        FeatureProvider fp = new TestFeatureProvider();
+        BuilderContext bc = new BuilderContext(fp);
+        ArtifactManager am = ArtifactManager.getArtifactManager(new ArtifactManagerConfig());
+
+        Feature fa = readFeature("/featureA.json", am);
+        Feature fb = readFeature("/featureB.json", am);
+        Feature[] features = {fa, fb};
+
+        try (FeatureResolver fr = new FrameworkResolver(am, getFrameworkProps())) {
+            Application app = ApplicationBuilder.assemble(null, bc, fr, features);
+            String genApp = writeApplication(app);
+
+            String expected = "{\"features\":["
+                    + "\"org.apache.sling.test.features:featureB:1.0.0\","
+                    + "\"org.apache.sling.test.features:featureA:1.0.0\"],"
+                + "\"bundles\":["
+                    + "{\"id\":\"commons-io:commons-io:2.6\",\"start-order\":\"10\"},"
+                    + "{\"id\":\"org.apache.felix:org.apache.felix.http.servlet-api:1.1.2\",\"start-order\":\"15\"},"
+                    + "{\"id\":\"commons-fileupload:commons-fileupload:1.3.3\",\"start-order\":\"16\"}]}";
+            assertEquals(expected, genApp);
+        }
+    }
+
+    @Test
+    public void testFeatureDependency() throws Exception {
+        FeatureProvider fp = new TestFeatureProvider();
+        BuilderContext bc = new BuilderContext(fp);
+        ArtifactManager am = ArtifactManager.getArtifactManager(new ArtifactManagerConfig());
+
+        // Feature D has a bundle (slf4j-api) with a dependency on feature C,
+        // which provides a package for slf4j
+        Feature fc = readFeature("/featureC.json", am);
+        Feature fd = readFeature("/featureD.json", am);
+        Feature[] features = {fd, fc};
+
+        try (FeatureResolver fr = new FrameworkResolver(am, getFrameworkProps())) {
+            Application app = ApplicationBuilder.assemble(null, bc, fr, features);
+            String genApp = writeApplication(app);
+
+            String expected = "{\"features\":["
+                    + "\"org.apache.sling.test.features:featureC:1.0.0\","
+                    + "\"org.apache.sling.test.features:featureD:1.0.0\"],"
+                    + "\"bundles\":[{\"id\":\"org.slf4j:slf4j-api:1.7.25\",\"start-order\":\"6\"}]}";
+            assertEquals(expected, genApp);
+        }
+    }
+
+    private static String writeApplication(Application app) throws Exception {
+        Writer writer = new StringWriter();
+        ApplicationJSONWriter.write(writer, app);
+        return writer.toString();
+    }
+
+    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;
+        }
+    }
+
+    private static class TestFeatureProvider implements FeatureProvider {
+        @Override
+        public Feature provide(ArtifactId id) {
+            return null;
+        }
+    }
+}
diff --git a/featuremodel/feature-applicationbuilder/src/test/resources/featureA.json b/featuremodel/feature-applicationbuilder/src/test/resources/featureA.json
new file mode 100644
index 0000000..1daed33
--- /dev/null
+++ b/featuremodel/feature-applicationbuilder/src/test/resources/featureA.json
@@ -0,0 +1,5 @@
+{
+    "id": "org.apache.sling.test.features/featureA/1.0.0",
+    "bundles": 
+        ["commons-fileupload/commons-fileupload/1.3.3"]
+}
\ No newline at end of file
diff --git a/featuremodel/feature-applicationbuilder/src/test/resources/featureB.json b/featuremodel/feature-applicationbuilder/src/test/resources/featureB.json
new file mode 100644
index 0000000..952c810
--- /dev/null
+++ b/featuremodel/feature-applicationbuilder/src/test/resources/featureB.json
@@ -0,0 +1,14 @@
+{
+    "id": "org.apache.sling.test.features/featureB/1.0.0",
+    "bundles": 
+        [
+            {
+                "id": "org.apache.felix/org.apache.felix.http.servlet-api/1.1.2",
+                "start-order" : 10
+            },
+            {
+                "id": "commons-io/commons-io/2.6",
+                "start-order" : 5
+            }
+        ]
+}
\ No newline at end of file
diff --git a/featuremodel/feature-applicationbuilder/src/test/resources/featureC.json b/featuremodel/feature-applicationbuilder/src/test/resources/featureC.json
new file mode 100644
index 0000000..0aed875
--- /dev/null
+++ b/featuremodel/feature-applicationbuilder/src/test/resources/featureC.json
@@ -0,0 +1,18 @@
+{
+    "id": "org.apache.sling.test.features/featureC/1.0.0",
+    "capabilities": [
+        {
+            "namespace": "org.foo.bar",
+            "attributes": {
+                "org.foo.bar": "toast",
+                "version:Version": "1.1"
+            }
+        }, {
+            "namespace": "osgi.wiring.package",
+            "attributes": {
+                "osgi.wiring.package": "org.slf4j.impl",
+                "version:Version": "1.7.1.test"
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/featuremodel/feature-applicationbuilder/src/test/resources/featureD.json b/featuremodel/feature-applicationbuilder/src/test/resources/featureD.json
new file mode 100644
index 0000000..e9aa806
--- /dev/null
+++ b/featuremodel/feature-applicationbuilder/src/test/resources/featureD.json
@@ -0,0 +1,5 @@
+{
+    "id": "org.apache.sling.test.features/featureD/1.0.0",
+    "bundles": 
+        ["org.slf4j/slf4j-api/1.7.25"]
+}
\ No newline at end of file
diff --git a/featuremodel/feature-resolver/pom.xml b/featuremodel/feature-resolver/pom.xml
index 7d379f9..d4bcdf8 100644
--- a/featuremodel/feature-resolver/pom.xml
+++ b/featuremodel/feature-resolver/pom.xml
@@ -72,6 +72,12 @@
             <version>0.0.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.osgi</artifactId>
+            <version>2.4.0</version>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.service.resolver</artifactId>
@@ -96,5 +102,17 @@
             <version>5.6.10</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.configurator</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.johnzon</artifactId>
+            <version>1.0.0</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
index f62c3db..249f4f2 100644
--- 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
@@ -16,25 +16,15 @@
  */
 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.FeatureResource;
 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.FeatureResourceImpl;
 import org.apache.sling.feature.resolver.impl.ResolveContextImpl;
 import org.apache.sling.feature.support.ArtifactManager;
 import org.osgi.framework.BundleContext;
@@ -44,6 +34,7 @@
 import org.osgi.framework.launch.FrameworkFactory;
 import org.osgi.framework.namespace.BundleNamespace;
 import org.osgi.framework.namespace.HostNamespace;
+import org.osgi.framework.namespace.IdentityNamespace;
 import org.osgi.framework.namespace.PackageNamespace;
 import org.osgi.framework.wiring.BundleRevision;
 import org.osgi.resource.Capability;
@@ -53,10 +44,19 @@
 import org.osgi.service.resolver.ResolutionException;
 import org.osgi.service.resolver.Resolver;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+
 public class FrameworkResolver implements FeatureResolver {
     private final ArtifactManager artifactManager;
     private final Resolver resolver;
-    private final Resource frameworkResource;
+    private final FeatureResource frameworkResource;
     private final Framework framework;
 
     public FrameworkResolver(ArtifactManager am, Map<String, String> frameworkProperties) {
@@ -72,10 +72,13 @@ public FrameworkResolver(ArtifactManager am, Map<String, String> frameworkProper
             BundleContext ctx = framework.getBundleContext();
 
             // Create a resource representing the framework
+            Map<String, List<Capability>> capabilities = new HashMap<>();
             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());
+            capabilities.put(PackageNamespace.PACKAGE_NAMESPACE, br.getCapabilities(PackageNamespace.PACKAGE_NAMESPACE));
+            capabilities.put(BundleNamespace.BUNDLE_NAMESPACE, br.getCapabilities(BundleNamespace.BUNDLE_NAMESPACE));
+            capabilities.put(IdentityNamespace.IDENTITY_NAMESPACE, br.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE));
+            frameworkResource = new BundleResourceImpl(framework.getSymbolicName(), framework.getVersion(), null, null,
+                    capabilities, Collections.emptyMap());
 
             int i=0;
             while (i < 20) {
@@ -101,65 +104,89 @@ public void close() throws Exception {
     }
 
     @Override
-    public List<Feature> orderFeatures(List<Feature> features) {
+    public List<FeatureResource> orderResources(List<Feature> features) {
         try {
-            return internalOrderFeatures(features);
+            return internalOrderResources(features);
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
 
-    public List<Feature> internalOrderFeatures(List<Feature> features) throws IOException {
-        Map<Resource, Feature> bundleMap = new HashMap<>();
+    public List<FeatureResource> internalOrderResources(List<Feature> features) throws IOException {
+        Map<Feature, FeatureResource> featureMap = new HashMap<>();
+        Map<FeatureResource, Feature> resourceMap = new HashMap<>();
         for (Feature f : features) {
+            FeatureResourceImpl fr = new FeatureResourceImpl(f);
+            resourceMap.put(fr, f);
+            featureMap.put(f, fr);
+
             for (Artifact b : f.getBundles()) {
                 BundleDescriptor bd = getBundleDescriptor(artifactManager, b);
-                Resource r = new BundleResourceImpl(bd);
-                bundleMap.put(r, f);
+                FeatureResource r = new BundleResourceImpl(bd, f);
+                resourceMap.put(r, f);
             }
         }
 
-        Set<Resource> availableBundles = new HashSet<>(bundleMap.keySet());
-        // Add these to the available features
+        Map<String, FeatureResource> idVerMap = new HashMap<>();
+        for (FeatureResource fr : resourceMap.keySet()) {
+            idVerMap.put(fr.getId() + ":" + fr.getVersion(), fr);
+        }
+
+        // Add these too
         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);
+        idVerMap.put("org.apache.sling.launchpad.api:1.2.0", new BundleResourceImpl(getBundleDescriptor(artifactManager, lpa), null));
+        idVerMap.put(framework.getSymbolicName() + ":" + framework.getVersion(), frameworkResource);
 
-        List<Resource> orderedBundles = new LinkedList<>();
+        List<FeatureResource> orderedResources = new LinkedList<>();
         try {
-            for (Resource bundle : bundleMap.keySet()) {
-                if (orderedBundles.contains(bundle)) {
+            for (FeatureResource resource : resourceMap.keySet()) {
+                if (orderedResources.contains(resource)) {
                     // Already handled
                     continue;
                 }
-                Map<Resource, List<Wire>> deps = resolver.resolve(new ResolveContextImpl(bundle, availableBundles));
+                Map<Resource, List<Wire>> deps = resolver.resolve(new ResolveContextImpl(resource, idVerMap.values()));
 
                 for (Map.Entry<Resource, List<Wire>> entry : deps.entrySet()) {
-                    Resource curBundle = entry.getKey();
+                    if (resource.equals(entry.getKey()))
+                        continue;
 
-                    if (!bundleMap.containsKey(curBundle)) {
-                        // This is some synthesized bundle. Ignoring.
+                    Resource depResource = entry.getKey();
+                    FeatureResource curResource = getFeatureResource(depResource, idVerMap);
+                    if (curResource == null)
                         continue;
-                    }
 
-                    if (!orderedBundles.contains(curBundle)) {
-                        orderedBundles.add(curBundle);
+                    if (!orderedResources.contains(curResource)) {
+                        orderedResources.add(curResource);
                     }
 
                     for (Wire w : entry.getValue()) {
-                        Resource provBundle = w.getProvider();
-                        int curBundleIdx = orderedBundles.indexOf(curBundle);
-                        int newBundleIdx = orderedBundles.indexOf(provBundle);
+                        FeatureResource provBundle = getFeatureResource(w.getProvider(), idVerMap);
+                        if (provBundle == null)
+                            continue;
+
+                        int curBundleIdx = orderedResources.indexOf(curResource);
+                        int newBundleIdx = orderedResources.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);
+                                orderedResources.remove(provBundle);
                             } else {
                                 // If the providing bundle is already before the current bundle, then no need to change anything
                                 continue;
                             }
                         }
-                        orderedBundles.add(curBundleIdx, provBundle);
+                        orderedResources.add(curBundleIdx, provBundle);
+                    }
+                }
+
+                // All of the dependencies of the resource have been added, now add the resource itself
+                if (!orderedResources.contains(resource)) {
+                    Feature associatedFeature = resource.getFeature();
+                    if (resource.equals(featureMap.get(associatedFeature))) {
+                        // The resource is a feature resource, don't add this one by itself.
+                    }
+                    else {
+                        orderedResources.add(resource);
                     }
                 }
             }
@@ -168,35 +195,62 @@ public void close() throws Exception {
         }
 
         // 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);
+        for (int i=0; i<orderedResources.size(); i++) {
+            Resource r = orderedResources.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
+                int idx = getBundleIndex(orderedResources, 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);
+                    FeatureResource frag = orderedResources.remove(i);
+                    orderedResources.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);
-                }
+        // Add the features at the appropriate place to the ordered resources list
+        for (int i=0; i<orderedResources.size(); i++) {
+            FeatureResource r = orderedResources.get(i);
+            FeatureResource associatedFeature = featureMap.get(r.getFeature());
+            if (associatedFeature == null)
+                continue;
+
+            int idx = orderedResources.indexOf(associatedFeature);
+            if (idx > i) {
+                orderedResources.remove(idx);
+                orderedResources.add(i, associatedFeature);
+            } else if (idx == -1) {
+                orderedResources.add(i, associatedFeature);
             }
         }
-        return orderedFeatures;
+
+        // If the framework shows up as a dependency, remove it as it's always there
+        orderedResources.remove(frameworkResource);
+
+        return orderedResources;
+    }
+
+    private FeatureResource getFeatureResource(Resource res, Map<String, FeatureResource> idVerMap) {
+        if (res instanceof FeatureResource)
+            return (FeatureResource) res;
+
+        // Obtain the identity from the resource and look up in the resource
+        List<Capability> caps = res.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE);
+        if (caps.size() == 0) {
+            return null;
+        }
+        Capability cap = caps.get(0);
+        Map<String, Object> attrs = cap.getAttributes();
+        Object id = attrs.get(IdentityNamespace.IDENTITY_NAMESPACE);
+        Object ver = attrs.get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+        String idVer = "" + id + ":" + ver;
+        return idVerMap.get(idVer);
     }
 
-    private static int getBundleIndex(List<Resource> bundles, String bundleSymbolicName) {
+    private static int getBundleIndex(List<FeatureResource> bundles, String bundleSymbolicName) {
         for (int i=0; i<bundles.size(); i++) {
             Resource b = bundles.get(i);
             if (bundleSymbolicName.equals(getBundleSymbolicName(b))) {
diff --git a/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/impl/AbstractResourceImpl.java b/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/impl/AbstractResourceImpl.java
new file mode 100644
index 0000000..3cc1ca1
--- /dev/null
+++ b/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/impl/AbstractResourceImpl.java
@@ -0,0 +1,50 @@
+/*
+ * 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 org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+abstract class AbstractResourceImpl {
+    public List<Requirement> getRequirements(String namespace, Map<String, List<Requirement>> requirements) {
+        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;
+    }
+
+    public List<Capability> getCapabilities(String namespace, Map<String, List<Capability>> capabilities) {
+        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;
+    }
+}
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
index 16cc812..5053a1d 100644
--- 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
@@ -16,14 +16,9 @@
  */
 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.Artifact;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.FeatureResource;
 import org.apache.sling.feature.OSGiCapability;
 import org.apache.sling.feature.OSGiRequirement;
 import org.apache.sling.feature.analyser.BundleDescriptor;
@@ -32,25 +27,38 @@
 import org.osgi.framework.VersionRange;
 import org.osgi.framework.namespace.BundleNamespace;
 import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
+import org.osgi.framework.namespace.IdentityNamespace;
 import org.osgi.framework.namespace.PackageNamespace;
 import org.osgi.resource.Capability;
 import org.osgi.resource.Requirement;
-import org.osgi.resource.Resource;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Implementation of the OSGi Resource interface.
  */
-public class BundleResourceImpl implements Resource {
-    final String hint;
+public class BundleResourceImpl extends AbstractResourceImpl implements FeatureResource {
+    final Artifact artifact;
+    final String bsn;
+    final Version version;
     final Map<String, List<Capability>> capabilities;
     final Map<String, List<Requirement>> requirements;
+    final Feature feature;
 
     /**
      * Create a resource based on a BundleDescriptor.
      * @param bd The BundleDescriptor to represent.
      */
-    public BundleResourceImpl(BundleDescriptor bd) {
-        hint = bd.getBundleSymbolicName() + " " + bd.getBundleVersion();
+    public BundleResourceImpl(BundleDescriptor bd, Feature feat) {
+        artifact = bd.getArtifact();
+        bsn = bd.getBundleSymbolicName();
+        version = bd.getArtifact().getId().getOSGiVersion();
+        feature = feat;
+
         Map<String, List<Capability>> caps = new HashMap<>();
         for (Capability c : bd.getCapabilities()) {
             List<Capability> l = caps.get(c.getNamespace());
@@ -67,16 +75,24 @@ public BundleResourceImpl(BundleDescriptor bd) {
             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()));
+            attrs.put(PackageNamespace.CAPABILITY_BUNDLE_SYMBOLICNAME_ATTRIBUTE, bsn);
+            attrs.put(PackageNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE, version);
             pkgCaps.add(new OSGiCapability(this, PackageNamespace.PACKAGE_NAMESPACE, attrs, Collections.emptyMap()));
         }
         caps.put(PackageNamespace.PACKAGE_NAMESPACE, Collections.unmodifiableList(pkgCaps));
 
+        // Add the identity capability
+        Map<String, Object> idattrs = new HashMap<>();
+        idattrs.put(IdentityNamespace.IDENTITY_NAMESPACE, bsn);
+        idattrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, IdentityNamespace.TYPE_BUNDLE);
+        idattrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, version);
+        OSGiCapability idCap = new OSGiCapability(this, IdentityNamespace.IDENTITY_NAMESPACE, idattrs, Collections.emptyMap());
+        caps.put(IdentityNamespace.IDENTITY_NAMESPACE, Collections.singletonList(idCap));
+
         // 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()));
+        battrs.put(BundleNamespace.BUNDLE_NAMESPACE, bsn);
+        battrs.put(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE, version);
         OSGiCapability bundleCap = new OSGiCapability(this, BundleNamespace.BUNDLE_NAMESPACE, battrs, Collections.emptyMap());
         caps.put(BundleNamespace.BUNDLE_NAMESPACE, Collections.singletonList(bundleCap));
         capabilities = Collections.unmodifiableMap(caps);
@@ -117,48 +133,59 @@ public BundleResourceImpl(BundleDescriptor bd) {
         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);
+    public BundleResourceImpl(String sn, String ver, Artifact art, Feature feat, Map<String, List<Capability>> caps, Map<String, List<Requirement>> reqs) {
+        this(sn, new Version(ver), art, feat, caps, reqs);
+    }
+
+    public BundleResourceImpl(String sn, Version ver, Artifact art, Feature feat, Map<String, List<Capability>> caps, Map<String, List<Requirement>> reqs) {
+        artifact = art;
+        bsn = sn;
+        version = ver;
+        feature = feat;
         capabilities = caps;
         requirements = reqs;
     }
 
     @Override
-    public List<Capability> getCapabilities(String namespace) {
-        if (namespace == null) {
-            return capabilities.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
-        }
+    public Artifact getArtifact() {
+        return artifact;
+    }
+
+    @Override
+    public String getId() {
+        return bsn;
+    }
+
+    @Override
+    public Version getVersion() {
+        return version;
+    }
 
-        List<Capability> caps = capabilities.get(namespace);
-        if (caps == null)
-            return Collections.emptyList();
-        return caps;
+    @Override
+    public List<Capability> getCapabilities(String namespace) {
+        return super.getCapabilities(namespace, capabilities);
     }
 
     @Override
     public List<Requirement> getRequirements(String namespace) {
-        if (namespace == null) {
-            return requirements.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
-        }
+        return super.getRequirements(namespace, requirements);
+    }
 
-        List<Requirement> reqs = requirements.get(namespace);
-        if (reqs == null)
-            return Collections.emptyList();
-        return reqs;
+    @Override
+    public Feature getFeature() {
+        return feature;
     }
 
     @Override
     public int hashCode() {
         final int prime = 31;
         int result = 1;
+        result = prime * result + ((artifact == null) ? 0 : artifact.hashCode());
+        result = prime * result + ((bsn == null) ? 0 : bsn.hashCode());
         result = prime * result + ((capabilities == null) ? 0 : capabilities.hashCode());
+        result = prime * result + ((feature == null) ? 0 : feature.hashCode());
         result = prime * result + ((requirements == null) ? 0 : requirements.hashCode());
+        result = prime * result + ((version == null) ? 0 : version.hashCode());
         return result;
     }
 
@@ -171,21 +198,41 @@ public boolean equals(Object obj) {
         if (getClass() != obj.getClass())
             return false;
         BundleResourceImpl other = (BundleResourceImpl) obj;
+        if (artifact == null) {
+            if (other.artifact != null)
+                return false;
+        } else if (!artifact.equals(other.artifact))
+            return false;
+        if (bsn == null) {
+            if (other.bsn != null)
+                return false;
+        } else if (!bsn.equals(other.bsn))
+            return false;
         if (capabilities == null) {
             if (other.capabilities != null)
                 return false;
         } else if (!capabilities.equals(other.capabilities))
             return false;
+        if (feature == null) {
+            if (other.feature != null)
+                return false;
+        } else if (!feature.equals(other.feature))
+            return false;
         if (requirements == null) {
             if (other.requirements != null)
                 return false;
         } else if (!requirements.equals(other.requirements))
             return false;
+        if (version == null) {
+            if (other.version != null)
+                return false;
+        } else if (!version.equals(other.version))
+            return false;
         return true;
     }
 
     @Override
     public String toString() {
-        return "BundleResourceImpl [" + hint + "]";
+        return "BundleResourceImpl [bsn=" + bsn + ", version=" + version + "]";
     }
 }
diff --git a/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/impl/FeatureResourceImpl.java b/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/impl/FeatureResourceImpl.java
new file mode 100644
index 0000000..dc5c6b9
--- /dev/null
+++ b/featuremodel/feature-resolver/src/main/java/org/apache/sling/feature/resolver/impl/FeatureResourceImpl.java
@@ -0,0 +1,153 @@
+/*
+ * 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 org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.FeatureResource;
+import org.apache.sling.feature.OSGiCapability;
+import org.apache.sling.feature.OSGiRequirement;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class FeatureResourceImpl extends AbstractResourceImpl implements FeatureResource {
+    private final Artifact artifact;
+    private final Feature feature;
+    private final Map<String, List<Capability>> capabilities;
+    private final Map<String, List<Requirement>> requirements;
+
+    public FeatureResourceImpl(Feature f) {
+        artifact = new Artifact(f.getId());
+        feature = f;
+
+        capabilities = new HashMap<>();
+        for (Capability r : f.getCapabilities()) {
+            List<Capability> l = capabilities.get(r.getNamespace());
+            if (l == null) {
+                l = new ArrayList<>();
+                capabilities.put(r.getNamespace(), l);
+            }
+            l.add(new OSGiCapability(this, r));
+        }
+
+        // Add the identity capability
+        Map<String, Object> idattrs = new HashMap<>();
+        idattrs.put(IdentityNamespace.IDENTITY_NAMESPACE, getId());
+        idattrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, "sling.feature");
+        idattrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, getVersion());
+        idattrs.put(IdentityNamespace.CAPABILITY_DESCRIPTION_ATTRIBUTE, f.getDescription());
+        idattrs.put(IdentityNamespace.CAPABILITY_LICENSE_ATTRIBUTE, f.getLicense());
+        OSGiCapability idCap = new OSGiCapability(this, IdentityNamespace.IDENTITY_NAMESPACE, idattrs, Collections.emptyMap());
+        capabilities.put(IdentityNamespace.IDENTITY_NAMESPACE, Collections.singletonList(idCap));
+
+        requirements = new HashMap<>();
+        for (Requirement r : f.getRequirements()) {
+            List<Requirement> l = requirements.get(r.getNamespace());
+            if (l == null) {
+                l = new ArrayList<>();
+                requirements.put(r.getNamespace(), l);
+            }
+            l.add(new OSGiRequirement(this, r));
+        }
+    }
+
+    @Override
+    public String getId() {
+        return artifact.getId().getArtifactId();
+    }
+
+    @Override
+    public Version getVersion() {
+        return artifact.getId().getOSGiVersion();
+    }
+
+    @Override
+    public Artifact getArtifact() {
+        return artifact;
+    }
+
+    @Override
+    public Feature getFeature() {
+        return feature;
+    }
+
+    @Override
+    public List<Capability> getCapabilities(String namespace) {
+        return super.getCapabilities(namespace, capabilities);
+    }
+
+    @Override
+    public List<Requirement> getRequirements(String namespace) {
+        return super.getRequirements(namespace, requirements);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((artifact == null) ? 0 : artifact.hashCode());
+        result = prime * result + ((capabilities == null) ? 0 : capabilities.hashCode());
+        result = prime * result + ((feature == null) ? 0 : feature.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;
+        FeatureResourceImpl other = (FeatureResourceImpl) obj;
+        if (artifact == null) {
+            if (other.artifact != null)
+                return false;
+        } else if (!artifact.equals(other.artifact))
+            return false;
+        if (capabilities == null) {
+            if (other.capabilities != null)
+                return false;
+        } else if (!capabilities.equals(other.capabilities))
+            return false;
+        if (feature == null) {
+            if (other.feature != null)
+                return false;
+        } else if (!feature.equals(other.feature))
+            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 "FeatureResourceImpl [artifact=" + artifact + "]";
+    }
+}
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
index 91f4183..f76db32 100644
--- 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
@@ -16,12 +16,6 @@
  */
 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;
@@ -32,19 +26,25 @@
 import org.osgi.service.resolver.HostedCapability;
 import org.osgi.service.resolver.ResolveContext;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
 /**
  * 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;
+    private final Collection<? extends 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) {
+    public ResolveContextImpl(Resource mainResource, Collection<? extends Resource> available) {
         bundle = mainResource;
         availableResources = available;
     }
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
index 3f28644..290263e 100644
--- 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
@@ -16,30 +16,30 @@
  */
 package org.apache.sling.feature.resolver;
 
-import static org.junit.Assert.assertEquals;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.FeatureResource;
+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;
+import org.osgi.framework.namespace.IdentityNamespace;
 
 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;
+import static org.junit.Assert.assertEquals;
 
 public class FrameworkResolverTest {
     private Path tempDir;
@@ -52,7 +52,7 @@ public void setup() throws Exception {
     @After
     public void tearDown() throws Exception {
         // Delete the temp dir again
-        Files.walk(tempDir, FileVisitOption.FOLLOW_LINKS)
+        Files.walk(tempDir)
             .sorted(Comparator.reverseOrder())
             .map(Path::toFile)
             .forEach(File::delete);
@@ -67,23 +67,70 @@ 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()));
+                    fr.orderResources(Collections.emptyList()));
         }
     }
 
     @Test
-    public void testOrderFeatures() throws Exception {
+    public void testOrderResources() 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);
 
+        StringBuilder expectedBundles = new StringBuilder();
+        expectedBundles.append("slf4j.simple 1.7.25\n");
+        expectedBundles.append("slf4j.api 1.7.25\n");
+        expectedBundles.append("org.apache.sling.commons.logservice 1.0.6\n");
+        expectedBundles.append("org.apache.commons.io 2.6.0\n");
+        expectedBundles.append("org.apache.felix.http.servlet-api 1.1.2\n");
+
+        StringBuilder expectedResources = new StringBuilder();
+        expectedResources.append("feature3 1.0.0\n");
+        expectedResources.append("feature2 1.0.0\n");
+        expectedResources.append("feature1 1.0.0\n");
+
+        StringBuilder actualBundles = new StringBuilder();
+        StringBuilder actualResources = new StringBuilder();
+        try (FeatureResolver fr = new FrameworkResolver(am, getFrameworkProps())) {
+            for(FeatureResource ordered : fr.orderResources(Arrays.asList(f1, f2, f3))) {
+                if (IdentityNamespace.TYPE_BUNDLE.equals(
+                        ordered.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE).iterator().next().
+                        getAttributes().get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE))) {
+                    actualBundles.append(ordered.getId() + " " + ordered.getVersion() + "\n");
+                } else {
+                    actualResources.append(ordered.getId() + " " + ordered.getVersion() + "\n");
+                }
+            }
+        }
+        assertEquals(expectedBundles.toString(), actualBundles.toString());
+        assertEquals(expectedResources.toString(), actualResources.toString());
+    }
+
+    @Test
+    public void testOrderResourcesWithFeatureProvidingCapability() throws Exception {
+        ArtifactManager am = ArtifactManager.getArtifactManager(new ArtifactManagerConfig());
+
+        Feature f4 = readFeature("/feature4.json", am);
+        Feature f5 = readFeature("/feature5.json", am);
+        Feature f6 = readFeature("/feature6.json", am);
+
+        StringBuilder expectedResources = new StringBuilder();
+        expectedResources.append("feature5 1.0.0\n");
+        expectedResources.append("feature4 1.0.0\n");
+        expectedResources.append("org.apache.sling.commons.logservice 1.0.6\n");
+        expectedResources.append("org.apache.felix.http.servlet-api 1.1.2\n");
+        expectedResources.append("feature6 1.0.0\n");
+        expectedResources.append("org.apache.commons.io 2.6.0\n");
+
+        StringBuilder actualResources = new StringBuilder();
         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);
+            for(FeatureResource ordered : fr.orderResources(Arrays.asList(f4, f5, f6))) {
+                actualResources.append(ordered.getId() + " " + ordered.getVersion() + "\n");
+            }
         }
+        assertEquals(expectedResources.toString(), actualResources.toString());
     }
 
     private Feature readFeature(final String res,
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
index 08deb2c..68f572e 100644
--- 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
@@ -16,21 +16,10 @@
  */
 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.Feature;
+import org.apache.sling.feature.FeatureResource;
 import org.apache.sling.feature.OSGiCapability;
 import org.apache.sling.feature.OSGiRequirement;
 import org.apache.sling.feature.analyser.BundleDescriptor;
@@ -38,6 +27,7 @@
 import org.apache.sling.feature.analyser.impl.BundleDescriptorImpl;
 import org.apache.sling.feature.support.util.PackageInfo;
 import org.junit.Test;
+import org.mockito.Mockito;
 import org.osgi.framework.Version;
 import org.osgi.framework.namespace.BundleNamespace;
 import org.osgi.framework.namespace.PackageNamespace;
@@ -45,6 +35,20 @@
 import org.osgi.resource.Requirement;
 import org.osgi.resource.Resource;
 
+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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
 public class BundleResourceImplTest {
     @Test
     public void testResource() {
@@ -64,7 +68,9 @@ public void testResource() {
         Requirement r1 = new OSGiRequirement("ns.1",
                 Collections.emptyMap(), Collections.singletonMap("mydir", "myvalue"));
         List<Requirement> reqList = Collections.singletonList(r1);
-        Resource res = new BundleResourceImpl(caps,
+        Artifact art = Mockito.mock(Artifact.class);
+        Feature feat = Mockito.mock(Feature.class);
+        FeatureResource res = new BundleResourceImpl("a.b.c", "1.2.3", art, feat, caps,
                 Collections.singletonMap("ns.1", reqList));
 
         assertEquals(0, res.getCapabilities("nonexistent").size());
@@ -77,6 +83,11 @@ public void testResource() {
         assertTrue(mergedCaps.containsAll(capLst1));
         assertTrue(mergedCaps.containsAll(capLst2));
         assertEquals(reqList, res.getRequirements(null));
+
+        assertEquals("a.b.c", res.getId());
+        assertEquals(new Version("1.2.3"), res.getVersion());
+        assertSame(art, res.getArtifact());
+        assertSame(feat, res.getFeature());
     }
 
     @Test
@@ -96,7 +107,7 @@ public void testBundleResource() throws Exception {
         bd.getImportedPackages().add(im1);
         bd.getImportedPackages().add(im2);
 
-        Resource res = new BundleResourceImpl(bd);
+        Resource res = new BundleResourceImpl(bd, null);
         assertNotNull(
                 getCapAttribute(res, BundleNamespace.BUNDLE_NAMESPACE, BundleNamespace.BUNDLE_NAMESPACE));
         assertEquals(new Version("1.2.3"),
@@ -155,7 +166,7 @@ public void testBundleResourceGenericCapReq() throws Exception {
         Set<Requirement> reqs = new HashSet<>(Arrays.asList(req1, req2));
         BundleDescriptorImpl bd = new BundleDescriptorImpl(artifact, Collections.emptySet(), reqs, caps);
 
-        Resource res = new BundleResourceImpl(bd);
+        Resource res = new BundleResourceImpl(bd, null);
 
         assertEquals(caps, new HashSet<>(res.getCapabilities("org.example.cap1")));
         assertEquals(Collections.singleton(req1),
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
index b63d14b..5e0f6f6 100644
--- 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
@@ -16,11 +16,6 @@
  */
 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;
@@ -40,10 +35,15 @@
 import org.osgi.service.resolver.HostedCapability;
 import org.osgi.service.resolver.ResolveContext;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
 public class ResolveContextImplTest {
     @Test
     public void testMandatory() {
-        Resource mainRes = new BundleResourceImpl(Collections.emptyMap(), Collections.emptyMap());
+        Resource mainRes = new BundleResourceImpl("a", "1", null, null, Collections.emptyMap(), Collections.emptyMap());
         List<Resource> available = Arrays.asList();
         ResolveContext ctx = new ResolveContextImpl(mainRes, available);
 
@@ -57,7 +57,7 @@ public void testFindProviders() {
         Resource res3 = exportBundle("org.foo", "1.0.0.TESTING");
         Resource res4 = exportBundle("org.foo", "1.9");
 
-        Resource mainRes = new BundleResourceImpl(Collections.emptyMap(), Collections.emptyMap());
+        Resource mainRes = new BundleResourceImpl("b", "2", null, null, Collections.emptyMap(), Collections.emptyMap());
         List<Resource> available = Arrays.asList(res1, res2, res3, res4);
         ResolveContext ctx = new ResolveContextImpl(mainRes, available);
 
@@ -79,7 +79,7 @@ private Resource exportBundle(String pkg, String version) {
         attrs.put(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE, new Version(version));
         Capability cap = new OSGiCapability(PackageNamespace.PACKAGE_NAMESPACE,
                 attrs, Collections.emptyMap());
-        return new BundleResourceImpl(
+        return new BundleResourceImpl("c", "3", null, null,
                 Collections.singletonMap(PackageNamespace.PACKAGE_NAMESPACE,
                         Collections.singletonList(cap)),
                 Collections.emptyMap());
@@ -126,4 +126,4 @@ public void testEffectiveRequirement() {
                 Collections.singletonMap("effective", "active"));
         assertFalse(ctx.isEffective(req3));
     }
-}
\ 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
index 33a09a8..014d228 100644
--- a/featuremodel/feature-resolver/src/test/resources/feature3.json
+++ b/featuremodel/feature-resolver/src/test/resources/feature3.json
@@ -1,5 +1,15 @@
 {
     "id": "org.apache.sling.test.features/feature3/1.0.0",
     "bundles": 
-        ["org.slf4j/slf4j-simple/1.7.25"]
+        [
+            "org.slf4j/slf4j-simple/1.7.25",
+            {
+                "id": "org.apache.felix/org.apache.felix.http.servlet-api/1.1.2",
+                "start-order" : 10
+            },
+            {
+                "id": "commons-io/commons-io/2.6",
+                "start-order" : 5
+            }
+        ]
 }
\ No newline at end of file
diff --git a/featuremodel/feature-resolver/src/test/resources/feature4.json b/featuremodel/feature-resolver/src/test/resources/feature4.json
new file mode 100644
index 0000000..decb8b2
--- /dev/null
+++ b/featuremodel/feature-resolver/src/test/resources/feature4.json
@@ -0,0 +1,5 @@
+{
+    "id": "org.apache.sling.test.features/feature4/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/feature5.json b/featuremodel/feature-resolver/src/test/resources/feature5.json
new file mode 100644
index 0000000..49b4f50
--- /dev/null
+++ b/featuremodel/feature-resolver/src/test/resources/feature5.json
@@ -0,0 +1,13 @@
+{
+    "id": "org.apache.sling.test.features/feature5/1.0.0",
+    "capabilities": [
+        {
+            "namespace": "osgi.wiring.package",
+            "attributes": {
+                "osgi.wiring.package": "org.slf4j",
+                "version:Version": "1.7.2"
+            }
+        }
+    ],
+    "bundles": ["org.apache.felix/org.apache.felix.http.servlet-api/1.1.2"] 
+}
\ No newline at end of file
diff --git a/featuremodel/feature-resolver/src/test/resources/feature6.json b/featuremodel/feature-resolver/src/test/resources/feature6.json
new file mode 100644
index 0000000..f8f5e18
--- /dev/null
+++ b/featuremodel/feature-resolver/src/test/resources/feature6.json
@@ -0,0 +1,12 @@
+{
+    "id": "org.apache.sling.test.features/feature6/1.0.0",
+    "requirements": [
+        {
+            "namespace": "osgi.wiring.package",
+            "directives": {
+                "filter": "(&(osgi.wiring.package=org.slf4j)(version>=1.5.0)(!(version>=2.0.0)))"
+            }
+        }
+    ],
+    "bundles": ["commons-io/commons-io/2.6"]
+}
\ No newline at end of file
diff --git a/featuremodel/feature-support/src/test/resources/features/test.json b/featuremodel/feature-support/src/test/resources/features/test.json
index f4711c4..451b824 100644
--- a/featuremodel/feature-support/src/test/resources/features/test.json
+++ b/featuremodel/feature-support/src/test/resources/features/test.json
@@ -63,19 +63,19 @@
             {
               "id" : "org.apache.sling/oak-server/1.0.0",
               "hash" : "4632463464363646436",
-              "startOrder" : 1
+              "start-order" : 1
             },
             {
               "id" : "org.apache.sling/application-bundle/2.0.0",
-              "startOrder" : 1
+              "start-order" : 1
             },
             {
               "id" : "org.apache.sling/another-bundle/2.1.0",
-              "startOrder" : 1
+              "start-order" : 1
             },
             {
               "id" : "org.apache.sling/foo-xyz/1.2.3",
-              "startOrder" : 2
+              "start-order" : 2
             }
     ],
     "configurations" : {
diff --git a/featuremodel/feature/src/main/java/org/apache/sling/feature/FeatureResource.java b/featuremodel/feature/src/main/java/org/apache/sling/feature/FeatureResource.java
new file mode 100644
index 0000000..38b0b43
--- /dev/null
+++ b/featuremodel/feature/src/main/java/org/apache/sling/feature/FeatureResource.java
@@ -0,0 +1,50 @@
+/*
+ * 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 org.osgi.framework.Version;
+import org.osgi.resource.Resource;
+
+/**
+ * A Resource that is associated with an Maven Artifact and belongs to a Feature.
+ */
+public interface FeatureResource extends Resource {
+    /**
+     * Obtain the ID of the resource. If the resource is a bundle then this
+     * is the bundle symbolic name.
+     * @return The ID of the resource.
+     */
+    String getId();
+
+    /**
+     * Obtain the version of the resource.
+     * @return The version of the resource.
+     */
+    Version getVersion();
+
+    /**
+     * Obtain the associated (Maven) Artifact.
+     * @return The artifact for this Resource.
+     */
+    Artifact getArtifact();
+
+    /**
+     * Obtain the feature that contains this resource.
+     * @return The feature that contains the resource.
+     */
+    Feature getFeature();
+}
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 49c17b8..2a9698a 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
@@ -16,12 +16,14 @@
  */
 package org.apache.sling.feature.process;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import org.apache.sling.feature.Application;
+import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.FeatureResource;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Build an application based on features.
@@ -89,12 +91,11 @@ public static Application assemble(
             app = new Application();
         }
 
-        // Created sorted feature list
         // Remove duplicate features by selecting the one with the highest version
-        List<Feature> sortedFeatureList = new ArrayList<>();
+        final List<Feature> featureList = new ArrayList<>();
         for(final Feature f : features) {
             Feature found = null;
-            for(final Feature s : sortedFeatureList) {
+            for(final Feature s : featureList) {
                 if ( s.getId().isSame(f.getId()) ) {
                     found = s;
                     break;
@@ -108,21 +109,29 @@ public static Application assemble(
                     add = false;
                 } else {
                     // remove lower version, higher version will be added
-                    app.getFeatureIds().remove(found.getId());
-                    sortedFeatureList.remove(found);
+                    featureList.remove(found);
                 }
             }
             if ( add ) {
-                app.getFeatureIds().add(f.getId());
-                sortedFeatureList.add(f);
+                featureList.add(f);
             }
         }
 
         // order by dependency chain
-        sortedFeatureList = resolver.orderFeatures(sortedFeatureList);
+        final List<FeatureResource> sortedResources = resolver.orderResources(featureList);
+
+        final List<Feature> sortedFeatures = new ArrayList<>();
+        for (final FeatureResource fr : sortedResources) {
+            Feature f = fr.getFeature();
+            if (!sortedFeatures.contains(f)) {
+                sortedFeatures.add(f);
+            }
+        }
 
         // assemble
-        for(final Feature f : sortedFeatureList) {
+        int featureStartOrder = 5; // begin with start order a little higher than 0
+        for(final Feature f : sortedFeatures) {
+            app.getFeatureIds().add(f.getId());
             final Feature assembled = FeatureBuilder.assemble(f, context.clone(new FeatureProvider() {
 
                 @Override
@@ -136,6 +145,16 @@ public Feature provide(final ArtifactId id) {
                 }
             }));
 
+            int globalStartOrder = featureStartOrder;
+            for (Artifact a : assembled.getBundles()) {
+                int so = a.getStartOrder() + featureStartOrder;
+                if (so > globalStartOrder)
+                    globalStartOrder = so;
+                a.setStartOrder(so);
+            }
+            // Next feature will have a higher start order than the previous
+            featureStartOrder = globalStartOrder + 1;
+
             merge(app, assembled);
         }
 
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
index b3eaa35..5fbba7c 100644
--- 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
@@ -19,20 +19,21 @@
 import java.util.List;
 
 import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.FeatureResource;
 
 /**
  * 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.
+     * Order the resources in list of features by their dependency chain.
+     * Each feature and its components are resolved. Then all the resources
+     * in the feature are ordered so that each resource is placed before
+     * the requiring feature/resources in the result.
      *
      * @param features
      *            The features to order.
-     * @return The ordered features.
+     * @return The ordered resources from the features.
      */
-    List<Feature> orderFeatures(List<Feature> features);
+    List<FeatureResource> orderResources(List<Feature> features);
 }


 

----------------------------------------------------------------
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:
users@infra.apache.org


With regards,
Apache Git Services