Repository: karaf
Updated Branches:
  refs/heads/master b46bd22bc -> cde64ae2a


[KARAF-2988] Add support for prerequisites on features


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

Branch: refs/heads/master
Commit: cde64ae2ab110c4659bc23255c7c2dc1d3eb03f0
Parents: c1a6ca7
Author: Guillaume Nodet <gno...@gmail.com>
Authored: Mon May 19 21:14:50 2014 +0200
Committer: Guillaume Nodet <gno...@gmail.com>
Committed: Mon May 19 21:16:05 2014 +0200

----------------------------------------------------------------------
 assemblies/apache-karaf/pom.xml                 |   1 +
 .../enterprise/src/main/feature/feature.xml     |   2 +
 assemblies/features/framework/pom.xml           |  23 ----
 .../framework/src/main/feature/feature.xml      |   1 -
 .../etc/org.apache.karaf.features.repos.cfg     |   1 +
 .../standard/src/main/feature/feature.xml       |   9 +-
 .../org/apache/karaf/features/Dependency.java   |   2 +
 .../features/internal/model/Dependency.java     |  23 +++-
 .../karaf/features/internal/model/Feature.java  |  13 +-
 .../features/internal/region/Subsystem.java     |  76 ++++++++----
 .../internal/region/SubsystemResolver.java      |  38 +++---
 .../features/internal/service/Deployer.java     |  64 +++++++++-
 .../internal/service/FeaturesServiceImpl.java   |  19 ++-
 .../karaf/features/karaf-features-1.3.0.xsd     |   2 +
 .../apache/karaf/features/RepositoryTest.java   |   4 +-
 .../features/internal/region/SubsystemTest.java |  42 +++----
 .../features/internal/service/DeployerTest.java | 118 +++++++++++++++++++
 .../features/internal/service/data2/a100.mf     |   5 +
 .../features/internal/service/data2/b100.mf     |   5 +
 .../internal/service/data2/features.xml         |  30 +++++
 20 files changed, 373 insertions(+), 105 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/assemblies/apache-karaf/pom.xml
----------------------------------------------------------------------
diff --git a/assemblies/apache-karaf/pom.xml b/assemblies/apache-karaf/pom.xml
index a799b0a..6b721ed 100644
--- a/assemblies/apache-karaf/pom.xml
+++ b/assemblies/apache-karaf/pom.xml
@@ -160,6 +160,7 @@
                         <feature>wrapper</feature>
                     </installedFeatures>
                     <bootFeatures>
+                        <feature>wrap</feature>
                         <feature>aries-blueprint</feature>
                         <feature>shell</feature>
                         <feature>shell-compat</feature>

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/assemblies/features/enterprise/src/main/feature/feature.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/enterprise/src/main/feature/feature.xml 
b/assemblies/features/enterprise/src/main/feature/feature.xml
index ad2ecf6..e38335c 100644
--- a/assemblies/features/enterprise/src/main/feature/feature.xml
+++ b/assemblies/features/enterprise/src/main/feature/feature.xml
@@ -111,6 +111,7 @@
 
     <feature name="hibernate" description="Hibernate 4.2.x JPA persistence 
engine support" version="${hibernate42.version}">
         <details>Enable Hibernate 4.2.x as persistence engine.</details>
+        <feature prerequisite="true">wrap</feature>
         <feature>transaction</feature>
         <feature>http</feature>
         <bundle 
dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.antlr/${antlr.bundle.version}</bundle>
@@ -137,6 +138,7 @@
 
     <feature name="hibernate" description="Hibernate 4.3.x JPA persistence 
engine support" version="${hibernate43.version}">
         <details>Enable Hibernate 4.3.x as persistence engine.</details>
+        <feature prerequisite="true">wrap</feature>
         <feature>transaction</feature>
         <feature>http</feature>
         <bundle dependency="true" 
start-level="30">mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0.Final</bundle>

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/assemblies/features/framework/pom.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/framework/pom.xml 
b/assemblies/features/framework/pom.xml
index 51dc83a..3e8ba86 100644
--- a/assemblies/features/framework/pom.xml
+++ b/assemblies/features/framework/pom.xml
@@ -139,29 +139,6 @@
                 </exclusion>
             </exclusions>
         </dependency>
-        <dependency>
-            <groupId>org.ops4j.pax.url</groupId>
-            <artifactId>pax-url-wrap</artifactId>
-            <classifier>uber</classifier>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.slf4j</groupId>
-                    <artifactId>slf4j-api</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.ops4j.base</groupId>
-                    <artifactId>ops4j-base-net</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.ops4j.pax.swissbox</groupId>
-                    <artifactId>pax-swissbox-bnd</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.ops4j.pax.url</groupId>
-                    <artifactId>pax-url-commons</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
 
         <dependency>
             <groupId>org.apache.felix</groupId>

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/assemblies/features/framework/src/main/feature/feature.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/framework/src/main/feature/feature.xml 
b/assemblies/features/framework/src/main/feature/feature.xml
index 51b97e5..3b79d62 100644
--- a/assemblies/features/framework/src/main/feature/feature.xml
+++ b/assemblies/features/framework/src/main/feature/feature.xml
@@ -24,7 +24,6 @@
     <feature version="${project.version}" description="Karaf core feature" 
name="framework">
         <!-- mvn: and wrap: url handlers -->
         <bundle start="true" 
start-level="5">mvn:org.ops4j.pax.url/pax-url-aether/${pax.url.version}</bundle>
-        <bundle start="true" 
start-level="5">mvn:org.ops4j.pax.url/pax-url-wrap/${pax.url.version}/jar/uber</bundle>
         <!-- logging -->
         <bundle start="true" 
