You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/05/19 21:16:24 UTC

[1/5] git commit: [KARAF-2988] Add support for prerequisites on features

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 <gn...@gmail.com>
Authored: Mon May 19 21:14:50 2014 +0200
Committer: Guillaume Nodet <gn...@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


[5/5] git commit: Fix manual and command help generation

Posted by gn...@apache.org.
Fix manual and command help generation


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

Branch: refs/heads/master
Commit: 49aeec2b73d3227ba78fb6bef7e025aafced8577
Parents: 0cf070d
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Mon May 19 10:53:45 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Mon May 19 21:16:05 2014 +0200

----------------------------------------------------------------------
 manual/pom.xml                                  | 41 +---------
 .../WEB-INF/scalate/layouts/default.scaml       |  2 +-
 manual/src/main/webapp/manual.conf              |  2 +-
 pom.xml                                         |  6 +-
 .../commands/AbstractCommandHelpPrinter.java    | 85 ++++++++++++++++++++
 .../tooling/commands/CommandHelpPrinter.java    |  6 +-
 .../commands/DocBookCommandHelpPrinter.java     | 74 ++++++++++-------
 .../tooling/commands/GenerateHelpMojo.java      | 11 +--
 .../commands/UserConfCommandHelpPrinter.java    | 67 +++++++++------
 9 files changed, 186 insertions(+), 108 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/49aeec2b/manual/pom.xml
----------------------------------------------------------------------
diff --git a/manual/pom.xml b/manual/pom.xml
index db14ba7..768fd21 100644
--- a/manual/pom.xml
+++ b/manual/pom.xml
@@ -167,7 +167,7 @@
                     </dependency>
                     <dependency>
                         <groupId>org.apache.karaf.shell</groupId>
-                        <artifactId>org.apache.karaf.shell.console</artifactId>
+                        <artifactId>org.apache.karaf.shell.core</artifactId>
                         <version>${project.version}</version>
                     </dependency>
                     <dependency>
@@ -176,32 +176,17 @@
                         <version>${project.version}</version>
                     </dependency>
                     <dependency>
-                        <groupId>org.apache.karaf.log</groupId>
-                        <artifactId>org.apache.karaf.log.command</artifactId>
-                        <version>${project.version}</version>
-                    </dependency>
-                    <dependency>
                         <groupId>org.apache.karaf.obr</groupId>
                         <artifactId>org.apache.karaf.obr.core</artifactId>
                         <version>${project.version}</version>
                     </dependency>
                     <dependency>
-                        <groupId>org.apache.karaf.obr</groupId>
-                        <artifactId>org.apache.karaf.obr.command</artifactId>
-                        <version>${project.version}</version>
-                    </dependency>
-                    <dependency>
                         <groupId>org.apache.karaf.bundle</groupId>
                         <artifactId>org.apache.karaf.bundle.core</artifactId>
                         <version>${project.version}</version>
                     </dependency>
                     <dependency>
                         <groupId>org.apache.karaf.bundle</groupId>
-                        <artifactId>org.apache.karaf.bundle.command</artifactId>
-                        <version>${project.version}</version>
-                    </dependency>
-                    <dependency>
-                        <groupId>org.apache.karaf.bundle</groupId>
                         <artifactId>org.apache.karaf.bundle.springstate</artifactId>
                         <version>${project.version}</version>
                     </dependency>
@@ -211,16 +196,6 @@
                         <version>${project.version}</version>
                     </dependency>
                     <dependency>
-                        <groupId>org.apache.karaf.service</groupId>
-                        <artifactId>org.apache.karaf.service.command</artifactId>
-                        <version>${project.version}</version>
-                    </dependency>
-                    <dependency>
-                        <groupId>org.apache.karaf.system</groupId>
-                        <artifactId>org.apache.karaf.system.command</artifactId>
-                        <version>${project.version}</version>
-                    </dependency>
-                    <dependency>
                         <groupId>org.apache.karaf.system</groupId>
                         <artifactId>org.apache.karaf.system.core</artifactId>
                         <version>${project.version}</version>
@@ -232,32 +207,22 @@
                     </dependency>
                     <dependency>
                         <groupId>org.apache.karaf.web</groupId>
