diff --git a/assemblies/features/standard/src/main/feature/feature.xml b/assemblies/features/standard/src/main/feature/feature.xml index ba1ee59..e2fe630 100644 Index: assemblies/features/standard/src/main/feature/feature.xml =================================================================== --- assemblies/features/standard/src/main/feature/feature.xml +++ assemblies/features/standard/src/main/feature/feature.xml @@ -16,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - + mvn:org.apache.karaf.shell/org.apache.karaf.shell.console/${project.version} @@ -33,6 +33,13 @@ mvn:org.apache.karaf.log/org.apache.karaf.log.command/${project.version} mvn:org.apache.karaf.service/org.apache.karaf.service.core/${project.version} mvn:org.apache.karaf.service/org.apache.karaf.service.command/${project.version} + + webconsole + mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.instance/${project.version} + mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.features/${project.version} + mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.gogo/${project.version} + mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.http/${project.version} + @@ -136,7 +143,7 @@ mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.kar/${project.version} - + realm=karaf @@ -144,15 +151,12 @@ mvn:org.apache.felix/org.apache.felix.metatype/${felix.metatype.version} mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.branding/${project.version} mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.console/${project.version} - + + + eventadmin + mvn:org.apache.felix/org.apache.felix.webconsole.plugins.event/${felix.eventadmin-plugin.version} + - - webconsole-base - mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.instance/${project.version} - mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.features/${project.version} - mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.gogo/${project.version} - mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.http/${project.version} - mvn:org.apache.felix/org.apache.felix.webconsole.plugins.event/${felix.eventadmin-plugin.version} @@ -196,7 +200,10 @@ mvn:org.apache.felix/org.apache.felix.metatype/${felix.metatype.version} mvn:org.apache.felix/org.apache.felix.scr/${felix.scr.version} mvn:org.apache.karaf.scr/org.apache.karaf.scr.command/${project.version} - mvn:org.apache.karaf.scr/org.apache.karaf.scr.management/${project.version} + + management + mvn:org.apache.karaf.scr/org.apache.karaf.scr.management/${project.version} + diff --git a/features/command/src/main/java/org/apache/karaf/features/command/InfoFeatureCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/InfoFeatureCommand.java index 8e6a253..9c4c862 100644 Index: features/command/src/main/java/org/apache/karaf/features/command/InfoFeatureCommand.java =================================================================== --- features/command/src/main/java/org/apache/karaf/features/command/InfoFeatureCommand.java +++ features/command/src/main/java/org/apache/karaf/features/command/InfoFeatureCommand.java @@ -16,9 +16,14 @@ */ package org.apache.karaf.features.command; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; +import org.apache.karaf.features.Conditional; import org.apache.karaf.shell.commands.Argument; import org.apache.karaf.shell.commands.Command; import org.apache.karaf.shell.commands.Option; @@ -32,6 +37,8 @@ import org.apache.karaf.features.FeaturesService; public class InfoFeatureCommand extends FeaturesCommandSupport { private static final String INDENT = " "; + private static final String FEATURE_CONTENT = "Feature"; + private static final String CONDITIONAL_CONTENT = "Conditional(%s)"; @Argument(index = 0, name = "name", description = "The name of the feature", required = true, multiValued = false) private String name; @@ -48,6 +55,9 @@ public class InfoFeatureCommand extends FeaturesCommandSupport { @Option(name = "-b", aliases={"--bundle"}, description="Display bundles info", required = false, multiValued = false) private boolean bundle; + @Option(name = "--conditional", description="Display conditional info", required = false, multiValued = false) + private boolean conditional; + @Option(name = "-t", aliases={"--tree"}, description="Display feature tree", required = false, multiValued = false) private boolean tree; @@ -66,10 +76,11 @@ public class InfoFeatureCommand extends FeaturesCommandSupport { } // default behavior - if (!config && !dependency && !bundle) { + if (!config && !dependency && !bundle && !conditional) { config = true; dependency = true; bundle = true; + conditional = true; } System.out.println("Feature " + feature.getName() + " " + feature.getVersion()); @@ -84,16 +95,20 @@ public class InfoFeatureCommand extends FeaturesCommandSupport { } if (config) { - displayConfigInformation(feature); - displayConfigFileInformation(feature); + displayConfigInformation(feature, FEATURE_CONTENT); + displayConfigFileInformation(feature, FEATURE_CONTENT); } if (dependency) { - displayDependencyInformation(feature); + displayDependencyInformation(feature, FEATURE_CONTENT); } if (bundle) { - displayBundleInformation(feature); + displayBundleInformation(feature, FEATURE_CONTENT); + } + + if(conditional) { + displayConditionalInfo(feature); } if (tree) { @@ -116,12 +131,12 @@ public class InfoFeatureCommand extends FeaturesCommandSupport { } } - private void displayBundleInformation(Feature feature) { + private void displayBundleInformation(Feature feature, String contentType) { List bundleInfos = feature.getBundles(); if (bundleInfos.isEmpty()) { - System.out.println("Feature has no bundles."); + System.out.println(contentType + " has no bundles."); } else { - System.out.println("Feature contains followed bundles:"); + System.out.println(contentType + " contains followed bundles:"); for (BundleInfo featureBundle : bundleInfos) { int startLevel = featureBundle.getStartLevel(); StringBuilder sb = new StringBuilder(); @@ -134,36 +149,36 @@ public class InfoFeatureCommand extends FeaturesCommandSupport { } } - private void displayDependencyInformation(Feature feature) { + private void displayDependencyInformation(Feature feature, String contentType) { List dependencies = feature.getDependencies(); if (dependencies.isEmpty()) { - System.out.println("Feature has no dependencies."); + System.out.println(contentType + " has no dependencies."); } else { - System.out.println("Feature depends on:"); + System.out.println(contentType + " depends on:"); for (Dependency featureDependency : dependencies) { System.out.println(INDENT + featureDependency.getName() + " " + featureDependency.getVersion()); } } } - private void displayConfigInformation(Feature feature) { + private void displayConfigInformation(Feature feature, String contentType) { Map> configurations = feature.getConfigurations(); if (configurations.isEmpty()) { - System.out.println("Feature has no configuration"); + System.out.println(contentType + " has no configuration"); } else { - System.out.println("Feature configuration:"); + System.out.println(contentType + " configuration:"); for (String name : configurations.keySet()) { System.out.println(INDENT + name); } } } - private void displayConfigFileInformation(Feature feature) { + private void displayConfigFileInformation(Feature feature, String contentType) { List configurationFiles = feature.getConfigurationFiles(); if (configurationFiles.isEmpty()) { - System.out.println("Feature has no configuration files"); + System.out.println(contentType + " has no configuration files"); } else { - System.out.println("Feature configuration files: "); + System.out.println(contentType + " configuration files: "); for (ConfigFileInfo configFileInfo : configurationFiles) { System.out.println(INDENT + configFileInfo.getFinalname()); } @@ -193,9 +208,23 @@ public class InfoFeatureCommand extends FeaturesCommandSupport { if (resolved != null) { if (bundle) { + List bundleLocation = new LinkedList(); List bundles = resolved.getBundles(); - for (int i = 0, j = bundles.size(); i < j; i++) { - System.out.println(prefix + " " + (i+1 == j ? "\\" : "+") + " " + bundles.get(i).getLocation()); + for (BundleInfo bundleInfo : bundles) { + bundleLocation.add(bundleInfo.getLocation()); + } + + if (conditional) { + for (Conditional cond : resolved.getConditional()) { + List condition = cond.getCondition(); + List conditionalBundles = cond.getBundles(); + for (BundleInfo bundleInfo : conditionalBundles) { + bundleLocation.add(bundleInfo.getLocation() + "(condition:"+condition+")"); + } + } + } + for (int i = 0, j = bundleLocation.size(); i < j; i++) { + System.out.println(prefix + " " + (i + 1 == j ? "\\" : "+") + " " + bundleLocation.get(i)); } } prefix += " "; @@ -204,9 +233,57 @@ public class InfoFeatureCommand extends FeaturesCommandSupport { Dependency toDisplay = dependencies.get(i); unresolved += displayFeatureTree(admin, toDisplay.getName(), toDisplay.getVersion(), prefix +1); } + + if (conditional) { + for (Conditional cond : resolved.getConditional()) { + List conditionDependencies = cond.getDependencies(); + for (int i = 0, j = conditionDependencies.size(); i < j; i++) { + Dependency toDisplay = dependencies.get(i); + unresolved += displayFeatureTree(admin, toDisplay.getName(), toDisplay.getVersion(), prefix +1); + } + } + } } return unresolved; } + private void displayConditionalInfo(Feature feature) { + List conditionals = feature.getConditional(); + if (conditionals.isEmpty()) { + System.out.println("Feature has no conditionals."); + } else { + System.out.println("Feature contains followed conditionals:"); + for (Conditional featureConditional : conditionals) { + String conditionDescription = getConditionDescription(featureConditional); + Feature wrappedConditional = featureConditional.asFeature(feature.getName(), feature.getVersion()); + if (config) { + displayConfigInformation(wrappedConditional, String.format(CONDITIONAL_CONTENT, conditionDescription)); + displayConfigFileInformation(wrappedConditional, String.format(CONDITIONAL_CONTENT, conditionDescription)); + } + + if (dependency) { + displayDependencyInformation(wrappedConditional, String.format(CONDITIONAL_CONTENT, conditionDescription)); + } + + if (bundle) { + displayBundleInformation(wrappedConditional, String.format(CONDITIONAL_CONTENT, conditionDescription)); + } + } + } + } + + private String getConditionDescription(Conditional cond) { + StringBuffer sb = new StringBuffer(); + Iterator di = cond.getCondition().iterator(); + while (di.hasNext()) { + Dependency dep = di.next(); + sb.append(dep.getName()).append("/").append(dep.getVersion()); + if (di.hasNext()) { + sb.append(" "); + } + } + return sb.toString(); + } + } diff --git a/features/core/src/main/java/org/apache/karaf/features/Conditional.java b/features/core/src/main/java/org/apache/karaf/features/Conditional.java index 3546d11..2473cf6 100644 Index: features/core/src/main/java/org/apache/karaf/features/Conditional.java =================================================================== --- features/core/src/main/java/org/apache/karaf/features/Conditional.java +++ features/core/src/main/java/org/apache/karaf/features/Conditional.java @@ -5,7 +5,7 @@ import java.util.Map; public interface Conditional { - String getCondition(); + List getCondition(); List getDependencies(); diff --git a/features/core/src/main/java/org/apache/karaf/features/Feature.java b/features/core/src/main/java/org/apache/karaf/features/Feature.java index 16c9fd4..4dfb624 100644 Index: features/core/src/main/java/org/apache/karaf/features/Feature.java =================================================================== --- features/core/src/main/java/org/apache/karaf/features/Feature.java +++ features/core/src/main/java/org/apache/karaf/features/Feature.java @@ -49,6 +49,8 @@ public interface Feature { Map> getConfigurations(); List getConfigurationFiles(); + + List getConditional(); int getStartLevel(); diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java b/features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java index c083f33..5d272ba 100644 Index: features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java =================================================================== --- features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java +++ features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java @@ -42,6 +42,7 @@ public class FeatureValidationUtil { public static final QName FEATURES_0_0 = new QName("features"); public static final QName FEATURES_1_0 = new QName("http://karaf.apache.org/xmlns/features/v1.0.0", "features"); public static final QName FEATURES_1_1 = new QName("http://karaf.apache.org/xmlns/features/v1.1.0", "features"); + public static final QName FEATURES_1_2 = new QName("http://karaf.apache.org/xmlns/features/v1.2.0", "features"); private static final Logger log = LoggerFactory.getLogger(FeatureValidationUtil.class); @@ -63,7 +64,10 @@ public class FeatureValidationUtil { validate(doc, "/org/apache/karaf/features/karaf-features-1.0.0.xsd"); } else if (FEATURES_1_1.equals(name)) { validate(doc, "/org/apache/karaf/features/karaf-features-1.1.0.xsd"); - } else { + } else if (FEATURES_1_2.equals(name)) { + validate(doc, "/org/apache/karaf/features/karaf-features-1.2.0.xsd"); + } + else { throw new IllegalArgumentException("Unrecognized root element: " + name); } } diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java index 7d42f5c..b630c52 100644 Index: features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java =================================================================== --- features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java +++ features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java @@ -60,6 +60,7 @@ import org.apache.felix.utils.manifest.Parser; import org.apache.felix.utils.version.VersionRange; import org.apache.felix.utils.version.VersionTable; import org.apache.karaf.features.BundleInfo; +import org.apache.karaf.features.Conditional; import org.apache.karaf.features.ConfigFileInfo; import org.apache.karaf.features.Dependency; import org.apache.karaf.features.Feature; @@ -437,6 +438,20 @@ public class FeaturesServiceImpl implements FeaturesService, FrameworkListener { InstallationState s = new InstallationState(); try { doInstallFeature(s, f, verbose); + doInstallFeatureConditionals(s, f, verbose); + state.bundleInfos.putAll(s.bundleInfos); + state.bundles.addAll(s.bundles); + state.features.putAll(s.features); + state.installed.addAll(s.installed); + + //Check if current feature satisfies the conditionals of existing features + for (Feature installedFeautre : listInstalledFeatures()) { + for (Conditional conditional : installedFeautre.getConditional()) { + if (dependenciesSatisfied(conditional.getCondition(), state)) { + doInstallFeatureConditionals(s, installedFeautre, verbose); + } + } + } state.bundleInfos.putAll(s.bundleInfos); state.bundles.addAll(s.bundles); state.features.putAll(s.features); @@ -481,6 +496,7 @@ public class FeaturesServiceImpl implements FeaturesService, FrameworkListener { } // Start all bundles for (Bundle b : state.bundles) { + LOGGER.info("Starting bundle: {}", b.getSymbolicName()); startBundle(state, b); } // Clean up for batch @@ -575,7 +591,7 @@ public class FeaturesServiceImpl implements FeaturesService, FrameworkListener { protected static class InstallationState { final Set installed = new HashSet(); - final List bundles = new ArrayList(); + final Set bundles = new HashSet(); final Map bundleInfos = new HashMap(); final Map> features = new HashMap>(); } @@ -608,34 +624,25 @@ public class FeaturesServiceImpl implements FeaturesService, FrameworkListener { state.features.put(feature, bundles); } - private void installFeatureDependency(Dependency dependency, InstallationState state, boolean verbose) - throws Exception { - VersionRange range = org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION.equals(dependency.getVersion()) - ? VersionRange.ANY_VERSION : new VersionRange(dependency.getVersion(), true, true); - Feature fi = null; - for (Feature f : installed.keySet()) { - if (f.getName().equals(dependency.getName())) { - Version v = VersionTable.getVersion(f.getVersion()); - if (range.contains(v)) { - if (fi == null || VersionTable.getVersion(fi.getVersion()).compareTo(v) < 0) { - fi = f; - } - } - } - } - if (fi == null) { - Map avail = getFeatures().get(dependency.getName()); - if (avail != null) { - for (Feature f : avail.values()) { - Version v = VersionTable.getVersion(f.getVersion()); - if (range.contains(v)) { - if (fi == null || VersionTable.getVersion(fi.getVersion()).compareTo(v) < 0) { - fi = f; - } - } - } + protected void doInstallFeatureConditionals(InstallationState state, Feature feature, boolean verbose) throws Exception { + InstallationState failure = new InstallationState(); + //Check conditions of the current feature. + for (Conditional conditional : feature.getConditional()) { + + if (dependenciesSatisfied(conditional.getCondition(), state)) { + InstallationState s = new InstallationState(); + doInstallFeature(s, conditional.asFeature(feature.getName(), feature.getVersion()), verbose); + state.bundleInfos.putAll(s.bundleInfos); + state.bundles.addAll(s.bundles); + state.features.putAll(s.features); + state.installed.addAll(s.installed); } } + } + + private void installFeatureDependency(Dependency dependency, InstallationState state, boolean verbose) + throws Exception { + Feature fi = getFeatureForDependency(dependency); if (fi == null) { throw new Exception("No feature named '" + dependency.getName() + "' with version '" + dependency.getVersion() + "' available"); @@ -987,6 +994,12 @@ public class FeaturesServiceImpl implements FeaturesService, FrameworkListener { // and remove all those who will still be in use. // This gives this list of bundles to uninstall. Set bundles = installed.remove(feature); + + //Also remove bundles installed as conditionals + for (Conditional conditional : feature.getConditional()) { + bundles.addAll(installed.remove(conditional.asFeature(feature.getName(),feature.getVersion()))); + } + for (Set b : installed.values()) { bundles.removeAll(b); } @@ -1458,4 +1471,58 @@ public class FeaturesServiceImpl implements FeaturesService, FrameworkListener { } return buffer.toString(); } + + /** + * Returns the {@link Feature} that matches the {@link Dependency}. + * @param dependency + * @return + * @throws Exception + */ + private Feature getFeatureForDependency(Dependency dependency) throws Exception { + VersionRange range = org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION.equals(dependency.getVersion()) + ? VersionRange.ANY_VERSION : new VersionRange(dependency.getVersion(), true, true); + Feature fi = null; + for (Feature f : installed.keySet()) { + if (f.getName().equals(dependency.getName())) { + Version v = VersionTable.getVersion(f.getVersion()); + if (range.contains(v)) { + if (fi == null || VersionTable.getVersion(fi.getVersion()).compareTo(v) < 0) { + fi = f; + } + } + } + } + if (fi == null) { + Map avail = getFeatures().get(dependency.getName()); + if (avail != null) { + for (Feature f : avail.values()) { + Version v = VersionTable.getVersion(f.getVersion()); + if (range.contains(v)) { + if (fi == null || VersionTable.getVersion(fi.getVersion()).compareTo(v) < 0) { + fi = f; + } + } + } + } + } + return fi; + } + + /** + * Estimates if the {@link List} of {@link Dependency} is satisfied. + * The method will look into {@link Feature}s that are already installed or now being installed (if {@link InstallationState} is provided (not null)). + * @param dependencies + * @param state + * @return + */ + private boolean dependenciesSatisfied(List dependencies, InstallationState state) throws Exception { + boolean satisfied = true; + for (Dependency dep : dependencies) { + Feature f = getFeatureForDependency(dep); + if (f != null && !isInstalled(f) && (state != null && !state.features.keySet().contains(f))) { + satisfied = false; + } + } + return satisfied; + } } diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java index 3586409..418b808 100644 Index: features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java =================================================================== --- features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java +++ features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java @@ -103,3 +103,4 @@ public class RepositoryImpl implements Repository { } } + diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Conditional.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Conditional.java index 0932ddb..c862813 100644 Index: features/core/src/main/java/org/apache/karaf/features/internal/model/Conditional.java =================================================================== --- features/core/src/main/java/org/apache/karaf/features/internal/model/Conditional.java +++ features/core/src/main/java/org/apache/karaf/features/internal/model/Conditional.java @@ -1,10 +1,13 @@ package org.apache.karaf.features.internal.model; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlType; -import org.apache.karaf.features.*; import org.apache.karaf.features.Feature; @XmlAccessorType(XmlAccessType.FIELD) @@ -17,21 +20,37 @@ import org.apache.karaf.features.Feature; }) public class Conditional extends Content implements org.apache.karaf.features.Conditional { - @XmlAttribute - protected String condition; + @XmlElement(name = "condition") + protected List condition; - public String getCondition() { + public List getCondition() { + if (condition == null) { + this.condition = new ArrayList(); + } return condition; } @Override public Feature asFeature(String name, String version) { - String conditionName = name+"-condition-"+condition.replaceAll("[^A-Za-z0-9 ]", "_"); + String conditionName = name + "-condition-" + getConditionId().replaceAll("[^A-Za-z0-9 ]", "_"); org.apache.karaf.features.internal.model.Feature f = new org.apache.karaf.features.internal.model.Feature(conditionName, version); f.getBundle().addAll(getBundle()); f.getConfig().addAll(getConfig()); f.getConfigfile().addAll(getConfigfile()); - f.getDependencies().addAll(getDependencies()); + f.getFeature().addAll(getFeature()); return f; } + + private String getConditionId() { + StringBuffer sb = new StringBuffer(); + Iterator di = getCondition().iterator(); + while (di.hasNext()) { + Dependency dependency = di.next(); + sb.append(dependency.getName() + "_" + dependency.getVersion()); + if (di.hasNext()) { + sb.append("_"); + } + } + return sb.toString(); + } } diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Content.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Content.java index 1e0891d..5653dcf 100644 Index: features/core/src/main/java/org/apache/karaf/features/internal/model/Content.java =================================================================== --- features/core/src/main/java/org/apache/karaf/features/internal/model/Content.java +++ features/core/src/main/java/org/apache/karaf/features/internal/model/Content.java @@ -11,11 +11,7 @@ import java.util.Map; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; -import javax.xml.bind.annotation.XmlType; import org.apache.karaf.features.BundleInfo; import org.apache.karaf.features.ConfigFileInfo; 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 bc7f1d2..a9786d7 100644 Index: features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java =================================================================== --- features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java +++ features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java @@ -19,14 +19,9 @@ package org.apache.karaf.features.internal.model; -import java.io.IOException; -import java.io.StringReader; import java.util.ArrayList; -import java.util.Collections; import java.util.Enumeration; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -36,9 +31,6 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlType; -import org.apache.karaf.features.BundleInfo; -import org.apache.karaf.features.ConfigFileInfo; - /** * @@ -77,18 +69,15 @@ import org.apache.karaf.features.ConfigFileInfo; "config", "configfile", "feature", - "bundle" + "bundle", + "conditional" }) -public class Feature implements org.apache.karaf.features.Feature { +public class Feature extends Content implements org.apache.karaf.features.Feature { public static String SPLIT_FOR_NAME_AND_VERSION = "_split_for_name_and_version_"; public static String DEFAULT_VERSION = "0.0.0"; protected String details; - protected List config; - protected List configfile; - protected List feature; - protected List bundle; @XmlAttribute(required = true) protected String name; @XmlAttribute @@ -103,6 +92,7 @@ public class Feature implements org.apache.karaf.features.Feature { protected Integer startLevel; @XmlAttribute protected String region; + protected List conditional; public Feature() { } @@ -130,121 +120,6 @@ public class Feature implements org.apache.karaf.features.Feature { } - /** - * Gets the value of the config property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the config property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getConfig().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link Config } - * - * - */ - public List getConfig() { - if (config == null) { - config = new ArrayList(); - } - return this.config; - } - - /** - * Gets the value of the configfile property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the configfile property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getConfigfile().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link ConfigFile } - * - * - */ - public List getConfigfile() { - if (configfile == null) { - configfile = new ArrayList(); - } - return this.configfile; - } - - /** - * Gets the value of the feature property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the feature property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getFeature().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link Dependency } - * - * - */ - public List getFeature() { - if (feature == null) { - feature = new ArrayList(); - } - return this.feature; - } - - /** - * Gets the value of the bundle property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the bundle property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getBundle().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link Bundle } - * - * - */ - public List getBundle() { - if (bundle == null) { - bundle = new ArrayList(); - } - return this.bundle; - } public String getId() { return getName() + "-" + getVersion(); @@ -362,39 +237,6 @@ public class Feature implements org.apache.karaf.features.Feature { this.install = install; } - public List getDependencies() { - return Collections.unmodifiableList(getFeature()); - } - - public List getBundles() { - return Collections.unmodifiableList(getBundle()); - } - - public Map> getConfigurations() { - Map> result = new HashMap>(); - for (Config config: getConfig()) { - String name = config.getName(); - StringReader propStream = new StringReader(config.getValue()); - Properties props = new Properties(); - try { - props.load(propStream); - } catch (IOException e) { - //ignore?? - } - interpolation(props); - Map propMap = new HashMap(); - for (Map.Entry entry: props.entrySet()) { - propMap.put((String)entry.getKey(), (String)entry.getValue()); - } - result.put(name, propMap); - } - return result; - } - - public List getConfigurationFiles() { - return Collections.unmodifiableList(getConfigfile()); - } - /** * Sets the value of the resolver property. * @@ -440,6 +282,33 @@ public class Feature implements org.apache.karaf.features.Feature { this.region = region; } + /** + * Gets the value of the conditional property. + *

+ *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the feature property. + *

+ *

+ * For example, to add a new item, do as follows: + *

+     *    getConditionals().add(newItem);
+     * 
+ *

+ *

+ *

+ * Objects of the following type(s) are allowed in the list + * {@link Dependency } + */ + public List getConditional() { + if (conditional == null) { + conditional = new ArrayList(); + } + return this.conditional; + } + public String toString() { String ret = getName() + SPLIT_FOR_NAME_AND_VERSION + getVersion(); return ret; diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java index 8afb4ec..a48b1e7 100644 Index: features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java =================================================================== --- features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java +++ features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java @@ -123,11 +123,11 @@ public class JaxbUtil { /** * Provides an empty inputsource for the entity resolver. - * Converts all elements with empty namespace to the features namespace to make old feature files + * Converts all elements to the features namespace to make old feature files * compatible to the new format */ public static class NoSourceAndNamespaceFilter extends XMLFilterImpl { - private static final String FEATURES_NAMESPACE = "http://karaf.apache.org/xmlns/features/v1.0.0"; + private static final String FEATURES_NAMESPACE = "http://karaf.apache.org/xmlns/features/v1.2.0"; private static final InputSource EMPTY_INPUT_SOURCE = new InputSource(new ByteArrayInputStream(new byte[0])); public NoSourceAndNamespaceFilter(XMLReader xmlReader) { @@ -141,22 +141,12 @@ public class JaxbUtil { @Override public void startElement(String uri, String localName, String qname, Attributes atts) throws SAXException { - if ("".equals(uri)) { - super.startElement(FEATURES_NAMESPACE, localName, qname, atts); - } else { - super.startElement(uri, localName, qname, atts); - } + super.startElement(FEATURES_NAMESPACE, localName, qname, atts); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { - if ("".equals(uri)) { - super.endElement(FEATURES_NAMESPACE, localName, qName); - } else { - super.endElement(uri, localName, qName); - } + super.endElement(FEATURES_NAMESPACE, localName, qName); } } - - -} +} \ No newline at end of file diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/package-info.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/package-info.java index 09c0bfe8..f994bc1 100644 Index: features/core/src/main/java/org/apache/karaf/features/internal/model/package-info.java =================================================================== --- features/core/src/main/java/org/apache/karaf/features/internal/model/package-info.java +++ features/core/src/main/java/org/apache/karaf/features/internal/model/package-info.java @@ -17,5 +17,5 @@ * under the License. */ -@javax.xml.bind.annotation.XmlSchema(namespace = "http://karaf.apache.org/xmlns/features/v1.0.0", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +@javax.xml.bind.annotation.XmlSchema(namespace = "http://karaf.apache.org/xmlns/features/v1.2.0", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) package org.apache.karaf.features.internal.model; diff --git a/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.2.0.xsd b/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.2.0.xsd index 1d7a187..dbc4bfa 100644 Index: features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.2.0.xsd =================================================================== --- features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.2.0.xsd +++ features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.2.0.xsd @@ -117,8 +117,8 @@ Definition of the Conditional. + - diff --git a/features/core/src/test/java/org/apache/karaf/features/ConditionalTest.java b/features/core/src/test/java/org/apache/karaf/features/ConditionalTest.java index cbdf4a1..21bd919 100644 Index: features/core/src/test/java/org/apache/karaf/features/ConditionalTest.java =================================================================== --- features/core/src/test/java/org/apache/karaf/features/ConditionalTest.java +++ features/core/src/test/java/org/apache/karaf/features/ConditionalTest.java @@ -35,7 +35,11 @@ public class ConditionalTest extends TestCase { assertEquals(1,feature.getConditional().size()); Conditional conditional = feature.getConditional().get(0); - assertEquals(conditional.getCondition(),"http"); + assertNotNull(conditional.getCondition()); + assertEquals(1,conditional.getCondition().size()); + Dependency dependency = conditional.getCondition().get(0); + assertNotNull(dependency); + assertEquals(dependency.getName(),"http"); assertNotNull(conditional.getBundles()); assertEquals(1, feature.getConditional().get(0).getBundles().size()); diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesValidationTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesValidationTest.java index 557bfff..09f9c62 100644 Index: features/core/src/test/java/org/apache/karaf/features/internal/FeaturesValidationTest.java =================================================================== --- features/core/src/test/java/org/apache/karaf/features/internal/FeaturesValidationTest.java +++ features/core/src/test/java/org/apache/karaf/features/internal/FeaturesValidationTest.java @@ -44,6 +44,11 @@ public class FeaturesValidationTest { @Test public void testNs12() throws Exception { + FeatureValidationUtil.validate(getClass().getResource("f06.xml").toURI()); + } + + @Test + public void testNs13() throws Exception { try { FeatureValidationUtil.validate(getClass().getResource("f05.xml").toURI()); fail("Validation should have failed"); diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/f06.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/f06.xml index bdb82f9..496cbdb 100644 Index: features/core/src/test/resources/org/apache/karaf/features/internal/f06.xml =================================================================== --- features/core/src/test/resources/org/apache/karaf/features/internal/f06.xml +++ features/core/src/test/resources/org/apache/karaf/features/internal/f06.xml @@ -23,7 +23,8 @@ mvn:org.springframework/spring-aop/2.5.6.SEC02 mvn:org.springframework/spring-context/2.5.6.SEC02 mvn:org.springframework/spring-context-support/2.5.6.SEC02 - + + http mvn:org.springframework/spring-web/2.5.6.SEC02 diff --git a/features/core/src/test/resources/org/apache/karaf/features/repo1.xml b/features/core/src/test/resources/org/apache/karaf/features/repo1.xml index 2b2c3a8..641ef12 100644 Index: features/core/src/test/resources/org/apache/karaf/features/repo1.xml =================================================================== --- features/core/src/test/resources/org/apache/karaf/features/repo1.xml +++ features/core/src/test/resources/org/apache/karaf/features/repo1.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - + urn:r1 diff --git a/itests/tests/src/test/java/org/apache/karaf/shell/itests/ConditionalFeaturesTest.java b/itests/tests/src/test/java/org/apache/karaf/shell/itests/ConditionalFeaturesTest.java index 4fb47c2..c17a255 100644 Index: itests/tests/src/test/java/org/apache/karaf/shell/itests/ConditionalFeaturesTest.java =================================================================== --- itests/tests/src/test/java/org/apache/karaf/shell/itests/ConditionalFeaturesTest.java +++ itests/tests/src/test/java/org/apache/karaf/shell/itests/ConditionalFeaturesTest.java @@ -16,15 +16,11 @@ */ package org.apache.karaf.shell.itests; -import static org.apache.karaf.tooling.exam.options.KarafDistributionOption.editConfigurationFileExtend; -import static org.apache.karaf.tooling.exam.options.KarafDistributionOption.karafDistributionConfiguration; -import static org.junit.Assert.assertNotNull; -import static org.ops4j.pax.exam.CoreOptions.maven; - import javax.inject.Inject; - +import junit.framework.Assert; import org.apache.felix.service.command.CommandProcessor; import org.apache.felix.service.command.CommandSession; +import org.apache.karaf.features.FeaturesService; import org.apache.karaf.tooling.exam.options.configs.FeaturesCfg; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,26 +30,28 @@ import org.ops4j.pax.exam.junit.Configuration; import org.ops4j.pax.exam.junit.JUnit4TestRunner; import org.ops4j.pax.exam.junit.ProbeBuilder; import org.ops4j.pax.exam.util.Filter; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.service.blueprint.container.BlueprintContainer; + +import static org.apache.karaf.tooling.exam.options.KarafDistributionOption.editConfigurationFileExtend; +import static org.apache.karaf.tooling.exam.options.KarafDistributionOption.karafDistributionConfiguration; +import static org.junit.Assert.assertNotNull; +import static org.ops4j.pax.exam.CoreOptions.maven; + @RunWith(JUnit4TestRunner.class) public class ConditionalFeaturesTest { @Inject - @Filter(value = "osgi.blueprint.container.symbolicname=org.apache.karaf.shell.obr", timeout = 20000) - private BlueprintContainer obrService; - - @Inject - @Filter(value = "osgi.blueprint.container.symbolicname=org.apache.karaf.wrapper.core", timeout = 20000) - private BlueprintContainer wrapperService; + private CommandProcessor cp; @Inject - @Filter(value = "osgi.blueprint.container.symbolicname=org.apache.karaf.wrapper.command", timeout = 20000) - private BlueprintContainer wrapperCommandsService; + private FeaturesService featuresService; @Inject - private CommandProcessor cp; + private BundleContext bundleContext; @ProbeBuilder public TestProbeBuilder probeConfiguration(TestProbeBuilder probe) { @@ -66,19 +64,49 @@ public class ConditionalFeaturesTest { return new Option[]{ karafDistributionConfiguration().frameworkUrl( maven().groupId("org.apache.karaf").artifactId("apache-karaf").type("zip") - .versionAsInProject()), editConfigurationFileExtend(FeaturesCfg.BOOT, ",obr,wrapper") }; + .versionAsInProject())}; } @Test - public void testFeatures() throws Exception { - // Make sure the command services are available - assertNotNull(obrService); - assertNotNull(wrapperService); - // Run some commands to make sure they are installed properly - CommandSession cs = cp.createSession(System.in, System.out, System.err); - cs.execute("obr:url-list"); - cs.execute("wrapper:install --help"); - cs.close(); + public void testScr() throws Exception { + //Remove management and install scr + featuresService.uninstallFeature("management"); + featuresService.installFeature("scr"); + Assert.assertFalse(isBundleInstalled("org.apache.karaf.scr.management")); + + //Add management back + featuresService.installFeature("management"); + Assert.assertTrue(isBundleInstalled("org.apache.karaf.scr.management")); } + @Test + public void testWebconsole() throws Exception { + featuresService.installFeature("webconsole"); + + Assert.assertTrue(isBundleInstalled("org.apache.karaf.webconsole.features")); + Assert.assertTrue(isBundleInstalled("org.apache.karaf.webconsole.instance")); + Assert.assertTrue(isBundleInstalled("org.apache.karaf.webconsole.gogo")); + Assert.assertTrue(isBundleInstalled("org.apache.karaf.webconsole.http")); + + Assert.assertFalse(isBundleInstalled("org.apache.felix.webconsole.plugins.event")); + + //Add eventadmin + try { + featuresService.installFeature("eventadmin"); + } catch (Exception ex) { + //ignore as the eventadmin activator might throw an error. + } + Assert.assertTrue(isBundleInstalled("org.apache.felix.webconsole.plugins.event")); + } + + + + private boolean isBundleInstalled(String symbolicName) { + for (Bundle bundle : bundleContext.getBundles()) { + if (bundle.getSymbolicName().equals(symbolicName)) { + return true; + } + } + return false; + } } diff --git a/tooling/karaf-maven-plugin/src/test/java/org/apache/karaf/tooling/features/GenerateDescriptorMojoTest.java b/tooling/karaf-maven-plugin/src/test/java/org/apache/karaf/tooling/features/GenerateDescriptorMojoTest.java index e0bc4cd..5cff29c 100644 Index: tooling/karaf-maven-plugin/src/test/java/org/apache/karaf/tooling/features/GenerateDescriptorMojoTest.java =================================================================== --- tooling/karaf-maven-plugin/src/test/java/org/apache/karaf/tooling/features/GenerateDescriptorMojoTest.java +++ tooling/karaf-maven-plugin/src/test/java/org/apache/karaf/tooling/features/GenerateDescriptorMojoTest.java @@ -46,6 +46,6 @@ public class GenerateDescriptorMojoTest { JaxbUtil.marshal(featuresRoot, baos); String s = new String(baos.toByteArray()); assert s.indexOf("repository") > -1; - assert s.indexOf("http://karaf.apache.org/xmlns/features/v1.0.0") > -1; + assert s.indexOf("http://karaf.apache.org/xmlns/features/v1.2.0") > -1; } -} +} \ No newline at end of file diff --git a/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java b/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java index 146f30b..2f3a5d9 100644 Index: webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java =================================================================== --- webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java +++ webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import org.apache.karaf.features.BundleInfo; +import org.apache.karaf.features.Conditional; import org.apache.karaf.features.ConfigFileInfo; import org.apache.karaf.features.Dependency; import org.apache.karaf.features.Feature; @@ -81,7 +82,12 @@ public class ExtendedFeature implements Feature public List getConfigurationFiles() { return this.feature.getConfigurationFiles(); } - + + @Override + public List getConditional() { + return this.feature.getConditional(); + } + public List getDependencies() { return this.feature.getDependencies();