start-level="8">mvn:org.ops4j.pax.logging/pax-logging-api/${pax.logging.version}</bundle>
         <bundle start="true" 
start-level="8">mvn:org.ops4j.pax.logging/pax-logging-service/${pax.logging.version}</bundle>

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/assemblies/features/framework/src/main/resources/resources/etc/org.apache.karaf.features.repos.cfg
----------------------------------------------------------------------
diff --git 
a/assemblies/features/framework/src/main/resources/resources/etc/org.apache.karaf.features.repos.cfg
 
b/assemblies/features/framework/src/main/resources/resources/etc/org.apache.karaf.features.repos.cfg
index 7574897..df7ea32 100644
--- 
a/assemblies/features/framework/src/main/resources/resources/etc/org.apache.karaf.features.repos.cfg
+++ 
b/assemblies/features/framework/src/main/resources/resources/etc/org.apache.karaf.features.repos.cfg
@@ -22,6 +22,7 @@
 # It could be directly installed using feature:repo-add command
 #
 
+enterprise   = mvn:org.apache.karaf.features/enterprise/${version}/xml/features
 cellar       = 
mvn:org.apache.karaf.cellar/apache-karaf-cellar/${version}/xml/features
 camel        = mvn:org.apache.camel.karaf/apache-camel/${version}/xml/features
 camel-extras = 
mvn:org.apache-extras.camel-extra.karaf/camel-extra/${version}/xml/features

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/assemblies/features/standard/src/main/feature/feature.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/standard/src/main/feature/feature.xml 
b/assemblies/features/standard/src/main/feature/feature.xml
index c02dea0..9f68409 100644
--- a/assemblies/features/standard/src/main/feature/feature.xml
+++ b/assemblies/features/standard/src/main/feature/feature.xml
@@ -84,9 +84,12 @@
     </feature>
 
     <feature name="deployer" description="Karaf Deployer" 
version="${project.version}">
-        <bundle start="true" 
start-level="24">mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.wrap/${project.version}</bundle>
         <bundle start="true" 
start-level="26">mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.features/${project.version}</bundle>
         <conditional>
+            <condition>wrap</condition>
+            <bundle start="true" 
start-level="24">mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.wrap/${project.version}</bundle>
+        </conditional>
+        <conditional>
             
<condition>req:osgi.extender;filter:="(&amp;(osgi.extender=osgi.blueprint)(version>=1.0))"</condition>
             <bundle start="true" 
start-level="24">mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.blueprint/${project.version}</bundle>
         </conditional>
@@ -295,4 +298,8 @@
         
<bundle>mvn:org.apache.aries.blueprint/org.apache.aries.blueprint.webosgi/${aries.blueprint.web.version}</bundle>
     </feature>
 