-                        <artifactId>org.apache.karaf.web.command</artifactId>
-                        <version>${project.version}</version>
-                    </dependency>
-                    <dependency>
-                        <groupId>org.apache.karaf.web</groupId>
                         <artifactId>org.apache.karaf.web.core</artifactId>
                         <version>${project.version}</version>
                     </dependency>
                     <dependency>
                         <groupId>org.apache.karaf.wrapper</groupId>
-                        <artifactId>org.apache.karaf.wrapper.command</artifactId>
-                        <version>${project.version}</version>
-                    </dependency>
-                    <dependency>
-                        <groupId>org.apache.karaf.wrapper</groupId>
                         <artifactId>org.apache.karaf.wrapper.core</artifactId>
                         <version>${project.version}</version>
                     </dependency>
                     <dependency>
                         <groupId>org.apache.karaf.instance</groupId>
-                        <artifactId>org.apache.karaf.instance.command</artifactId>
+                        <artifactId>org.apache.karaf.instance.core</artifactId>
                         <version>${project.version}</version>
                     </dependency>
                     <dependency>
                         <groupId>org.apache.karaf.diagnostic</groupId>
-                        <artifactId>org.apache.karaf.diagnostic.command</artifactId>
+                        <artifactId>org.apache.karaf.diagnostic.core</artifactId>
                         <version>${project.version}</version>
                     </dependency>
                     <dependency>

http://git-wip-us.apache.org/repos/asf/karaf/blob/49aeec2b/manual/src/main/webapp/WEB-INF/scalate/layouts/default.scaml
----------------------------------------------------------------------
diff --git a/manual/src/main/webapp/WEB-INF/scalate/layouts/default.scaml b/manual/src/main/webapp/WEB-INF/scalate/layouts/default.scaml
index c88ba58..e4f22cd 100644
--- a/manual/src/main/webapp/WEB-INF/scalate/layouts/default.scaml
+++ b/manual/src/main/webapp/WEB-INF/scalate/layouts/default.scaml
@@ -98,7 +98,7 @@
         %td#cell-3-2
           #footer
             #site-footer
-              &copy; 2008-2011 The Apache Software Foundation
+              &copy; 2008-2014 The Apache Software Foundation
               %br
               Apache Karaf, Karaf, Apache, the Apache feather logo, and the Apache Karaf project logo are trademarks of The Apache Software Foundation.
         %td#cell-3-3

http://git-wip-us.apache.org/repos/asf/karaf/blob/49aeec2b/manual/src/main/webapp/manual.conf
----------------------------------------------------------------------
diff --git a/manual/src/main/webapp/manual.conf b/manual/src/main/webapp/manual.conf
index db428da..de21749 100644
--- a/manual/src/main/webapp/manual.conf
+++ b/manual/src/main/webapp/manual.conf
@@ -15,7 +15,7 @@ Users' Guide
 {div}
 
 {div:class=copyright-section}
