Repository: karaf Updated Branches: refs/heads/karaf-2.x 9da61ba12 -> 49a2c2bf4
[KARAF-2453] - Using features to extend existing configuration Project: http://git-wip-us.apache.org/repos/asf/karaf/repo Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/49a2c2bf Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/49a2c2bf Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/49a2c2bf Branch: refs/heads/karaf-2.x Commit: 49a2c2bf431c7fd375994ff05b974a5154d0e4f6 Parents: 9da61ba Author: anierbeck <[email protected]> Authored: Sat Aug 23 20:32:44 2014 +0200 Committer: anierbeck <[email protected]> Committed: Sat Aug 23 20:32:44 2014 +0200 ---------------------------------------------------------------------- .../features/command/InfoFeatureCommand.java | 7 +- .../org/apache/karaf/features/Conditional.java | 2 +- .../org/apache/karaf/features/ConfigInfo.java | 30 +++ .../java/org/apache/karaf/features/Feature.java | 3 +- .../karaf/features/FeaturesNamespaces.java | 4 +- .../features/internal/ConditionalImpl.java | 2 +- .../karaf/features/internal/ConfigInfoImpl.java | 61 +++++ .../karaf/features/internal/ContentImpl.java | 9 +- .../internal/FeatureValidationUtil.java | 2 + .../features/internal/FeaturesServiceImpl.java | 59 ++++- .../karaf/features/internal/RepositoryImpl.java | 4 +- .../karaf/features/karaf-features-1.2.1.xsd | 262 +++++++++++++++++++ .../apache/karaf/features/RepositoryTest.java | 54 +++- .../org/apache/karaf/features/repo3.xml | 35 +++ .../features/management/codec/JmxFeature.java | 14 +- .../webconsole/features/ExtendedFeature.java | 4 +- 16 files changed, 514 insertions(+), 38 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/command/src/main/java/org/apache/karaf/features/command/InfoFeatureCommand.java ---------------------------------------------------------------------- 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 1f6b32d..fe4090a 100644 --- 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 @@ -27,6 +27,7 @@ import org.apache.felix.gogo.commands.Option; import org.apache.karaf.features.BundleInfo; import org.apache.karaf.features.Conditional; import org.apache.karaf.features.ConfigFileInfo; +import org.apache.karaf.features.ConfigInfo; import org.apache.karaf.features.Feature; import org.apache.karaf.features.FeaturesService; @@ -153,13 +154,13 @@ public class InfoFeatureCommand extends FeaturesCommandSupport { } private void displayConfigInformation(Feature feature, String contentType) { - Map<String, Map<String, String>> configurations = feature.getConfigurations(); + List<ConfigInfo> configurations = feature.getConfigurations(); if (configurations.isEmpty()) { System.out.println(contentType + " has no configuration"); } else { System.out.println(contentType + " configuration:"); - for (String name : configurations.keySet()) { - System.out.println(" " + name); + for (ConfigInfo configInf : configurations) { + System.out.println(" " + configInf.getName()); } } } http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/core/src/main/java/org/apache/karaf/features/Conditional.java ---------------------------------------------------------------------- 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 5a44e00..809bd28 100644 --- a/features/core/src/main/java/org/apache/karaf/features/Conditional.java +++ b/features/core/src/main/java/org/apache/karaf/features/Conditional.java @@ -27,7 +27,7 @@ public interface Conditional { List<BundleInfo> getBundles(); - Map<String, Map<String, String>> getConfigurations(); + List<ConfigInfo> getConfigurations(); List<ConfigFileInfo> getConfigurationFiles(); http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/core/src/main/java/org/apache/karaf/features/ConfigInfo.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/ConfigInfo.java b/features/core/src/main/java/org/apache/karaf/features/ConfigInfo.java new file mode 100644 index 0000000..b801f78 --- /dev/null +++ b/features/core/src/main/java/org/apache/karaf/features/ConfigInfo.java @@ -0,0 +1,30 @@ +/* + * 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.features; + +import java.util.Hashtable; +import java.util.Map; + +public interface ConfigInfo { + + String getName(); + + Map<String, String> getProperties(); + + boolean isAppend(); + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/core/src/main/java/org/apache/karaf/features/Feature.java ---------------------------------------------------------------------- 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 37829da..0e3d068 100644 --- a/features/core/src/main/java/org/apache/karaf/features/Feature.java +++ b/features/core/src/main/java/org/apache/karaf/features/Feature.java @@ -17,7 +17,6 @@ package org.apache.karaf.features; import java.util.List; -import java.util.Map; /** * A feature is a list of bundles associated identified by its name. @@ -44,7 +43,7 @@ public interface Feature { List<BundleInfo> getBundles(); - Map<String, Map<String, String>> getConfigurations(); + List<ConfigInfo> getConfigurations(); List<ConfigFileInfo> getConfigurationFiles(); http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java b/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java index 921c832..f8cd319 100644 --- a/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java +++ b/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java @@ -26,13 +26,15 @@ public interface FeaturesNamespaces { String URI_1_0_0 = "http://karaf.apache.org/xmlns/features/v1.0.0"; String URI_1_1_0 = "http://karaf.apache.org/xmlns/features/v1.1.0"; String URI_1_2_0 = "http://karaf.apache.org/xmlns/features/v1.2.0"; + String URI_1_2_1 = "http://karaf.apache.org/xmlns/features/v1.2.1"; - String URI_CURRENT = URI_1_2_0; + String URI_CURRENT = URI_1_2_1; QName FEATURES_0_0_0 = new QName("features"); QName FEATURES_1_0_0 = new QName(URI_1_0_0, "features"); QName FEATURES_1_1_0 = new QName(URI_1_1_0, "features"); QName FEATURES_1_2_0 = new QName(URI_1_2_0, "features"); + QName FEATURES_1_2_1 = new QName(URI_1_2_1, "features"); QName FEATURES_CURRENT = FEATURES_1_1_0; http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/core/src/main/java/org/apache/karaf/features/internal/ConditionalImpl.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/ConditionalImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/ConditionalImpl.java index 78637aa..1b994ce 100644 --- a/features/core/src/main/java/org/apache/karaf/features/internal/ConditionalImpl.java +++ b/features/core/src/main/java/org/apache/karaf/features/internal/ConditionalImpl.java @@ -39,7 +39,7 @@ public class ConditionalImpl extends ContentImpl implements Conditional { String conditionName = name + "-condition-" + getConditionId().replaceAll("[^A-Za-z0-9 ]", "_"); FeatureImpl f = new FeatureImpl(conditionName, version); f.getBundles().addAll(getBundles()); - f.getConfigurations().putAll(getConfigurations()); + f.getConfigurations().addAll(getConfigurations()); f.getConfigurationFiles().addAll(getConfigurationFiles()); f.getDependencies().addAll(getDependencies()); return f; http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/core/src/main/java/org/apache/karaf/features/internal/ConfigInfoImpl.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/ConfigInfoImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/ConfigInfoImpl.java new file mode 100644 index 0000000..a347506 --- /dev/null +++ b/features/core/src/main/java/org/apache/karaf/features/internal/ConfigInfoImpl.java @@ -0,0 +1,61 @@ +/* + * 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.features.internal; + +import java.util.Hashtable; +import java.util.Map; + +import org.apache.karaf.features.ConfigInfo; + +public class ConfigInfoImpl implements ConfigInfo { + + private String name; + private Map<String, String> properties; + private boolean append; + + public ConfigInfoImpl(String name, Map<String, String> hashtable, + String append) { + this.name = name; + this.properties = hashtable; + this.append = Boolean.parseBoolean(append); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map<String, String> getProperties() { + return properties; + } + + public void setProperties(Hashtable<String, String> properties) { + this.properties = properties; + } + + public boolean isAppend() { + return append; + } + + public void setAppend(boolean append) { + this.append = append; + } + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/core/src/main/java/org/apache/karaf/features/internal/ContentImpl.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/ContentImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/ContentImpl.java index ac0bb19..a89cdf5 100644 --- a/features/core/src/main/java/org/apache/karaf/features/internal/ContentImpl.java +++ b/features/core/src/main/java/org/apache/karaf/features/internal/ContentImpl.java @@ -23,13 +23,14 @@ import java.util.Map; import org.apache.karaf.features.BundleInfo; import org.apache.karaf.features.ConfigFileInfo; +import org.apache.karaf.features.ConfigInfo; import org.apache.karaf.features.Feature; public class ContentImpl { private List<Feature> dependencies = new ArrayList<Feature>(); private List<BundleInfo> bundles = new ArrayList<BundleInfo>(); - private Map<String, Map<String,String>> configs = new HashMap<String, Map<String,String>>(); + private List<ConfigInfo> configs = new ArrayList<ConfigInfo>(); private List<ConfigFileInfo> configurationFiles = new ArrayList<ConfigFileInfo>(); public List<Feature> getDependencies() { @@ -40,7 +41,7 @@ public class ContentImpl { return bundles; } - public Map<String, Map<String, String>> getConfigurations() { + public List<ConfigInfo> getConfigurations() { return configs; } @@ -56,8 +57,8 @@ public class ContentImpl { bundles.add(bundle); } - public void addConfig(String name, Map<String,String> properties) { - configs.put(name, properties); + public void addConfig(ConfigInfo configInfo) { + configs.add(configInfo); } public void addConfigurationFile(ConfigFileInfo configurationFileInfo) { http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java ---------------------------------------------------------------------- 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 09e6342..d00cabb 100644 --- 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 @@ -68,6 +68,8 @@ public class FeatureValidationUtil { validate(doc, "/org/apache/karaf/features/karaf-features-1.1.0.xsd"); } else if (FeaturesNamespaces.FEATURES_1_2_0.equals(name)) { validate(doc, "/org/apache/karaf/features/karaf-features-1.2.0.xsd"); + } else if (FeaturesNamespaces.FEATURES_1_2_1.equals(name)) { + validate(doc, "/org/apache/karaf/features/karaf-features-1.2.1.xsd"); } else { throw new IllegalArgumentException("Unrecognized root element: " + name); http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java ---------------------------------------------------------------------- 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 0c927a7..caf23c8 100644 --- 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 @@ -43,6 +43,7 @@ import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.TreeSet; @@ -54,6 +55,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; +import org.apache.felix.utils.collections.MapToDictionary; import org.apache.felix.utils.manifest.Clause; import org.apache.felix.utils.manifest.Parser; import org.apache.felix.utils.version.VersionRange; @@ -61,6 +63,7 @@ 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.ConfigInfo; import org.apache.karaf.features.Feature; import org.apache.karaf.features.FeatureEvent; import org.apache.karaf.features.FeaturesListener; @@ -600,19 +603,41 @@ public class FeaturesServiceImpl implements FeaturesService, FrameworkListener { } } } - for (String config : feature.getConfigurations().keySet()) { - Dictionary<String, String> props = new Hashtable<String, String>(feature.getConfigurations().get(config)); - String[] pid = parsePid(config); - Configuration cfg = findExistingConfiguration(configAdmin, pid[0], pid[1]); - if (cfg == null) { - cfg = createConfiguration(configAdmin, pid[0], pid[1]); - String key = createConfigurationKey(pid[0], pid[1]); - props.put(CONFIG_KEY, key); - if (cfg.getBundleLocation() != null) { - cfg.setBundleLocation(null); - } - cfg.update(props); - } + for (ConfigInfo config : feature.getConfigurations()) { + String name = config.getName(); + Map<String, String> props = config.getProperties(); + + String[] pid = parsePid(config.getName()); + Configuration cfg = findExistingConfiguration(configAdmin, pid[0], + pid[1]); + if (cfg == null) { + + Dictionary<String, String> cfgProps = convertToDict(config + .getProperties()); + + cfg = createConfiguration(configAdmin, pid[0], pid[1]); + String key = createConfigurationKey(pid[0], pid[1]); + cfgProps.put(CONFIG_KEY, key); + if (cfg.getBundleLocation() != null) { + cfg.setBundleLocation(null); + } + cfg.update(cfgProps); + } else if (config.isAppend()) { + Dictionary<String, Object> properties = cfg.getProperties(); + for (Enumeration<String> propKeys = properties.keys(); propKeys + .hasMoreElements();) { + String key = propKeys.nextElement(); + // remove existing entry, since it's about appending. + if (props.containsKey(key)) { + props.remove(key); + } + } + if (props.size() > 0) { + // convert props to dictionary + Dictionary<String, String> cfgProps = convertToDict(props); + cfg.update(cfgProps); + } + } } for (ConfigFileInfo configFile : feature.getConfigurationFiles()) { installConfigurationFile(configFile.getLocation(), @@ -643,6 +668,14 @@ public class FeaturesServiceImpl implements FeaturesService, FrameworkListener { } + private Dictionary<String, String> convertToDict(Map<String, String> props) { + Dictionary<String, String> cfgProps = new Hashtable<String, String>(); + for (Entry<String, String> property : props.entrySet()) { + cfgProps.put(property.getKey(), property.getValue()); + } + return cfgProps; + } + private String createConfigurationKey(String pid, String factoryPid) { return factoryPid == null ? pid : pid + "-" + factoryPid; } http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java ---------------------------------------------------------------------- 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 c0fc354..a730bb3 100644 --- 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 @@ -214,7 +214,9 @@ public class RepositoryImpl implements Repository { Element c = (Element) configNodes.item(j); if (c.getParentNode() != e) continue; String cfgName = c.getAttribute("name"); + String append = c.getAttribute("append"); String data = c.getTextContent(); + Properties properties = new Properties(); properties.load(new ByteArrayInputStream(data.getBytes())); interpolation(properties); @@ -223,7 +225,7 @@ public class RepositoryImpl implements Repository { String n = key.toString(); hashtable.put(n, properties.getProperty(n)); } - content.addConfig(cfgName, hashtable); + content.addConfig(new ConfigInfoImpl(cfgName, hashtable, append)); } NodeList configurationFiles = e.getElementsByTagName("configfile"); for (int j = 0; j < configurationFiles.getLength(); j++) { http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.2.1.xsd ---------------------------------------------------------------------- diff --git a/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.2.1.xsd b/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.2.1.xsd new file mode 100644 index 0000000..5044073 --- /dev/null +++ b/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.2.1.xsd @@ -0,0 +1,262 @@ +<?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. + +--> +<xs:schema elementFormDefault="qualified" + targetNamespace="http://karaf.apache.org/xmlns/features/v1.2.1" + xmlns:tns="http://karaf.apache.org/xmlns/features/v1.2.1" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + + <xs:annotation> + <xs:documentation><![CDATA[ +Karaf features mechanism. For documentation please visit the +<a href="http://karaf.apache.org/">Karaf website</a>. + ]]></xs:documentation> + </xs:annotation> + + <xs:complexType name="features"> + <xs:annotation> + <xs:documentation><![CDATA[ +Root element of Feature definition. It contains an required attribute for +designating from which repository this feature should be loaded. The Karaf +shell will show the repository name when displaying information about the feature. + ]]></xs:documentation> + </xs:annotation> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="repository" type="xs:anyURI"> + <xs:annotation> + <xs:documentation><![CDATA[ +Additional repositories where dependencies are stored. + ]]></xs:documentation> + </xs:annotation> + </xs:element> + <xs:element name="feature" type="tns:feature"> + <xs:annotation> + <xs:documentation><![CDATA[ +Feature definition. + ]]></xs:documentation> + </xs:annotation> + </xs:element> + </xs:choice> + <xs:attribute name="name" type="xs:string" use="required"/> + </xs:complexType> + + <xs:complexType name="feature"> + <xs:annotation> + <xs:documentation><![CDATA[ +Definition of the Feature. + ]]></xs:documentation> + </xs:annotation> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="details" minOccurs="0" type="xs:string"> + <xs:annotation> + <xs:documentation><![CDATA[ +The help text shown for this feature when using feature:info console command. + ]]> + </xs:documentation> + </xs:annotation> + </xs:element> + <xs:element name="config" type="tns:config" /> + <xs:element name="configfile" type="tns:configFile" /> + <xs:element name="feature" type="tns:dependency" /> + <xs:element name="bundle" type="tns:bundle" /> + <xs:element name="conditional" type="tns:conditional" /> + </xs:choice> + <xs:attribute name="name" type="tns:featureName" use="required" /> + <xs:attribute name="version" type="xs:string" default="0.0.0" /> + <xs:attribute name="description" type="xs:string" /> + <xs:attribute name="resolver" type="tns:resolver"> + <xs:annotation> + <xs:documentation><![CDATA[ +Optional alternative resolver to use for determining the list of bundles to install for a given feature. + ]]> + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="install" type="tns:install"> + <xs:annotation> + <xs:documentation><![CDATA[ +Marks if the feaute will be automatically started when thrown to the deploy folder. + ]]> + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="start-level" type="xs:int"> + <xs:annotation> + <xs:documentation><![CDATA[ +Set this attribute to have an OSGi start level for this feature different +from the default start level defined in Karaf's config.properties. + ]]> + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + + <xs:complexType name="conditional"> + <xs:annotation> + <xs:documentation><![CDATA[ +Definition of the Conditional. + ]]></xs:documentation> + </xs:annotation> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="config" type="tns:config" /> + <xs:element name="configfile" type="tns:configFile" /> + <xs:element name="feature" type="tns:dependency" /> + <xs:element name="bundle" type="tns:bundle" /> + <xs:element name="condition" type="tns:dependency" minOccurs="0" maxOccurs="1" /> + </xs:choice> + </xs:complexType> + + + <xs:complexType name="bundle"> + <xs:annotation> + <xs:documentation><![CDATA[ +Deployable element to install. + ]]></xs:documentation> + </xs:annotation> + <xs:simpleContent> + <xs:extension base="xs:anyURI"> + <xs:attribute name="start-level" type="xs:int"> + <xs:annotation> + <xs:documentation><![CDATA[ +Set this attribute to have an OSGi start level for this bundle different +from the default start level defined in the Karaf's config.properties. + ]]> + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="start" type="xs:boolean" default="true"> + <xs:annotation> + <xs:documentation><![CDATA[ +If false, leaves bundle in resolved state rather than the default active state. + ]]> + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="dependency" type="xs:boolean"> + <xs:annotation> + <xs:documentation><![CDATA[ +Mark this bundle as a dependency for the resolver. + ]]> + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + + <xs:complexType name="dependency"> + <xs:annotation> + <xs:documentation><![CDATA[ +Dependency of feature. + ]]></xs:documentation> + </xs:annotation> + <xs:simpleContent> + <xs:extension base="tns:featureName"> + <xs:attribute name="version" type="xs:string" default="0.0.0" /> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + + <xs:complexType name="config"> + <xs:annotation> + <xs:documentation><![CDATA[ +Configuration entries which should be created during feature installation. This +configuration may be used with OSGi Configuration Admin. The element content is +read in as a properties file. + ]]></xs:documentation> + </xs:annotation> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="append" type="xs:boolean" use="optional"> + <xs:annotation> + <xs:documentation><![CDATA[ +Optional flag to append unknown values to the configuration. + ]]> + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + + <xs:complexType name="configFile"> + <xs:annotation> + <xs:documentation><![CDATA[ +Additional configuration files which should be created during feature installation. + ]]></xs:documentation> + </xs:annotation> + <xs:simpleContent> + <xs:extension base="xs:anyURI"> + <xs:attribute name="finalname" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation><![CDATA[ +The final destination path and name for the configuration file. + ]]></xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="override" type="xs:boolean"> + <xs:annotation> + <xs:documentation><![CDATA[ +If the configFile already exists at the finalname location, whether or not to replace it. + ]]></xs:documentation> + </xs:annotation> + </xs:attribute> + + </xs:extension> + </xs:simpleContent> + </xs:complexType> + + <xs:simpleType name="featureName"> + <xs:annotation> + <xs:documentation><![CDATA[ +Feature name should be non empty string. + ]]></xs:documentation> + </xs:annotation> + <xs:restriction base="xs:string"> + <xs:minLength value="1" /> + </xs:restriction> + </xs:simpleType> + + <xs:simpleType name="resolver"> + <xs:annotation> + <xs:documentation><![CDATA[ +Resolver to use. Karaf will look for OSGi service which have following properties: +objectClass: org.apache.karaf.features.Resolver +name: the value + ]]></xs:documentation> + </xs:annotation> + <xs:restriction base="xs:string"> + <xs:minLength value="1" /> + </xs:restriction> + </xs:simpleType> + <xs:simpleType name="install"> + <xs:annotation> + <xs:documentation><![CDATA[ +Installation mode. Can be either manual or auto. Specifies whether the feature should be automatically installed when +dropped inside the deploy folder. Note: This attribute doesn't affect feature descriptors that are installed from the +command line or as part of the org.apache.karaf.features.cfg. + ]]></xs:documentation> + </xs:annotation> + <xs:restriction base="xs:string"> + <xs:minLength value="1" /> + </xs:restriction> + </xs:simpleType> + <xs:element name="features" type="tns:features" /> + +</xs:schema> http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/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 2e1c6da..f3312ea 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 @@ -19,8 +19,9 @@ package org.apache.karaf.features; import java.net.URI; import junit.framework.TestCase; -import org.apache.karaf.features.internal.RepositoryImpl; + import org.apache.karaf.features.internal.FeatureImpl; +import org.apache.karaf.features.internal.RepositoryImpl; public class RepositoryTest extends TestCase { @@ -40,9 +41,54 @@ public class RepositoryTest extends TestCase { assertEquals("f1", features[0].getName()); assertNotNull(features[0].getConfigurations()); assertEquals(1, features[0].getConfigurations().size()); - assertNotNull(features[0].getConfigurations().get("c1")); - assertEquals(1, features[0].getConfigurations().get("c1").size()); - assertEquals("v", features[0].getConfigurations().get("c1").get("k")); + assertNotNull(features[0].getConfigurations().get(0).getName()); + assertEquals("c1", features[0].getConfigurations().get(0).getName()); + assertEquals(1, features[0].getConfigurations().get(0).getProperties().size()); + assertEquals("v", features[0].getConfigurations().get(0).getProperties().get("k")); + assertNotNull(features[0].getDependencies()); + assertEquals(0, features[0].getDependencies().size()); + assertNotNull(features[0].getBundles()); + assertEquals(2, features[0].getBundles().size()); + assertEquals("b1", features[0].getBundles().get(0).getLocation()); + assertEquals("b2", features[0].getBundles().get(1).getLocation()); + assertNotNull(features[1]); + assertEquals("f2", features[1].getName()); + assertNotNull(features[1].getConfigurations()); + assertEquals(0, features[1].getConfigurations().size()); + assertNotNull(features[1].getDependencies()); + assertEquals(1, features[1].getDependencies().size()); + assertEquals("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + FeatureImpl.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()); + assertEquals("f3", features[2].getName()); + assertNotNull(features[2].getConfigurationFiles()); + assertEquals(1, features[2].getConfigurationFiles().size()); + assertEquals("cf1", features[2].getConfigurationFiles().get(0).getFinalname()); + assertEquals("cfloc", features[2].getConfigurationFiles().get(0).getLocation()); + } + + + public void testLoadConfigAppend() throws Exception { + RepositoryImpl r = new RepositoryImpl(getClass().getResource("repo3.xml").toURI()); + // Check repo + URI[] repos = r.getRepositories(); + assertNotNull(repos); + assertEquals(1, repos.length); + assertEquals(URI.create("urn:r1"), repos[0]); + // Check features + Feature[] features = r.getFeatures(); + assertNotNull(features); + assertEquals(3, features.length); + assertNotNull(features[0]); + assertEquals("f1", features[0].getName()); + assertNotNull(features[0].getConfigurations()); + assertEquals(1, features[0].getConfigurations().size()); + assertNotNull(features[0].getConfigurations().get(0).getName()); + assertEquals("c1", features[0].getConfigurations().get(0).getName()); + assertEquals(1, features[0].getConfigurations().get(0).getProperties().size()); + assertEquals("v", features[0].getConfigurations().get(0).getProperties().get("k")); + assertTrue(features[0].getConfigurations().get(0).isAppend()); assertNotNull(features[0].getDependencies()); assertEquals(0, features[0].getDependencies().size()); assertNotNull(features[0].getBundles()); http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/core/src/test/resources/org/apache/karaf/features/repo3.xml ---------------------------------------------------------------------- diff --git a/features/core/src/test/resources/org/apache/karaf/features/repo3.xml b/features/core/src/test/resources/org/apache/karaf/features/repo3.xml new file mode 100644 index 0000000..8f06ffa --- /dev/null +++ b/features/core/src/test/resources/org/apache/karaf/features/repo3.xml @@ -0,0 +1,35 @@ +<?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.2.1"> + <repository>urn:r1</repository> + <feature name="f1" region="foo"> + <config name="c1" append="true"> + k=v + </config> + <bundle>b1</bundle> + <bundle>b2</bundle> + </feature> + <feature name="f2"> + <feature>f1</feature> + <bundle>b3</bundle> + </feature> + <feature name="f3"> + <configfile finalname="cf1" override="true">cfloc</configfile> + <bundle>b4</bundle> + </feature> +</features> http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/features/management/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java ---------------------------------------------------------------------- diff --git a/features/management/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java b/features/management/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java index ad682be..12f920b 100644 --- a/features/management/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java +++ b/features/management/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java @@ -35,6 +35,7 @@ import javax.management.openmbean.TabularDataSupport; import org.apache.karaf.features.BundleInfo; import org.apache.karaf.features.ConfigFileInfo; +import org.apache.karaf.features.ConfigInfo; import org.apache.karaf.features.Feature; import org.apache.karaf.features.management.FeaturesServiceMBean; @@ -76,7 +77,7 @@ public class JmxFeature { itemValues[1] = feature.getVersion(); itemValues[2] = getFeatureIdentifierTable(feature.getDependencies()); itemValues[3] = getBundleUris(feature.getBundles()); - itemValues[4] = getConfigTable(feature.getConfigurations()); + itemValues[4] = getConfigList(feature.getConfigurations()); itemValues[5] = getConfigFileList(feature.getConfigurationFiles()); itemValues[6] = installed; data = new CompositeDataSupport(FEATURE, itemNames, itemValues); @@ -122,13 +123,14 @@ public class JmxFeature { return array; } - static TabularData getConfigTable(Map<String, Map<String, String>> configs) throws OpenDataException { + static TabularData getConfigList(List<ConfigInfo> configs) throws OpenDataException { TabularDataSupport table = new TabularDataSupport(FEATURE_CONFIG_TABLE); - for (Map.Entry<String, Map<String, String>> entry : configs.entrySet()) { + for (ConfigInfo configInfo : configs) { String[] itemNames = FeaturesServiceMBean.FEATURE_CONFIG; - Object[] itemValues = new Object[2]; - itemValues[0] = entry.getKey(); - itemValues[1] = getConfigElementTable(entry.getValue()); + Object[] itemValues = new Object[3]; + itemValues[0] = configInfo.getName(); + itemValues[1] = getConfigElementTable(configInfo.getProperties()); + itemValues[2] = configInfo.isAppend(); CompositeData config = new CompositeDataSupport(FEATURE_CONFIG, itemNames, itemValues); table.put(config); } http://git-wip-us.apache.org/repos/asf/karaf/blob/49a2c2bf/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java ---------------------------------------------------------------------- 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 568f231..748b0ed 100644 --- 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 @@ -17,11 +17,11 @@ package org.apache.karaf.webconsole.features; 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.ConfigInfo; import org.apache.karaf.features.Feature; public class ExtendedFeature implements Feature { @@ -51,7 +51,7 @@ public class ExtendedFeature implements Feature { return this.feature.getBundles(); } - public Map<String, Map<String, String>> getConfigurations() { + public List<ConfigInfo> getConfigurations() { return this.feature.getConfigurations(); }