+    <feature name="wrap" description="Wrap URL handler">
+        <bundle start="true" 
start-level="5">mvn:org.ops4j.pax.url/pax-url-wrap/${pax.url.version}/jar/uber</bundle>
+    </feature>
+
 </features>

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/main/java/org/apache/karaf/features/Dependency.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/Dependency.java 
b/features/core/src/main/java/org/apache/karaf/features/Dependency.java
index 5e019c7..421df19 100644
--- a/features/core/src/main/java/org/apache/karaf/features/Dependency.java
+++ b/features/core/src/main/java/org/apache/karaf/features/Dependency.java
@@ -23,4 +23,6 @@ public interface Dependency {
 
     String getVersion();
 
+    boolean isPrerequisite();
+
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java
 
b/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java
index b099f28..2a4d8dd 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java
@@ -42,13 +42,15 @@ import javax.xml.bind.annotation.XmlValue;
  * </pre>
  */
 @XmlAccessorType(XmlAccessType.FIELD)
-@XmlType(name = "dependency", propOrder = {"value"})
+@XmlType(name = "dependency", propOrder = {"name"})
 public class Dependency implements org.apache.karaf.features.Dependency {
 
     @XmlValue
-    protected String value;
+    protected String name;
     @XmlAttribute
     protected String version;
+    @XmlAttribute
+    protected boolean prerequisite;
 
     /**
      * Feature name should be non empty string.
@@ -57,7 +59,7 @@ public class Dependency implements 
org.apache.karaf.features.Dependency {
      * {@link String }
      */
     public String getName() {
-        return value;
+        return name;
     }
 
     /**
@@ -67,7 +69,7 @@ public class Dependency implements 
org.apache.karaf.features.Dependency {
      *              {@link String }
      */
     public void setName(String value) {
-        this.value = value;
+        this.name = value;
     }
 
     /**
@@ -78,7 +80,7 @@ public class Dependency implements 
org.apache.karaf.features.Dependency {
      */
     public String getVersion() {
         if (version == null) {
-            return "0.0.0";
+            return Feature.DEFAULT_VERSION;
         } else {
             return version;
         }
@@ -94,8 +96,17 @@ public class Dependency implements 
org.apache.karaf.features.Dependency {
         this.version = value;
     }
 
+    @Override
+    public boolean isPrerequisite() {
+        return prerequisite;
+    }
+
+    public void setPrerequisite(boolean prerequisite) {
+        this.prerequisite = prerequisite;
+    }
+
     public String toString() {
-        return getName() + Feature.SPLIT_FOR_NAME_AND_VERSION + getVersion();
+        return getName() + Feature.VERSION_SEPARATOR + getVersion();
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
 
b/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
index 6cf3820..4d3eb12 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
@@ -30,7 +30,6 @@ import javax.xml.bind.annotation.XmlTransient;
 import javax.xml.bind.annotation.XmlType;
 
 import org.apache.felix.utils.version.VersionCleaner;
-import org.apache.felix.utils.version.VersionTable;
 
 
 /**
@@ -78,7 +77,7 @@ import org.apache.felix.utils.version.VersionTable;
         })
 public class Feature extends Content implements 
org.apache.karaf.features.Feature {
 
-    public static final String SPLIT_FOR_NAME_AND_VERSION = "/";
+    public static final String VERSION_SEPARATOR = "/";
     public static final String DEFAULT_VERSION = "0.0.0";
 
 
@@ -114,10 +113,10 @@ public class Feature extends Content implements 
org.apache.karaf.features.Featur
 
 
     public static org.apache.karaf.features.Feature valueOf(String str) {
-        if (str.contains(SPLIT_FOR_NAME_AND_VERSION)) {
-            String strName = str.substring(0, 
str.indexOf(SPLIT_FOR_NAME_AND_VERSION));
-            String strVersion = 
str.substring(str.indexOf(SPLIT_FOR_NAME_AND_VERSION)
-                    + SPLIT_FOR_NAME_AND_VERSION.length(), str.length());
+        if (str.contains(VERSION_SEPARATOR)) {
+            String strName = str.substring(0, str.indexOf(VERSION_SEPARATOR));
+            String strVersion = str.substring(str.indexOf(VERSION_SEPARATOR)
+                    + VERSION_SEPARATOR.length(), str.length());
             return new Feature(strName, strVersion);
         } else {
             return new Feature(str);
@@ -128,7 +127,7 @@ public class Feature extends Content implements 
org.apache.karaf.features.Featur
 
 
     public String getId() {
-        return getName() + SPLIT_FOR_NAME_AND_VERSION + getVersion();
+        return getName() + VERSION_SEPARATOR + getVersion();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java
 
b/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java
index bfe170e..f64bc0f 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -85,11 +86,11 @@ public class Subsystem extends ResourceImpl {
     private final boolean acceptDependencies;
     private final Subsystem parent;
     private final Feature feature;
-    private final List<Subsystem> children = new ArrayList<Subsystem>();
+    private final List<Subsystem> children = new ArrayList<>();
     private final Map<String, Set<String>> importPolicy;
     private final Map<String, Set<String>> exportPolicy;
-    private final List<Resource> installable = new ArrayList<Resource>();
-    private final Map<String, DependencyInfo> dependencies = new 
HashMap<String, DependencyInfo>();
+    private final List<Resource> installable = new ArrayList<>();
+    private final Map<String, DependencyInfo> dependencies = new HashMap<>();
 
     public Subsystem(String name) {
         super(name, TYPE_SUBSYSTEM, Version.emptyVersion);
@@ -117,8 +118,8 @@ public class Subsystem extends ResourceImpl {
             this.exportPolicy = SHARE_ALL_POLICY;
         }
 
-        Map<String, String> dirs = new HashMap<String, String>();
-        Map<String, Object> attrs = new HashMap<String, Object>();
+        Map<String, String> dirs = new HashMap<>();
+        Map<String, Object> attrs = new HashMap<>();
         attrs.put(IDENTITY_NAMESPACE, feature.getName());
         attrs.put(CAPABILITY_TYPE_ATTRIBUTE, TYPE_FEATURE);
         attrs.put(CAPABILITY_VERSION_ATTRIBUTE, new 
VersionRange(VersionTable.getVersion(feature.getVersion()), true));
@@ -205,7 +206,7 @@ public class Subsystem extends ResourceImpl {
     }
 
     public Map<String, BundleInfo> getBundleInfos() {
-        Map<String, BundleInfo> infos = new HashMap<String, BundleInfo>();
+        Map<String, BundleInfo> infos = new HashMap<>();
         for (DependencyInfo di : dependencies.values()) {
             infos.put(di.getLocation(), di);
         }
@@ -213,14 +214,20 @@ public class Subsystem extends ResourceImpl {
     }
 
     @SuppressWarnings("InfiniteLoopStatement")
-    public void preResolve(Collection<Feature> features,
-                           DownloadManager manager,
-                           Set<String> overrides,
-                           String featureResolutionRange) throws Exception {
+    public void build(Collection<Feature> features) throws Exception {
         for (Subsystem child : children) {
-            child.preResolve(features, manager, overrides, 
featureResolutionRange);
+            child.build(features);
         }
-        List<Requirement> processed = new ArrayList<Requirement>();
+        if (feature != null) {
+            for (Dependency dep : feature.getDependencies()) {
+                Subsystem ss = this;
+                while (!ss.isAcceptDependencies()) {
+                    ss = ss.getParent();
+                }
+                ss.requireFeature(dep.getName(), dep.getVersion());
+            }
+        }
+        List<Requirement> processed = new ArrayList<>();
         while (true) {
             List<Requirement> requirements = 
getRequirements(IDENTITY_NAMESPACE);
             requirements.removeAll(processed);
@@ -240,7 +247,7 @@ public class Subsystem extends ResourceImpl {
                                 Subsystem fs = getChild(ssName);
                                 if (fs == null) {
                                     fs = new Subsystem(ssName, feature, this);
-                                    fs.preResolve(features, manager, 
overrides, featureResolutionRange);
+                                    fs.build(features);
                                     installable.add(fs);
                                     children.add(fs);
                                 }
@@ -251,10 +258,38 @@ public class Subsystem extends ResourceImpl {
                 processed.add(requirement);
             }
         }
+    }
+
+    public Set<String> collectPrerequisites() {
+        Set<String> prereqs = new HashSet<>();
+        doCollectPrerequisites(prereqs);
+        return prereqs;
+    }
+
+    private void doCollectPrerequisites(Set<String> prereqs) {
+        for (Subsystem child : children) {
+            child.doCollectPrerequisites(prereqs);
+        }
+        if (feature != null) {
+            for (Dependency dep : feature.getDependencies()) {
+                if (dep.isPrerequisite()) {
+                    prereqs.add(dep.toString());
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("InfiniteLoopStatement")
+    public void downloadBundles(DownloadManager manager,
+                                Set<String> overrides,
+                                String featureResolutionRange) throws 
Exception {
+        for (Subsystem child : children) {
+            child.downloadBundles(manager, overrides, featureResolutionRange);
+        }
         if (feature != null) {
-            final Map<String, ResourceImpl> bundles = new 
ConcurrentHashMap<String, ResourceImpl>();
+            final Map<String, ResourceImpl> bundles = new 
ConcurrentHashMap<>();
             final Downloader downloader = manager.createDownloader();
-            final Map<BundleInfo, Conditional> infos = new HashMap<BundleInfo, 
Conditional>();
+            final Map<BundleInfo, Conditional> infos = new HashMap<>();
             for (Conditional cond : feature.getConditional()) {
                 for (final BundleInfo bi : cond.getBundles()) {
                     infos.put(bi, cond);
@@ -286,15 +321,8 @@ public class Subsystem extends ResourceImpl {
             }
             downloader.await();
             Overrides.override(bundles, overrides);
-            for (Dependency dep : feature.getDependencies()) {
-                Subsystem ss = this;
-                while (!ss.isAcceptDependencies()) {
-                    ss = ss.getParent();
-                }
-                ss.requireFeature(dep.getName(), dep.getVersion());
-            }
             // Add conditionals
-            Map<Conditional, Resource> resConds = new HashMap<Conditional, 
Resource>();
+            Map<Conditional, Resource> resConds = new HashMap<>();
             for (Conditional cond : feature.getConditional()) {
                 FeatureResource resCond = FeatureResource.build(feature, cond, 
featureResolutionRange, bundles);
                 addIdentityRequirement(this, resCond, false);
@@ -382,7 +410,7 @@ public class Subsystem extends ResourceImpl {
     }
 
     Map<String, Set<String>> createPolicy(List<? extends ScopeFilter> filters) 
{
-        Map<String, Set<String>> policy = new HashMap<String, Set<String>>();
+        Map<String, Set<String>> policy = new HashMap<>();
         for (ScopeFilter filter : filters) {
             addToMapSet(policy, filter.getNamespace(), filter.getFilter());
         }

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
 
b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
index 2682e58..f8f7bca 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
@@ -32,7 +32,6 @@ import org.apache.karaf.features.Feature;
 import org.apache.karaf.features.internal.download.DownloadManager;
 import org.apache.karaf.features.internal.download.Downloader;
 import org.apache.karaf.features.internal.download.StreamProvider;
-import org.apache.karaf.features.internal.download.simple.SimpleDownloader;
 import org.apache.karaf.features.internal.resolver.CapabilityImpl;
 import org.apache.karaf.features.internal.resolver.CapabilitySet;
 import org.apache.karaf.features.internal.resolver.ResourceBuilder;
@@ -76,6 +75,7 @@ public class SubsystemResolver {
     private Map<Resource, List<Wire>> wiring;
 
     // Cached computed results
+    private ResourceImpl environmentResource;
     private Map<String, String> flatSubsystemsMap;
     private Map<String, Set<Resource>> bundlesPerRegions;
     private Map<Resource, String> bundles;
@@ -85,21 +85,14 @@ public class SubsystemResolver {
     private Map<String, Map<String, BundleInfo>> bundleInfos;
 
 
-    public SubsystemResolver() {
-        this(new SimpleDownloader());
-    }
-
     public SubsystemResolver(DownloadManager manager) {
         this.manager = manager;
     }
 
-    public Map<Resource, List<Wire>> resolve(
+    public void prepare(
             Collection<Feature> allFeatures,
             Map<String, Set<String>> features,
-            Map<String, Set<BundleRevision>> system,
-            Set<String> overrides,
-            String featureResolutionRange,
-            org.osgi.service.repository.Repository globalRepository
+            Map<String, Set<BundleRevision>> system
     ) throws Exception {
         // Build subsystems on the fly
         for (Map.Entry<String, Set<String>> entry : features.entrySet()) {
@@ -128,13 +121,13 @@ public class SubsystemResolver {
             }
         }
         if (root == null) {
-            return Collections.emptyMap();
+            return;
         }
+
         // Pre-resolve
-        root.preResolve(allFeatures, manager, overrides, 
featureResolutionRange);
+        root.build(allFeatures);
 
         // Add system resources
-        ResourceImpl environmentResource = null;
         BundleRevision sysBundleRev = null;
         boolean hasEeCap = false;
         for (Map.Entry<String, Set<BundleRevision>> entry : system.entrySet()) 
{
@@ -157,7 +150,7 @@ public class SubsystemResolver {
                     Map<String, String> headers = new 
DictionaryAsMap<>(res.getBundle().getHeaders());
                     Resource tmp = 
ResourceBuilder.build(res.getBundle().getLocation(), headers);
                     for (Capability cap : 
tmp.getCapabilities(ServiceNamespace.SERVICE_NAMESPACE)) {
-                        dummy.addCapability(new CapabilityImpl(dummy, 
cap.getNamespace(), cap.getDirectives() ,cap.getAttributes()));
+                        dummy.addCapability(new CapabilityImpl(dummy, 
cap.getNamespace(), cap.getDirectives(), cap.getAttributes()));
                     }
                     ss.addSystemResource(res);
                     for (Capability cap : res.getCapabilities(null)) {
@@ -177,6 +170,23 @@ public class SubsystemResolver {
             
environmentResource.addCapabilities(ResourceBuilder.parseCapability(environmentResource,
 provideCaps));
             root.addSystemResource(environmentResource);
         }
+    }
+
+    public Set<String> collectPrerequisites() throws Exception {
+        return root.collectPrerequisites();
+    }
+
+    public Map<Resource, List<Wire>> resolve(
+            Set<String> overrides,
+            String featureResolutionRange,
+            final org.osgi.service.repository.Repository globalRepository
+    ) throws Exception {
+        if (root == null) {
+            return Collections.emptyMap();
+        }
+
+        // Download bundles
+        root.downloadBundles(manager, overrides, featureResolutionRange);
 
         // Populate digraph and resolve
         digraph = new StandardRegionDigraph(null, null);

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
 
b/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
index 5a1cd02..d80fe3d 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
@@ -34,6 +34,7 @@ import java.util.TreeMap;
 import java.util.TreeSet;
 
 import org.apache.felix.utils.version.VersionRange;
+import org.apache.felix.utils.version.VersionTable;
 import org.apache.karaf.features.BundleInfo;
 import org.apache.karaf.features.Feature;
 import org.apache.karaf.features.FeatureEvent;
@@ -58,7 +59,6 @@ import org.osgi.framework.startlevel.BundleStartLevel;
 import org.osgi.framework.wiring.BundleRevision;
 import org.osgi.framework.wiring.BundleWire;
 import org.osgi.framework.wiring.BundleWiring;
-import org.osgi.resource.Capability;
 import org.osgi.resource.Resource;
 import org.osgi.resource.Wire;
 import org.osgi.service.repository.Repository;
@@ -118,6 +118,18 @@ public class Deployer {
         void replaceDigraph(Map<String, Map<String,Map<String,Set<String>>>> 
policies, Map<String, Set<Long>> bundles) throws BundleException, 
InvalidSyntaxException;
     }
 
+    public static class PartialDeploymentException extends Exception {
+        private final Set<String> missing;
+
+        public PartialDeploymentException(Set<String> missing) {
+            this.missing = missing;
+        }
+
+        public Set<String> getMissing() {
+            return missing;
+        }
+    }
+
     static class DeploymentState {
         State state;
         Bundle serviceBundle;
@@ -191,10 +203,56 @@ public class Deployer {
         // TODO: bundles
 
         SubsystemResolver resolver = new SubsystemResolver(manager);
-        resolver.resolve(
+        resolver.prepare(
                 dstate.features.values(),
                 request.requestedFeatures,
-                apply(unmanagedBundles, adapt(BundleRevision.class)),
+                apply(unmanagedBundles, adapt(BundleRevision.class))
+        );
+        Set<String> prereqs = resolver.collectPrerequisites();
+        if (!prereqs.isEmpty()) {
+            for (Iterator<String> iterator = prereqs.iterator(); 
iterator.hasNext(); ) {
+                String prereq = iterator.next();
+                String[] parts = prereq.split("/");
+                VersionRange range;
+                if (parts[1].equals("0.0.0")) {
+                    range = VersionRange.ANY_VERSION;
+                } else if (!parts[1].startsWith("[") && 
!parts[1].startsWith("(")) {
+                    range = new 
VersionRange(Macro.transform(request.featureResolutionRange, parts[1]));
+                } else {
+                    range = new VersionRange(parts[1]);
+                }
+                boolean found = false;
+                for (Set<String> featureSet : 
dstate.state.installedFeatures.values()) {
+                    for (String feature : featureSet) {
+                        String[] p = feature.split("/");
+                        found = parts[0].equals(p[0]) && 
range.contains(VersionTable.getVersion(p[1]));
+                        if (found) break;
+                    }
+                    if (found) break;
+                }
+                if (found) {
+                    iterator.remove();
+                }
+            }
+        }
+        if (!prereqs.isEmpty()) {
+            DeploymentRequest newRequest = new DeploymentRequest();
+            newRequest.bundleUpdateRange = request.bundleUpdateRange;
+            newRequest.featureResolutionRange = request.featureResolutionRange;
+            newRequest.globalRepository = request.globalRepository;
+            newRequest.options = request.options;
+            newRequest.overrides = request.overrides;
+            newRequest.requestedFeatures = 
copy(dstate.state.requestedFeatures);
+            for (String prereq : prereqs) {
+                addToMapSet(newRequest.requestedFeatures, ROOT_REGION, prereq);
+            }
+            newRequest.stateChanges = Collections.emptyMap();
+            newRequest.updateSnaphots = request.updateSnaphots;
+            deploy(dstate, newRequest);
+            throw new PartialDeploymentException(prereqs);
+        }
+
+        resolver.resolve(
                 request.overrides,
                 request.featureResolutionRange,
                 request.globalRepository);

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
 
b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
index f0f0677..03686aa 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
@@ -908,9 +908,22 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
                                   EnumSet<Option> options                      
          // installation options
     ) throws Exception {
 
-        Deployer.DeploymentState dstate = getDeploymentState(state);
-        Deployer.DeploymentRequest request = 
getDeploymentRequest(requestedFeatures, stateChanges, options);
-        new Deployer(new SimpleDownloader(), this).deploy(dstate, request);
+        Set<String> prereqs = new HashSet<>();
+        while (true) {
+            try {
+                Deployer.DeploymentState dstate = getDeploymentState(state);
+                Deployer.DeploymentRequest request = 
getDeploymentRequest(requestedFeatures, stateChanges, options);
+                new Deployer(new SimpleDownloader(), this).deploy(dstate, 
request);
+                break;
+            } catch (Deployer.PartialDeploymentException e) {
+                if (!prereqs.containsAll(e.getMissing())) {
+                    prereqs.addAll(e.getMissing());
+                    state = copyState();
+                } else {
+                    throw new Exception("Deployment aborted due to loop in 
missing prerequisites: " + e.getMissing());
+                }
+            }
+        }
     }
 
     public void print(String message, boolean verbose) {

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.3.0.xsd
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.3.0.xsd
 
b/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.3.0.xsd
index e28f05f..4d6e81d 100644
--- 
a/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.3.0.xsd
+++ 
b/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.3.0.xsd
@@ -165,6 +165,7 @@ Dependency of feature.
         <xs:simpleContent>
             <xs:extension base="tns:featureName">
                 <xs:attribute name="version" type="xs:string" default="0.0.0" 
/>
+                <xs:attribute name="prerequisite" type="xs:boolean" 
default="false"/>
             </xs:extension>
         </xs:simpleContent>
     </xs:complexType>
@@ -219,6 +220,7 @@ Additional requirements of this feature.
         </xs:annotation>
         <xs:simpleContent>
             <xs:extension base="xs:string">
+                <xs:attribute name="prerequisite" type="xs:boolean" 
default="false"/>
             </xs:extension>
         </xs:simpleContent>
     </xs:complexType>

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/test/java/org/apache/karaf/features/RepositoryTest.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/test/java/org/apache/karaf/features/RepositoryTest.java 
b/features/core/src/test/java/org/apache/karaf/features/RepositoryTest.java
index 164dc79..c8da238 100644
--- a/features/core/src/test/java/org/apache/karaf/features/RepositoryTest.java
+++ b/features/core/src/test/java/org/apache/karaf/features/RepositoryTest.java
@@ -56,7 +56,7 @@ public class RepositoryTest extends TestCase {
         assertEquals(0, features[1].getConfigurations().size());
         assertNotNull(features[1].getDependencies());
         assertEquals(1, features[1].getDependencies().size());
-        assertEquals("f1" + 
org.apache.karaf.features.internal.model.Feature.SPLIT_FOR_NAME_AND_VERSION + 
org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION, 
features[1].getDependencies().get(0).toString());
+        assertEquals("f1" + 
org.apache.karaf.features.internal.model.Feature.VERSION_SEPARATOR + 
org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION, 
features[1].getDependencies().get(0).toString());
         assertNotNull(features[1].getBundles());
         assertEquals(1, features[1].getBundles().size());
         assertEquals("b3", features[1].getBundles().get(0).getLocation());
@@ -98,7 +98,7 @@ public class RepositoryTest extends TestCase {
         assertEquals(0, features[1].getConfigurations().size());
         assertNotNull(features[1].getDependencies());
         assertEquals(1, features[1].getDependencies().size());
-        assertEquals("f1" + 
org.apache.karaf.features.internal.model.Feature.SPLIT_FOR_NAME_AND_VERSION + 
org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION, 
features[1].getDependencies().get(0).toString());
+        assertEquals("f1" + 
org.apache.karaf.features.internal.model.Feature.VERSION_SEPARATOR + 
org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION, 
features[1].getDependencies().get(0).toString());
         assertNotNull(features[1].getBundles());
         assertEquals(1, features[1].getBundles().size());
         assertEquals("b3", features[1].getBundles().get(0).getLocation());

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java
 
b/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java
index 42c66d7..e6b5b8f 100644
--- 
a/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java
+++ 
b/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java
@@ -54,10 +54,10 @@ public class SubsystemTest {
         addToMapSet(expected, "root/apps1", "b/1.0.0");
 
         SubsystemResolver resolver = new SubsystemResolver(new 
TestDownloadManager(getClass(), "data1"));
-        resolver.resolve(Arrays.asList(repo.getFeatures()),
+        resolver.prepare(Arrays.asList(repo.getFeatures()),
                          features,
-                         Collections.<String, Set<BundleRevision>>emptyMap(),
-                         Collections.<String>emptySet(),
+                         Collections.<String, Set<BundleRevision>>emptyMap());
+        resolver.resolve(Collections.<String>emptySet(),
                          FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
                          null);
 
@@ -85,10 +85,10 @@ public class SubsystemTest {
         addToMapSet(expected, "root/apps2#f1", "a/1.0.0");
 
         SubsystemResolver resolver = new SubsystemResolver(new 
TestDownloadManager(getClass(), "data2"));
-        resolver.resolve(Arrays.asList(repo.getFeatures()),
+        resolver.prepare(Arrays.asList(repo.getFeatures()),
                          features,
-                         Collections.<String, Set<BundleRevision>>emptyMap(),
-                         Collections.<String>emptySet(),
+                         Collections.<String, Set<BundleRevision>>emptyMap());
+        resolver.resolve(Collections.<String>emptySet(),
                          FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
                          null);
 
@@ -106,10 +106,10 @@ public class SubsystemTest {
         addToMapSet(expected, "root/apps1", "a/1.0.1");
 
         SubsystemResolver resolver = new SubsystemResolver(new 
TestDownloadManager(getClass(), "data3"));
-        resolver.resolve(Arrays.asList(repo.getFeatures()),
+        resolver.prepare(Arrays.asList(repo.getFeatures()),
                          features,
-                         Collections.<String, Set<BundleRevision>>emptyMap(),
-                         Collections.singleton("b"),
+                         Collections.<String, Set<BundleRevision>>emptyMap());
+        resolver.resolve(Collections.singleton("b"),
                          FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
                          null);
 
@@ -126,12 +126,12 @@ public class SubsystemTest {
         addToMapSet(expected, "root/apps1", "a/1.0.0");
 
         SubsystemResolver resolver = new SubsystemResolver(new 
TestDownloadManager(getClass(), "data4"));
-        resolver.resolve(Arrays.asList(repo.getFeatures()),
-                features,
-                Collections.<String, Set<BundleRevision>>emptyMap(),
-                Collections.<String>emptySet(),
-                FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
-                null);
+        resolver.prepare(Arrays.asList(repo.getFeatures()),
+                         features,
+                         Collections.<String, Set<BundleRevision>>emptyMap());
+        resolver.resolve(Collections.<String>emptySet(),
+                         FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
+                         null);
 
         verify(resolver, expected);
     }
@@ -148,12 +148,12 @@ public class SubsystemTest {
         addToMapSet(expected, "root/apps1", "b/1.0.0");
 
         SubsystemResolver resolver = new SubsystemResolver(new 
TestDownloadManager(getClass(), "data4"));
-        resolver.resolve(Arrays.asList(repo.getFeatures()),
-                features,
-                Collections.<String, Set<BundleRevision>>emptyMap(),
-                Collections.<String>emptySet(),
-                FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
-                null);
+        resolver.prepare(Arrays.asList(repo.getFeatures()),
+                         features,
+                         Collections.<String, Set<BundleRevision>>emptyMap());
+        resolver.resolve(Collections.<String>emptySet(),
+                         FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
+                         null);
 
         verify(resolver, expected);
     }

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
 
b/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
index 42c7c49..723df3b 100644
--- 
a/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
+++ 
b/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
@@ -44,6 +44,7 @@ import static org.apache.karaf.features.FeaturesService.*;
 import static org.apache.karaf.features.internal.util.MapUtils.addToMapSet;
 import static org.easymock.EasyMock.anyInt;
 import static org.easymock.EasyMock.anyObject;
+import static org.junit.Assert.fail;
 
 public class DeployerTest {
 
@@ -259,6 +260,123 @@ public class DeployerTest {
         EasyMock.verify(callback);
     }
 
+    @Test
+    public void testPrerequisite() throws Exception {
+
+        String dataDir = "data2";
+
+        TestDownloadManager manager = new TestDownloadManager(getClass(), 
dataDir);
+
+        RepositoryImpl repo = new 
RepositoryImpl(getClass().getResource(dataDir + "/features.xml").toURI());
+        repo.load(true);
+        Feature f1 = repo.getFeatures()[0];
+        Feature f2 = repo.getFeatures()[1];
+
+        Bundle serviceBundle1 = createTestBundle(1, Bundle.ACTIVE, dataDir, 
"a100");
+        Bundle serviceBundle2 = createTestBundle(2, Bundle.ACTIVE, dataDir, 
"b100");
+
+        Deployer.DeployCallback callback = 
EasyMock.createMock(Deployer.DeployCallback.class);
+        Deployer deployer = new Deployer(manager, callback);
+
+        callback.print(EasyMock.anyString(), EasyMock.anyBoolean());
+        EasyMock.expectLastCall().anyTimes();
+        callback.installBundle(EasyMock.eq(ROOT_REGION), EasyMock.eq("a100"), 
EasyMock.<InputStream>anyObject());
+        EasyMock.expectLastCall().andReturn(serviceBundle1);
+        callback.replaceDigraph(EasyMock.<Map<String, Map<String, Map<String, 
Set<String>>>>>anyObject(),
+                EasyMock.<Map<String, Set<Long>>>anyObject());
+        EasyMock.expectLastCall();
+        callback.saveState(EasyMock.<State>anyObject());
+        EasyMock.expectLastCall();
+        callback.installFeatureConfigs(f1);
+        EasyMock.expectLastCall();
+        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject());
+        EasyMock.expectLastCall();
+        callback.callListeners(EasyMock.<FeatureEvent>anyObject());
+        EasyMock.expectLastCall();
+
+        EasyMock.replay(callback);
+
+        Deployer.DeploymentState dstate = new Deployer.DeploymentState();
+        dstate.state = new State();
+        dstate.bundles = new HashMap<>();
+        dstate.bundlesPerRegion = new HashMap<>();
+        dstate.features = new HashMap<>();
+        dstate.features.put(f1.getId(), f1);
+        dstate.features.put(f2.getId(), f2);
+        dstate.filtersPerRegion = new HashMap<>();
+        dstate.filtersPerRegion.put(ROOT_REGION, new HashMap<String, 
Map<String, Set<String>>>());
+
+        Deployer.DeploymentRequest request = new Deployer.DeploymentRequest();
+        request.bundleUpdateRange = DEFAULT_BUNDLE_UPDATE_RANGE;
+        request.featureResolutionRange = DEFAULT_FEATURE_RESOLUTION_RANGE;
+        request.globalRepository = null;
+        request.options = EnumSet.noneOf(Option.class);
+        request.overrides = Collections.emptySet();
+        request.stateChanges = Collections.emptyMap();
+        request.updateSnaphots = UPDATE_SNAPSHOTS_NONE;
+        request.requestedFeatures = new HashMap<>();
+        addToMapSet(request.requestedFeatures, ROOT_REGION, f2.getName());
+
+        try {
+            deployer.deploy(dstate, request);
+            fail("Should have thrown an exception");
+        } catch (Deployer.PartialDeploymentException e) {
+            // ok
+        }
+
+        EasyMock.verify(callback);
+
+        EasyMock.reset(callback);
+
+        callback.print(EasyMock.anyString(), EasyMock.anyBoolean());
+        EasyMock.expectLastCall().anyTimes();
+        callback.installBundle(EasyMock.eq(ROOT_REGION), EasyMock.eq("b100"), 
EasyMock.<InputStream>anyObject());
+        EasyMock.expectLastCall().andReturn(serviceBundle2);
+        callback.replaceDigraph(EasyMock.<Map<String, Map<String, Map<String, 
Set<String>>>>>anyObject(),
+                EasyMock.<Map<String, Set<Long>>>anyObject());
+        EasyMock.expectLastCall();
+        callback.saveState(EasyMock.<State>anyObject());
+        EasyMock.expectLastCall();
+        callback.installFeatureConfigs(f2);
+        EasyMock.expectLastCall();
+        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject());
+        EasyMock.expectLastCall();
+        callback.callListeners(EasyMock.<FeatureEvent>anyObject());
+        EasyMock.expectLastCall();
+
+        EasyMock.replay(callback);
+
+        dstate = new Deployer.DeploymentState();
+        dstate.state = new State();
+        addToMapSet(dstate.state.installedFeatures, ROOT_REGION, f1.getId());
+        dstate.state.stateFeatures.put(ROOT_REGION, 
Collections.singletonMap(f1.getId(), "Started"));
+        addToMapSet(dstate.state.managedBundles, ROOT_REGION, 
serviceBundle1.getBundleId());
+        dstate.bundles = new HashMap<>();
+        dstate.bundles.put(serviceBundle1.getBundleId(), serviceBundle1);
+        dstate.bundlesPerRegion = new HashMap<>();
+        addToMapSet(dstate.bundlesPerRegion, ROOT_REGION, 
serviceBundle1.getBundleId());
+        dstate.features = new HashMap<>();
+        dstate.features.put(f1.getId(), f1);
+        dstate.features.put(f2.getId(), f2);
+        dstate.filtersPerRegion = new HashMap<>();
+        dstate.filtersPerRegion.put(ROOT_REGION, new HashMap<String, 
Map<String, Set<String>>>());
+
+        request = new Deployer.DeploymentRequest();
+        request.bundleUpdateRange = DEFAULT_BUNDLE_UPDATE_RANGE;
+        request.featureResolutionRange = DEFAULT_FEATURE_RESOLUTION_RANGE;
+        request.globalRepository = null;
+        request.options = EnumSet.noneOf(Option.class);
+        request.overrides = Collections.emptySet();
+        request.stateChanges = Collections.emptyMap();
+        request.updateSnaphots = UPDATE_SNAPSHOTS_NONE;
+        request.requestedFeatures = new HashMap<>();
+        addToMapSet(request.requestedFeatures, ROOT_REGION, f2.getName());
+
+        deployer.deploy(dstate, request);
+
+        EasyMock.verify(callback);
+    }
+
     private TestBundle createTestBundle(long bundleId, int state, String dir, 
String name) throws IOException, BundleException {
         URL loc = getClass().getResource(dir + "/" + name + ".mf");
         Manifest man = new Manifest(loc.openStream());

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/test/resources/org/apache/karaf/features/internal/service/data2/a100.mf
----------------------------------------------------------------------
diff --git 
a/features/core/src/test/resources/org/apache/karaf/features/internal/service/data2/a100.mf
 
b/features/core/src/test/resources/org/apache/karaf/features/internal/service/data2/a100.mf
new file mode 100644
index 0000000..20a7811
--- /dev/null
+++ 
b/features/core/src/test/resources/org/apache/karaf/features/internal/service/data2/a100.mf
@@ -0,0 +1,5 @@
+Manifest-Version: 1
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: a
+Bundle-Version: 1.0.0
+

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/test/resources/org/apache/karaf/features/internal/service/data2/b100.mf
----------------------------------------------------------------------
diff --git 
a/features/core/src/test/resources/org/apache/karaf/features/internal/service/data2/b100.mf
 
b/features/core/src/test/resources/org/apache/karaf/features/internal/service/data2/b100.mf
new file mode 100644
index 0000000..dc96158
--- /dev/null
+++ 
b/features/core/src/test/resources/org/apache/karaf/features/internal/service/data2/b100.mf
@@ -0,0 +1,5 @@
+Manifest-Version: 1
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: b
+Bundle-Version: 1.0.0
+

http://git-wip-us.apache.org/repos/asf/karaf/blob/cde64ae2/features/core/src/test/resources/org/apache/karaf/features/internal/service/data2/features.xml
----------------------------------------------------------------------
diff --git 
a/features/core/src/test/resources/org/apache/karaf/features/internal/service/data2/features.xml
 
b/features/core/src/test/resources/org/apache/karaf/features/internal/service/data2/features.xml
new file mode 100644
index 0000000..a70c692
--- /dev/null
+++ 
b/features/core/src/test/resources/org/apache/karaf/features/internal/service/data2/features.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+
+-->
+<features name="test" xmlns="http://karaf.apache.org/xmlns/features/v1.3.0";>
+
+    <feature name="f1" version="1.0.0">
+        <bundle>a100</bundle>
+    </feature>
+
+    <feature name="f2" version="1.0.0">
+        <feature prerequisite="true">f1</feature>
+        <bundle>b100</bundle>
+    </feature>
+
+</features>
\ No newline at end of file

Reply via email to