-Copyright 2011 The Apache Software Foundation
+Copyright 2008-2014 The Apache Software Foundation
 
 The PDF format of the Karaf Manual has been generated by Prince XML (http://www.princexml.com).
 {div}

http://git-wip-us.apache.org/repos/asf/karaf/blob/49aeec2b/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 8aeb574..fe95af6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2250,9 +2250,9 @@
                                 <exclude>**/foo</exclude>
                                 <exclude>**/org.ops4j.pax.exam.TestContainerFactory</exclude>
                                 <!--manual resources -->
-                                <exclude>manual/**/*.css</exclude>
-                                <exclude>manual/**/*.ssp</exclude>
-                                <exclude>manual/**/*.conf</exclude>
+                                <exclude>**/*.css</exclude>
+                                <exclude>**/*.ssp</exclude>
+                                <exclude>**/*.conf</exclude>
                                 <!-- test manifests -->
                                 <exclude>**/*.mf</exclude>
                                 <!-- test json files -->

http://git-wip-us.apache.org/repos/asf/karaf/blob/49aeec2b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/AbstractCommandHelpPrinter.java
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/AbstractCommandHelpPrinter.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/AbstractCommandHelpPrinter.java
new file mode 100644
index 0000000..dee1eb9
--- /dev/null
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/AbstractCommandHelpPrinter.java
@@ -0,0 +1,85 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.tooling.commands;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+
+public abstract class AbstractCommandHelpPrinter implements CommandHelpPrinter {
+
+    protected Argument replaceDefaultArgument(Field field, Argument argument) {
+        if (Argument.DEFAULT.equals(argument.name())) {
+            final Argument delegate = argument;
+            final String name = field.getName();
+            argument = new Argument() {
+                public String name() {
+                    return name;
+                }
+
+                public String description() {
+                    return delegate.description();
+                }
+
+                public boolean required() {
+                    return delegate.required();
+                }
+
+                public int index() {
+                    return delegate.index();
+                }
+
+                public boolean multiValued() {
+                    return delegate.multiValued();
+                }
+
+                public String valueToShowInHelp() {
+                    return delegate.valueToShowInHelp();
+                }
+
+                public Class<? extends Annotation> annotationType() {
+                    return delegate.annotationType();
+                }
+            };
+        }
+        return argument;
+    }
+
+    protected Object getDefaultValue(Action action, Field field) {
+        try {
+            field.setAccessible(true);
+            return field.get(action);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    protected String getDefaultValueString(Object o) {
+        if (o != null
+                && (!(o instanceof Boolean) || ((Boolean) o))
+                && (!(o instanceof Number) || ((Number) o).doubleValue() != 0.0)) {
+            return o.toString();
+        } else {
+            return null;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/49aeec2b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/CommandHelpPrinter.java
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/CommandHelpPrinter.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/CommandHelpPrinter.java
index 5fae3db..f255434 100644
--- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/CommandHelpPrinter.java
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/CommandHelpPrinter.java
@@ -22,19 +22,17 @@ import java.io.PrintStream;
 import java.util.Map;
 import java.util.Set;
 
-import org.apache.karaf.shell.commands.Action;
-import org.apache.karaf.shell.commands.meta.ActionMetaData;
+import org.apache.karaf.shell.api.action.Action;
 
 public interface CommandHelpPrinter {
     /**
      * Print help for a single action to the out stream
      * 
      * @param action
-     * @param actionMeta
      * @param out stream to write to
      * @param includeHelpOption include the help option in the doc
      */
-    void printHelp(Action action, ActionMetaData actionMeta, PrintStream out, boolean includeHelpOption);
+    void printHelp(Action action, PrintStream out, boolean includeHelpOption);
     
     /**
      * Print the overview of all given commands to the out stream 

http://git-wip-us.apache.org/repos/asf/karaf/blob/49aeec2b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/DocBookCommandHelpPrinter.java
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/DocBookCommandHelpPrinter.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/DocBookCommandHelpPrinter.java
index 7467b9a..d40841c 100644
--- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/DocBookCommandHelpPrinter.java
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/DocBookCommandHelpPrinter.java
@@ -21,38 +21,53 @@ package org.apache.karaf.tooling.commands;
 import java.io.PrintStream;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import org.apache.karaf.shell.commands.Action;
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.HelpOption;
-import org.apache.karaf.shell.commands.Option;
-import org.apache.karaf.shell.commands.meta.ActionMetaData;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.impl.action.command.HelpOption;
+
 
 /**
  * Prints documentation in docbook syntax
  */
-public class DocBookCommandHelpPrinter implements CommandHelpPrinter {
+public class DocBookCommandHelpPrinter extends AbstractCommandHelpPrinter {
 
     @Override
-    public void printHelp(Action action, ActionMetaData actionMeta, PrintStream out, boolean includeHelpOption) {
-
-        Map<Option, Field> optionsMap = actionMeta.getOptions();
-        Map<Argument, Field> argsMap = actionMeta.getArguments();
+    public void printHelp(Action action, PrintStream out, boolean includeHelpOption) {
         Command command = action.getClass().getAnnotation(Command.class);
-        List<Argument> arguments = new ArrayList<Argument>(argsMap.keySet());
-        Collections.sort(arguments, new Comparator<Argument>() {
-            public int compare(Argument o1, Argument o2) {
-                return Integer.valueOf(o1.index()).compareTo(Integer.valueOf(o2.index()));
+        Set<Option> options = new HashSet<>();
+        List<Argument> arguments = new ArrayList<Argument>();
+        Map<Argument, Field> argFields = new HashMap<>();
+        Map<Option, Field> optFields = new HashMap<>();
+        for (Class<?> type = action.getClass(); type != null; type = type.getSuperclass()) {
+            for (Field field : type.getDeclaredFields()) {
+                Option option = field.getAnnotation(Option.class);
+                if (option != null) {
+                    options.add(option);
+                }
+
+                Argument argument = field.getAnnotation(Argument.class);
+                if (argument != null) {
+                    argument = replaceDefaultArgument(field, argument);
+                    argFields.put(argument, field);
+                    int index = argument.index();
+                    while (arguments.size() <= index) {
+                        arguments.add(null);
+                    }
+                    if (arguments.get(index) != null) {
+                        throw new IllegalArgumentException("Duplicate argument index: " + index + " on Action " + action.getClass().getName());
+                    }
+                    arguments.set(index, argument);
+                }
             }
-        });
-        Set<Option> options = new HashSet<Option>(optionsMap.keySet());
+        }
         if (includeHelpOption)
             options.add(HelpOption.HELP);
 
@@ -92,10 +107,14 @@ public class DocBookCommandHelpPrinter implements CommandHelpPrinter {
                 out.println("      <td>" + argument.name() + "</td>");
                 String description = argument.description();
                 if (!argument.required()) {
-                    Object o = actionMeta.getDefaultValue(action, argument);
-                    String defaultValue = actionMeta.getDefaultValueString(o);
-                    if (defaultValue != null) {
-                        description += " (defaults to " + o.toString() + ")";
+                    if (argument.valueToShowInHelp() != null && argument.valueToShowInHelp().length() != 0) {
+                        if (Argument.DEFAULT_STRING.equals(argument.valueToShowInHelp())) {
+                            Object o = getDefaultValue(action, argFields.get(argument));
+                            String defaultValue = getDefaultValueString(o);
+                            if (defaultValue != null) {
+                                description += " (defaults to " + o.toString() + ")";
+                            }
+                        }
                     }
                 }
                 out.println("      <td>" + description + "</td>");
@@ -117,8 +136,8 @@ public class DocBookCommandHelpPrinter implements CommandHelpPrinter {
                 for (String alias : option.aliases()) {
                     opt += ", " + alias;
                 }
-                Object o = actionMeta.getDefaultValue(action, option);
-                String defaultValue = actionMeta.getDefaultValueString(o);
+                Object o = getDefaultValue(action, optFields.get(option));
+                String defaultValue = getDefaultValueString(o);
                 if (defaultValue != null) {
                     description += " (defaults to " + o.toString() + ")";
                 }
@@ -132,12 +151,11 @@ public class DocBookCommandHelpPrinter implements CommandHelpPrinter {
             out.println("  </section>");
         }
 
-        String desc = actionMeta.getDetailedDescription();
-        if (desc != null) {
+        if (command.detailedDescription().length() > 0) {
             out.println("  <section>");
             out.println("    <title>Details</title>");
             out.println("    <para>");
-            out.println(desc);
+            out.println(command.detailedDescription());
             out.println("    </para>");
             out.println("  </section>");
         }

http://git-wip-us.apache.org/repos/asf/karaf/blob/49aeec2b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/GenerateHelpMojo.java
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/GenerateHelpMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/GenerateHelpMojo.java
index 7416640..b75a766 100644
--- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/GenerateHelpMojo.java
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/GenerateHelpMojo.java
@@ -30,10 +30,8 @@ import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
 
-import org.apache.karaf.shell.commands.Action;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.meta.ActionMetaData;
-import org.apache.karaf.shell.commands.meta.ActionMetaDataFactory;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Command;
 import org.apache.maven.artifact.DependencyResolutionRequiredException;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
@@ -118,8 +116,7 @@ public class GenerateHelpMojo extends AbstractMojo {
             for (Class<?> clazz : classes) {
                 try {
                     Action action = (Action) clazz.newInstance();
-                    ActionMetaData meta = new ActionMetaDataFactory().create(action.getClass());
-                    Command cmd = meta.getCommand();
+                    Command cmd = clazz.getAnnotation(Command.class);
 
                     // skip the *-help command
                     if (cmd.scope().equals("*")) continue;
@@ -127,7 +124,7 @@ public class GenerateHelpMojo extends AbstractMojo {
                     File output = new File(targetFolder, cmd.scope() + "-" + cmd.name() + "." + commandSuffix);
                     FileOutputStream outStream = new FileOutputStream(output);
                     PrintStream out = new PrintStream(outStream);
-                    helpPrinter.printHelp(action, meta, out, includeHelpOption);
+                    helpPrinter.printHelp(action, out, includeHelpOption);
                     out.close();
                     outStream.close();
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/49aeec2b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/UserConfCommandHelpPrinter.java
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/UserConfCommandHelpPrinter.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/UserConfCommandHelpPrinter.java
index 468386f..59f7bd9 100644
--- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/UserConfCommandHelpPrinter.java
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/commands/UserConfCommandHelpPrinter.java
@@ -21,37 +21,53 @@ package org.apache.karaf.tooling.commands;
 import java.io.PrintStream;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import org.apache.karaf.shell.commands.Action;
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.HelpOption;
-import org.apache.karaf.shell.commands.Option;
-import org.apache.karaf.shell.commands.meta.ActionMetaData;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.impl.action.command.HelpOption;
+
 
 /**
  * Prints documentation in wiki syntax
  */
-public class UserConfCommandHelpPrinter implements CommandHelpPrinter {
+public class UserConfCommandHelpPrinter extends AbstractCommandHelpPrinter {
 
     @Override
-    public void printHelp(Action action, ActionMetaData actionMeta, PrintStream out, boolean includeHelpOption) {
-        Map<Option, Field> optionsMap = actionMeta.getOptions();
-        Map<Argument, Field> argsMap = actionMeta.getArguments();
-        Command command = actionMeta.getCommand();
-        List<Argument> arguments = new ArrayList<Argument>(argsMap.keySet());
-        Collections.sort(arguments, new Comparator<Argument>() {
-            public int compare(Argument o1, Argument o2) {
-                return Integer.valueOf(o1.index()).compareTo(Integer.valueOf(o2.index()));
+    public void printHelp(Action action, PrintStream out, boolean includeHelpOption) {
+        Command command = action.getClass().getAnnotation(Command.class);
+        Set<Option> options = new HashSet<>();
+        List<Argument> arguments = new ArrayList<Argument>();
+        Map<Argument, Field> argFields = new HashMap<>();
+        Map<Option, Field> optFields = new HashMap<>();
+        for (Class<?> type = action.getClass(); type != null; type = type.getSuperclass()) {
+            for (Field field : type.getDeclaredFields()) {
+                Option option = field.getAnnotation(Option.class);
+                if (option != null) {
+                    options.add(option);
+                }
+
+                Argument argument = field.getAnnotation(Argument.class);
+                if (argument != null) {
+                    argument = replaceDefaultArgument(field, argument);
+                    argFields.put(argument, field);
+                    int index = argument.index();
+                    while (arguments.size() <= index) {
+                        arguments.add(null);
+                    }
+                    if (arguments.get(index) != null) {
+                        throw new IllegalArgumentException("Duplicate argument index: " + index + " on Action " + action.getClass().getName());
+                    }
+                    arguments.set(index, argument);
+                }
             }
-        });
-        Set<Option> options = new HashSet<Option>(optionsMap.keySet());
+        }
         if (includeHelpOption)
             options.add(HelpOption.HELP);
 
@@ -83,8 +99,8 @@ public class UserConfCommandHelpPrinter implements CommandHelpPrinter {
             for (Argument argument : arguments) {
                 String description = argument.description();
                 if (!argument.required()) {
-                    Object o = actionMeta.getDefaultValue(action, argument);
-                    String defaultValue = actionMeta.getDefaultValueString(o);
+                    Object o = getDefaultValue(action, argFields.get(argument));
+                    String defaultValue = getDefaultValueString(o);
                     if (defaultValue != null) {
                         description += " (defaults to " + o.toString() + ")";
                     }
@@ -102,8 +118,8 @@ public class UserConfCommandHelpPrinter implements CommandHelpPrinter {
                 for (String alias : option.aliases()) {
                     opt += ", " + alias;
                 }
-                Object o = actionMeta.getDefaultValue(action, option);
-                String defaultValue = actionMeta.getDefaultValueString(o);
+                Object o = getDefaultValue(action, optFields.get(option));
+                String defaultValue = getDefaultValueString(o);
                 if (defaultValue != null) {
                     desc += " (defaults to " + defaultValue + ")";
                 }
@@ -111,10 +127,9 @@ public class UserConfCommandHelpPrinter implements CommandHelpPrinter {
             }
             out.println();
         }
-        String detailedDesc = actionMeta.getDetailedDescription();
-        if (detailedDesc != null) {
+        if (command.detailedDescription().length() > 0) {
             out.println("h2. Details");
-            out.println(detailedDesc);
+            out.println(command.detailedDescription());
         }
         out.println();
     }


[4/5] git commit: Fix web module activator

Posted by gn...@apache.org.
Fix web module activator


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

Branch: refs/heads/master
Commit: c1a6ca7454d15e19462f92dcdd75b98df4d641ad
Parents: 1d60fca
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Mon May 19 11:56:14 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Mon May 19 21:16:05 2014 +0200

----------------------------------------------------------------------
 web/src/main/java/org/apache/karaf/web/internal/osgi/Activator.java | 1 -
 1 file changed, 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/c1a6ca74/web/src/main/java/org/apache/karaf/web/internal/osgi/Activator.java
----------------------------------------------------------------------
diff --git a/web/src/main/java/org/apache/karaf/web/internal/osgi/Activator.java b/web/src/main/java/org/apache/karaf/web/internal/osgi/Activator.java
index 4709e81..7a1681c 100644
--- a/web/src/main/java/org/apache/karaf/web/internal/osgi/Activator.java
+++ b/web/src/main/java/org/apache/karaf/web/internal/osgi/Activator.java
@@ -32,7 +32,6 @@ import org.ops4j.pax.web.service.spi.WebListener;
         requires = @RequireService(WarManager.class),
         provides = @ProvideService(WebContainerService.class)
 )
-@Managed("org.apache.karaf.shell")
 public class Activator extends BaseActivator {
 
     @Override


[2/5] git commit: Clean some warnings and unused code in features/core

Posted by gn...@apache.org.
Clean some warnings and unused code in features/core


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

Branch: refs/heads/master
Commit: 1d60fca2132cc2b204ac43efb1a9fcc589e16e82
Parents: 49aeec2
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Mon May 19 11:17:50 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Mon May 19 21:16:05 2014 +0200

----------------------------------------------------------------------
 .../internal/resolver/ResourceUtils.java        |  4 +--
 .../internal/resolver/SimpleFilter.java         |  7 +++--
 .../internal/service/BootFeaturesInstaller.java | 29 ++------------------
 3 files changed, 9 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/1d60fca2/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java
index d6cfe1d..d3f5527 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java
@@ -79,8 +79,8 @@ public final class ResourceUtils {
     }
 
     public static void addIdentityRequirement(ResourceImpl resource, String name, String type, String range) {
-        Map<String, String> dirs = new HashMap<String, String>();
-        Map<String, Object> attrs = new HashMap<String, Object>();
+        Map<String, String> dirs = new HashMap<>();
+        Map<String, Object> attrs = new HashMap<>();
         if (name != null) {
             attrs.put(IDENTITY_NAMESPACE, name);
         }

http://git-wip-us.apache.org/repos/asf/karaf/blob/1d60fca2/features/core/src/main/java/org/apache/karaf/features/internal/resolver/SimpleFilter.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/SimpleFilter.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/SimpleFilter.java
index 1e2a48c..2354519 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/SimpleFilter.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/SimpleFilter.java
@@ -57,8 +57,9 @@ public class SimpleFilter {
         return op;
     }
 
+    @SuppressWarnings("unchecked")
     public String toString() {
-        String s = null;
+        String s;
         switch (op) {
         case AND:
             s = "(&" + toString((List) value) + ")";
@@ -138,6 +139,7 @@ public class SimpleFilter {
         return o.toString();
     }
 
+    @SuppressWarnings("unchecked")
     public static SimpleFilter parse(String filter) {
         int idx = skipWhitespace(filter, 0);
 
@@ -446,11 +448,12 @@ public class SimpleFilter {
      * @param attrs Map of attributes to convert to a filter.
      * @return A filter corresponding to the attributes.
      */
+    @SuppressWarnings("unchecked")
     public static SimpleFilter convert(Map<String, Object> attrs) {
         // Rather than building a filter string to be parsed into a SimpleFilter,
         // we will just create the parsed SimpleFilter directly.
 
-        List<SimpleFilter> filters = new ArrayList<SimpleFilter>();
+        List<SimpleFilter> filters = new ArrayList<>();
 
         for (Entry<String, Object> entry : attrs.entrySet()) {
             if (entry.getValue() instanceof VersionRange) {

http://git-wip-us.apache.org/repos/asf/karaf/blob/1d60fca2/features/core/src/main/java/org/apache/karaf/features/internal/service/BootFeaturesInstaller.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/BootFeaturesInstaller.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/BootFeaturesInstaller.java
index 81e6a3a..f8283dd 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/service/BootFeaturesInstaller.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/BootFeaturesInstaller.java
@@ -28,7 +28,6 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.karaf.features.BootFinished;
-import org.apache.karaf.features.Feature;
 import org.apache.karaf.features.FeaturesService;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
@@ -36,8 +35,6 @@ import org.slf4j.LoggerFactory;
 
 public class BootFeaturesInstaller {
 
-    public static final String VERSION_PREFIX = "version=";
-
     private static final Logger LOGGER = LoggerFactory.getLogger(BootFeaturesInstaller.class);
 
     private final FeaturesServiceImpl featuresService;
@@ -115,32 +112,10 @@ public class BootFeaturesInstaller {
         }
     }
 
-    /**
-     * @param featureSt either feature name or <featurename>;version=<version>
-     * @return feature matching the feature string
-     * @throws Exception
-     */
-    private Feature getFeature(String featureSt) throws Exception {
-        String[] parts = featureSt.trim().split(";");
-        String featureName = parts[0];
-        String featureVersion = null;
-        for (String part : parts) {
-            // if the part starts with "version=" it contains the version info
-            if (part.startsWith(VERSION_PREFIX)) {
-                featureVersion = part.substring(VERSION_PREFIX.length());
-            }
-        }
-        if (featureVersion == null) {
-            // no version specified - use default version
-            featureVersion = org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION;
-        }
-        return featuresService.getFeature(featureName, featureVersion);
-    }
-
     protected List<Set<String>> parseBootFeatures(String bootFeatures) {
         Pattern pattern = Pattern.compile("(\\((.+))\\),|.+");
         Matcher matcher = pattern.matcher(bootFeatures);
-        List<Set<String>> result = new ArrayList<Set<String>>();
+        List<Set<String>> result = new ArrayList<>();
         while (matcher.find()) {
             String group = matcher.group(2) != null ? matcher.group(2) : matcher.group();
             result.add(parseFeatureList(group));
@@ -149,7 +124,7 @@ public class BootFeaturesInstaller {
     }
 
     protected Set<String> parseFeatureList(String group) {
-        HashSet<String> features = new HashSet<String>();
+        HashSet<String> features = new HashSet<>();
         for (String feature : Arrays.asList(group.trim().split("\\s*,\\s*"))) {
             if (feature.length() > 0) {
                 features.add(feature);


[3/5] git commit: Fix scr feature to add missing capabilities

Posted by gn...@apache.org.
Fix scr feature to add missing capabilities


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

Branch: refs/heads/master
Commit: 0cf070d4f56348110b74234ce7a2a82022572f4d
Parents: b46bd22
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Mon May 19 09:59:53 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Mon May 19 21:16:05 2014 +0200

----------------------------------------------------------------------
 assemblies/features/standard/src/main/feature/feature.xml | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/0cf070d4/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 a7f6e60..c02dea0 100644
--- a/assemblies/features/standard/src/main/feature/feature.xml
+++ b/assemblies/features/standard/src/main/feature/feature.xml
@@ -273,7 +273,7 @@
     <feature name="scr" description="Declarative Service support" version="${project.version}">
         <feature>eventadmin</feature>
         <bundle dependency="true" start-level="30">mvn:org.apache.felix/org.apache.felix.metatype/${felix.metatype.version}</bundle>
-        <bundle dependency="true" start-level="30">mvn:org.apache.felix/org.apache.felix.scr/${felix.scr.version}</bundle>
+        <bundle start-level="30">mvn:org.apache.felix/org.apache.felix.scr/${felix.scr.version}</bundle>
         <bundle start-level="30">mvn:org.apache.karaf.scr/org.apache.karaf.scr.command/${project.version}</bundle>
         <conditional>
             <condition>management</condition>
@@ -283,6 +283,10 @@
             <condition>webconsole</condition>
             <bundle start-level="30">mvn:org.apache.felix/org.apache.felix.webconsole.plugins.ds/${felix.scr.webconsole.plugin.version}</bundle>
         </conditional>
+        <capability>
+            osgi.service;effective:=active;objectClass=org.apache.felix.scr.ScrService,
+            osgi.extender;osgi.extender="osgi.component";uses:="org.osgi.service.component";version:Version="1.2.1"
+        </capability>
     </feature>
 
     <feature name="blueprint-web" description="Provides an OSGI-aware Servlet ContextListener for bootstrapping