Repository: karaf
Updated Branches:
  refs/heads/karaf-3.0.x 940a2d5d8 -> 575ec6d97


[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/575ec6d9
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/575ec6d9
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/575ec6d9

Branch: refs/heads/karaf-3.0.x
Commit: 575ec6d97be6575c58ca236ccbed6ad25ab11c1a
Parents: 940a2d5
Author: anierbeck <[email protected]>
Authored: Wed Aug 20 21:23:39 2014 +0200
Committer: anierbeck <[email protected]>
Committed: Thu Aug 21 22:05:58 2014 +0200

----------------------------------------------------------------------
 .../enterprise/src/main/feature/feature.xml     |   2 +-
 .../framework/src/main/feature/feature.xml      |   2 +-
 .../spring/src/main/feature/feature.xml         |   4 +-
 .../standard/src/main/feature/feature.xml       |   4 +-
 .../features/command/InfoFeatureCommand.java    |   7 +-
 .../org/apache/karaf/features/Conditional.java  |   3 +-
 .../org/apache/karaf/features/ConfigInfo.java   |  31 +++
 .../java/org/apache/karaf/features/Feature.java |   5 +-
 .../karaf/features/FeaturesNamespaces.java      |   6 +-
 .../internal/FeatureConfigInstaller.java        |  84 +++++-
 .../internal/FeatureValidationUtil.java         |  12 +-
 .../karaf/features/internal/model/Config.java   |  54 +++-
 .../karaf/features/internal/model/Content.java  |  40 +--
 .../features/management/codec/JmxFeature.java   |  39 ++-
 .../karaf/features/karaf-features-1.2.1.xsd     | 262 +++++++++++++++++++
 .../apache/karaf/features/RepositoryTest.java   |  60 ++++-
 .../org/apache/karaf/features/repo3.xml         |  35 +++
 .../apache/karaf/itests/KarafTestSupport.java   |   3 +
 .../src/it/test-aggregate-features/control.xml  |   2 +-
 .../src/it/test-basic-generation/control.xml    |   2 +-
 .../src/it/test-check-dependencies/control.xml  |   2 +-
 .../src/it/test-input-file/control.xml          |   2 +-
 .../src/it/test-type-classifier/control.xml     |   2 +-
 .../karaf/tooling/features/model/ConfigRef.java |  41 +++
 .../karaf/tooling/features/model/Feature.java   |  10 +-
 .../tooling/features/model/Repository.java      |   3 +-
 .../webconsole/features/ExtendedFeature.java    |   4 +-
 27 files changed, 628 insertions(+), 93 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/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 a228ce1..d867813 100644
--- a/assemblies/features/enterprise/src/main/feature/feature.xml
+++ b/assemblies/features/enterprise/src/main/feature/feature.xml
@@ -16,7 +16,7 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<features name="enterprise-${project.version}" 
xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 
http://karaf.apache.org/xmlns/features/v1.2.0";>
+<features name="enterprise-${project.version}" 
xmlns="http://karaf.apache.org/xmlns/features/v1.2.1"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.1 
http://karaf.apache.org/xmlns/features/v1.2.1";>
 
     <!-- NB: this file is not the one really used. This file is used by the 
karaf-maven-plugin to define the start-level of bundles in the generated 
feature.xml -->
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/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 563207a..40bcca7 100644
--- a/assemblies/features/framework/src/main/feature/feature.xml
+++ b/assemblies/features/framework/src/main/feature/feature.xml
@@ -16,7 +16,7 @@
       See the License for the specific language governing permissions and
       limitations under the License.
 -->
-<features name="framework-${project.version}" 
xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 
http://karaf.apache.org/xmlns/features/v1.2.0";>
+<features name="framework-${project.version}" 
xmlns="http://karaf.apache.org/xmlns/features/v1.2.1"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.1 
http://karaf.apache.org/xmlns/features/v1.2.1";>
 
     <!-- This file is not used in the assembly., This file is used by the 
karaf-maven-plugin to generate a "final" feature.xml
               including the correct start-level for the generation of the 
startup.propertie file -->

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/assemblies/features/spring/src/main/feature/feature.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/spring/src/main/feature/feature.xml 
b/assemblies/features/spring/src/main/feature/feature.xml
index 904670b..e4688e1 100644
--- a/assemblies/features/spring/src/main/feature/feature.xml
+++ b/assemblies/features/spring/src/main/feature/feature.xml
@@ -16,7 +16,7 @@
       See the License for the specific language governing permissions and
       limitations under the License.
 -->
-<features name="spring-${project.version}" 
xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 
http://karaf.apache.org/xmlns/features/v1.2.0";>
+<features name="spring-${project.version}" 
xmlns="http://karaf.apache.org/xmlns/features/v1.2.1"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.1 
http://karaf.apache.org/xmlns/features/v1.2.1";>
 
     <!-- NB: this file is not the one really used. This file is used by the 
karaf-maven-plugin to define the start-level of bundles in the generated 
feature.xml -->
 
@@ -183,7 +183,7 @@
 
     <feature name="spring-web" description="Spring 3.2.x Web support" 
version="${spring32.version}" resolver="(obr)">
         <feature version="${spring32.version}">spring</feature>
-        <feature version="${http.feature.version}">http</feature>
+        <feature>http</feature>
         <bundle 
start-level="30">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.spring-web/${spring32.version}</bundle>
         <bundle 
start-level="30">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.spring-webmvc/${spring32.version}</bundle>
     </feature>

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/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 afb8976..1129023 100644
--- a/assemblies/features/standard/src/main/feature/feature.xml
+++ b/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.
 -->
-<features name="standard-${project.version}" 
xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 
http://karaf.apache.org/xmlns/features/v1.2.0";>
+<features name="standard-${project.version}" 
xmlns="http://karaf.apache.org/xmlns/features/v1.2.1"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.1 
http://karaf.apache.org/xmlns/features/v1.2.1";>
 
        
<repository>mvn:org.ops4j.pax.web/pax-web-features/${pax.web.version}/xml/features</repository>
 
@@ -107,7 +107,7 @@
     </feature>
 
     <feature name="http-whiteboard" description="Provide HTTP Whiteboard 
pattern support" version="${project.version}" resolver="(obr)">
-       <feature version="${project.version}">http</feature>
+       <feature>http</feature>
         <feature version="[${pax.web.version},5)">pax-http-whiteboard</feature>
     </feature>
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/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 3286a2a..87b2dc2 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.karaf.shell.commands.Command;
 import org.apache.karaf.shell.commands.Option;
 import org.apache.karaf.features.BundleInfo;
 import org.apache.karaf.features.ConfigFileInfo;
+import org.apache.karaf.features.ConfigInfo;
 import org.apache.karaf.features.Dependency;
 import org.apache.karaf.features.Feature;
 import org.apache.karaf.features.FeaturesService;
@@ -160,13 +161,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(INDENT + name);
+                       for (ConfigInfo configInfo : configurations) {
+                               System.out.println(INDENT + 
configInfo.getName());
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/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 c0e4d59..c381517 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,8 @@ public interface Conditional {
 
     List<BundleInfo> getBundles();
 
-    Map<String, Map<String, String>> getConfigurations();
+//    Map<String, Map<String, String>> getConfigurations();
+    List<ConfigInfo> getConfigurations();
 
     List<ConfigFileInfo> getConfigurationFiles();
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/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..f7b6b5c
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/ConfigInfo.java
@@ -0,0 +1,31 @@
+/*
+ * 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.Properties;
+
+public interface ConfigInfo {
+       
+       String getName();
+       
+       String getValue();
+       
+       Properties getProperties();
+
+       boolean isAppend();
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/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 4dfb624..ee43bb1 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
@@ -19,6 +19,8 @@ package org.apache.karaf.features;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.karaf.features.internal.model.Config;
+
 /**
  * A feature is a list of bundles associated identified by its name.
  */
@@ -46,7 +48,8 @@ public interface Feature {
 
     List<BundleInfo> getBundles();
 
-    Map<String, Map<String, String>> getConfigurations();
+//    Map<String, Map<String, String>> getConfigurations();
+    List<ConfigInfo> getConfigurations();
 
     List<ConfigFileInfo> getConfigurationFiles();
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/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 8e09748..d108b63 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,14 +26,16 @@ 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_2_0;
+       QName FEATURES_CURRENT = FEATURES_1_2_1;
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/features/core/src/main/java/org/apache/karaf/features/internal/FeatureConfigInstaller.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/internal/FeatureConfigInstaller.java
 
b/features/core/src/main/java/org/apache/karaf/features/internal/FeatureConfigInstaller.java
index 4f26ba0..2e1b0c1 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/internal/FeatureConfigInstaller.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/internal/FeatureConfigInstaller.java
@@ -21,12 +21,19 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.StringReader;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Dictionary;
+import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.List;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.apache.karaf.features.ConfigFileInfo;
+import org.apache.karaf.features.ConfigInfo;
 import org.apache.karaf.features.Feature;
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
@@ -82,25 +89,73 @@ public class FeatureConfigInstaller {
     }
 
     void installFeatureConfigs(Feature feature, boolean verbose) throws 
IOException, InvalidSyntaxException {
-        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 (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();
+                       Properties props = config.getProperties();
+
+                       // interpolation(props);
+            
+
+                       String[] pid = parsePid(config.getName());
+                       Configuration cfg = 
findExistingConfiguration(configAdmin, pid[0],
+                                       pid[1]);
+                       if (cfg == null) {
+                               Dictionary<String, String> cfgProps = 
convertToDict(props);
+
+                               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(), 
configFile.getFinalname(), configFile.isOverride(), verbose);
         }
     }
 
+       private Dictionary<String, String> convertToDict(Properties props) {
+               Dictionary<String, String> cfgProps = new Hashtable<String, 
String>();
+               for (@SuppressWarnings("rawtypes")
+               Enumeration e = props.propertyNames(); e.hasMoreElements();) {
+                       String key = (String) e.nextElement();
+                       String val = props.getProperty(key);
+                       cfgProps.put(key, val);
+               }
+               return cfgProps;
+       }
+
     private String createConfigurationKey(String pid, String factoryPid) {
         return factoryPid == null ? pid : pid + "-" + factoryPid;
     }
@@ -163,4 +218,5 @@ public class FeatureConfigInstaller {
                }
             
     }
+    
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/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 dfb7a7d..449c461 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
@@ -44,6 +44,7 @@ public class FeatureValidationUtil {
     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");
+    public static final QName FEATURES_1_2_1 = new 
QName("http://karaf.apache.org/xmlns/features/v1.2.1";, "features");
     private static final Logger LOGGER = 
LoggerFactory.getLogger(FeatureValidationUtil.class);
 
     /**
@@ -53,6 +54,7 @@ public class FeatureValidationUtil {
      * @throws Exception When validation fails.
      */
     public static void validate(URI uri) throws Exception {
+               LOGGER.debug("validating URI {}", uri);
         Document doc = load(uri);
 
         QName name = new QName(doc.getDocumentElement().getNamespaceURI(), 
doc.getDocumentElement().getLocalName());
@@ -61,11 +63,17 @@ public class FeatureValidationUtil {
             LOGGER.warn("Old style feature file without namespace found (URI: 
{}). This format is deprecated and support for it will soon be removed", uri);
             return;
         } else if (FeaturesNamespaces.FEATURES_1_0_0.equals(name)) {
+                       LOGGER.debug("validating with schema version 1.0.0");
             validate(doc, 
"/org/apache/karaf/features/karaf-features-1.0.0.xsd");
         } else if (FeaturesNamespaces.FEATURES_1_1_0.equals(name)) {
-            validate(doc, 
"/org/apache/karaf/features/karaf-features-1.1.0.xsd");
+                       LOGGER.debug("validating with schema version 1.1.0");
+                       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");
+                       LOGGER.debug("validating with schema version 1.2.0");
+                       validate(doc, 
"/org/apache/karaf/features/karaf-features-1.2.0.xsd");
+               } else if (FeaturesNamespaces.FEATURES_1_2_1.equals(name)) {
+                       LOGGER.debug("validating with schema version 1.2.1");
+                       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/575ec6d9/features/core/src/main/java/org/apache/karaf/features/internal/model/Config.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/internal/model/Config.java
 
b/features/core/src/main/java/org/apache/karaf/features/internal/model/Config.java
index a2c6674..67019be 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/internal/model/Config.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/internal/model/Config.java
@@ -19,12 +19,21 @@
 
 package org.apache.karaf.features.internal.model;
 
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Enumeration;
+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.XmlAttribute;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlValue;
 
+import org.apache.karaf.features.ConfigInfo;
+
 
 /**
  * 
@@ -52,12 +61,14 @@ import javax.xml.bind.annotation.XmlValue;
 @XmlType(name = "config", propOrder = {
     "value"
 })
-public class Config {
+public class Config implements ConfigInfo{
 
     @XmlValue
     protected String value;
     @XmlAttribute(required = true)
     protected String name;
+    @XmlAttribute(required = false)
+       private Boolean append = false;
 
     /**
      * Gets the value of the value property.
@@ -107,4 +118,45 @@ public class Config {
         this.name = value;
     }
 
+       /**
+        * @return the append
+        */
+       public boolean isAppend() {
+               return append;
+       }
+
+       /**
+        * @param append the append to set
+        */
+       public void setAppend(boolean append) {
+               this.append = append;
+       }
+
+       public Properties getProperties() {
+               StringReader propStream = new StringReader(getValue());
+               Properties props = new Properties();
+               try {
+                       props.load(propStream);
+               } catch (IOException e) {
+                       // ignore??
+               }
+               return props;
+       }
+
+       @SuppressWarnings("rawtypes")
+       private void interpolation(Properties properties) {
+               for (Enumeration e = properties.propertyNames(); 
e.hasMoreElements();) {
+                       String key = (String) e.nextElement();
+                       String val = properties.getProperty(key);
+                       Matcher matcher = 
Pattern.compile("\\$\\{([^}]+)\\}").matcher(val);
+                       while (matcher.find()) {
+                               String rep = 
System.getProperty(matcher.group(1));
+                               if (rep != null) {
+                                       val = val.replace(matcher.group(0), 
rep);
+                                       matcher.reset(val);
+                               }
+                       }
+                       properties.put(key, val);
+               }
+       }
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/features/core/src/main/java/org/apache/karaf/features/internal/model/Content.java
----------------------------------------------------------------------
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 756e4c1..544057d 100644
--- 
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
@@ -27,9 +27,12 @@ import java.util.Map;
 import java.util.Properties;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+
 import javax.xml.bind.annotation.XmlTransient;
+
 import org.apache.karaf.features.BundleInfo;
 import org.apache.karaf.features.ConfigFileInfo;
+import org.apache.karaf.features.ConfigInfo;
 
 @XmlTransient
 public class Content {
@@ -155,45 +158,12 @@ public class Content {
         return Collections.<BundleInfo>unmodifiableList(getBundle());
     }
 
-    public Map<String, Map<String, String>> getConfigurations() {
-        Map<String, Map<String, String>> result = new HashMap<String, 
Map<String, String>>();
-        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<String, String> propMap = new HashMap<String, String>();
-            for (Map.Entry<Object, Object> entry : props.entrySet()) {
-                propMap.put((String) entry.getKey(), (String) 
entry.getValue());
-            }
-            result.put(name, propMap);
-        }
-        return result;
+    public List<ConfigInfo> getConfigurations() {
+       return Collections.<ConfigInfo>unmodifiableList(getConfig());
     }
 
     public List<ConfigFileInfo> getConfigurationFiles() {
         return Collections.<ConfigFileInfo>unmodifiableList(getConfigfile());
     }
 
-    @SuppressWarnings("rawtypes")
-       protected void interpolation(Properties properties) {
-        for (Enumeration e = properties.propertyNames(); e.hasMoreElements(); 
) {
-            String key = (String) e.nextElement();
-            String val = properties.getProperty(key);
-            Matcher matcher = Pattern.compile("\\$\\{([^}]+)\\}").matcher(val);
-            while (matcher.find()) {
-                String rep = System.getProperty(matcher.group(1));
-                if (rep != null) {
-                    val = val.replace(matcher.group(0), rep);
-                    matcher.reset(val);
-                }
-            }
-            properties.put(key, val);
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java
 
b/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java
index 54fa3c0..79d00d1 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java
@@ -20,6 +20,7 @@ import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Properties;
 import java.util.Set;
 
 import javax.management.openmbean.TabularData;
@@ -35,6 +36,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.Dependency;
 import org.apache.karaf.features.Feature;
 import org.apache.karaf.features.management.FeaturesServiceMBean;
@@ -77,7 +79,7 @@ public class JmxFeature {
             itemValues[1] = feature.getVersion();
             itemValues[2] = 
getDependencyIdentifierTable(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);
@@ -123,16 +125,18 @@ public class JmxFeature {
         return array;
     }
 
-    static TabularData getConfigTable(Map<String, Map<String, String>> 
configs) throws OpenDataException {
+       static TabularData getConfigList(List<ConfigInfo> config) throws 
OpenDataException {
         TabularDataSupport table = new 
TabularDataSupport(FEATURE_CONFIG_TABLE);
-        for (Map.Entry<String, Map<String, String>> entry : 
configs.entrySet()) {
-            String[] itemNames = FeaturesServiceMBean.FEATURE_CONFIG;
-            Object[] itemValues = new Object[2];
-            itemValues[0] = entry.getKey();
-            itemValues[1] = getConfigElementTable(entry.getValue());
-            CompositeData config = new CompositeDataSupport(FEATURE_CONFIG, 
itemNames, itemValues);
-            table.put(config);
-        }
+        for (ConfigInfo configInfo : config) {
+               String[] itemNames = FeaturesServiceMBean.FEATURE_CONFIG;
+               Object[] itemValues = new Object[3];
+               itemValues[0] = configInfo.getName();
+                       itemValues[1] = 
getConfigElementTable(configInfo.getProperties());
+                       itemValues[2] = configInfo.isAppend();
+                       CompositeData configComposite = new 
CompositeDataSupport(
+                                       FEATURE_CONFIG, itemNames, itemValues);
+                       table.put(configComposite);
+               }
         return table;
     }
     
@@ -158,6 +162,21 @@ public class JmxFeature {
         return table;
     }
 
+       static TabularData getConfigElementTable(Properties props)
+                       throws OpenDataException {
+               TabularDataSupport table = new TabularDataSupport(
+                               FEATURE_CONFIG_ELEMENT_TABLE);
+               for (Object key : props.keySet()) {
+                       String[] itemNames = 
FeaturesServiceMBean.FEATURE_CONFIG_ELEMENT;
+                       Object[] itemValues = { (String) key,
+                                       props.getProperty((String) key) };
+                       CompositeData element = new CompositeDataSupport(
+                                       FEATURE_CONFIG_ELEMENT, itemNames, 
itemValues);
+                       table.put(element);
+               }
+               return table;
+       }
+
 
     static {
         FEATURE_IDENTIFIER = createFeatureIdentifierType();

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/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/575ec6d9/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 cf97a20..25e3e5e 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
@@ -39,9 +39,10 @@ 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());
@@ -81,9 +82,56 @@ 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" + 
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());
+        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(true, 
features[2].getConfigurationFiles().get(0).isOverride());
+        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/575ec6d9/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/575ec6d9/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
----------------------------------------------------------------------
diff --git a/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java 
b/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
index ccde975..7636afa 100644
--- a/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
+++ b/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
@@ -19,6 +19,7 @@ import static 
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.configure
 import static 
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
 import static 
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.karafDistributionConfiguration;
 import static 
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
+import static 
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.logLevel;
 import static 
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.replaceConfigurationFile;
 
 import java.io.ByteArrayOutputStream;
@@ -62,6 +63,7 @@ import org.ops4j.pax.exam.Configuration;
 import org.ops4j.pax.exam.Option;
 import org.ops4j.pax.exam.ProbeBuilder;
 import org.ops4j.pax.exam.TestProbeBuilder;
+import org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel;
 import org.ops4j.pax.exam.options.MavenArtifactUrlReference;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -125,6 +127,7 @@ public class KarafTestSupport {
             // enable JMX RBAC security, thanks to the KarafMBeanServerBuilder
             configureSecurity().enableKarafMBeanServerBuilder(),
             keepRuntimeFolder(),
+                               logLevel(LogLevel.DEBUG),
             replaceConfigurationFile("etc/org.ops4j.pax.logging.cfg", 
getConfigFile("/etc/org.ops4j.pax.logging.cfg")),
             editConfigurationFilePut("etc/org.apache.karaf.features.cfg", 
"featuresBoot", "config,standard,region,package,kar,management"),
             editConfigurationFilePut("etc/org.ops4j.pax.web.cfg", 
"org.osgi.service.http.port", HTTP_PORT),

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/tooling/karaf-maven-plugin/src/it/test-aggregate-features/control.xml
----------------------------------------------------------------------
diff --git 
a/tooling/karaf-maven-plugin/src/it/test-aggregate-features/control.xml 
b/tooling/karaf-maven-plugin/src/it/test-aggregate-features/control.xml
index 7c50b96..efa21fa 100644
--- a/tooling/karaf-maven-plugin/src/it/test-aggregate-features/control.xml
+++ b/tooling/karaf-maven-plugin/src/it/test-aggregate-features/control.xml
@@ -18,7 +18,7 @@
   ~ under the License.
   -->
 
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"; 
name="aggregate-features">
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.1"; 
name="aggregate-features">
     <feature description="aggregate-recursive-module-c" version="1.0-SNAPSHOT" 
name="aggregate-recursive-module-c">
         <details>Test Description</details>
         <bundle>mvn:test/aggregate-recursive-module-b/1.0-SNAPSHOT</bundle>

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/tooling/karaf-maven-plugin/src/it/test-basic-generation/control.xml
----------------------------------------------------------------------
diff --git 
a/tooling/karaf-maven-plugin/src/it/test-basic-generation/control.xml 
b/tooling/karaf-maven-plugin/src/it/test-basic-generation/control.xml
index 06139fe..facd313 100644
--- a/tooling/karaf-maven-plugin/src/it/test-basic-generation/control.xml
+++ b/tooling/karaf-maven-plugin/src/it/test-basic-generation/control.xml
@@ -18,4 +18,4 @@
   ~ under the License.
   -->
 
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"; 
name="test-basic-generation"/>
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.1"; 
name="test-basic-generation"/>

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/tooling/karaf-maven-plugin/src/it/test-check-dependencies/control.xml
----------------------------------------------------------------------
diff --git 
a/tooling/karaf-maven-plugin/src/it/test-check-dependencies/control.xml 
b/tooling/karaf-maven-plugin/src/it/test-check-dependencies/control.xml
index a1a81c4..df4aba0 100644
--- a/tooling/karaf-maven-plugin/src/it/test-check-dependencies/control.xml
+++ b/tooling/karaf-maven-plugin/src/it/test-check-dependencies/control.xml
@@ -18,7 +18,7 @@
   ~ under the License.
   -->
 
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"; 
name="check-dependencies-features">
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.1"; 
name="check-dependencies-features">
     <feature description="dependency-module-c" version="1.0-SNAPSHOT" 
name="dependency-module-c">
         <bundle>mvn:test/dependency-module-a/1.0-SNAPSHOT</bundle>
         <bundle>mvn:test/dependency-module-b/1.0-SNAPSHOT</bundle>

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/tooling/karaf-maven-plugin/src/it/test-input-file/control.xml
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/it/test-input-file/control.xml 
b/tooling/karaf-maven-plugin/src/it/test-input-file/control.xml
index 25a2acb..795e8d9 100644
--- a/tooling/karaf-maven-plugin/src/it/test-input-file/control.xml
+++ b/tooling/karaf-maven-plugin/src/it/test-input-file/control.xml
@@ -18,7 +18,7 @@
   ~ under the License.
   -->
 
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"; 
name="test-input-file">
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.1"; 
name="test-input-file">
     <feature description="Test Description" version="1.0-SNAPSHOT" 
name="test-input-file">
         <details>Test Description</details>
         <bundle>mvn:test/test-input-file/1.0-SNAPSHOT</bundle>

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/tooling/karaf-maven-plugin/src/it/test-type-classifier/control.xml
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/it/test-type-classifier/control.xml 
b/tooling/karaf-maven-plugin/src/it/test-type-classifier/control.xml
index 9b84d85..708c26c 100644
--- a/tooling/karaf-maven-plugin/src/it/test-type-classifier/control.xml
+++ b/tooling/karaf-maven-plugin/src/it/test-type-classifier/control.xml
@@ -18,4 +18,4 @@
   ~ under the License.
   -->
 
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"; 
name="test-type-classifier"/>
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.1"; 
name="test-type-classifier"/>

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/ConfigRef.java
----------------------------------------------------------------------
diff --git 
a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/ConfigRef.java
 
b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/ConfigRef.java
new file mode 100644
index 0000000..2661381
--- /dev/null
+++ 
b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/ConfigRef.java
@@ -0,0 +1,41 @@
+package org.apache.karaf.tooling.features.model;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+public class ConfigRef {
+
+       private String name;
+       private Map<String, String> properties;
+       private boolean append;
+
+       public ConfigRef(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(Map<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/575ec6d9/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/Feature.java
----------------------------------------------------------------------
diff --git 
a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/Feature.java
 
b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/Feature.java
index a818671..3372973 100644
--- 
a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/Feature.java
+++ 
b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/Feature.java
@@ -28,7 +28,7 @@ public class Feature {
     private String version;
     private List<String> dependencies = new ArrayList<String>();
     private List<BundleRef> bundles = new ArrayList<BundleRef>();
-    private Map<String, Map<String, String>> configs = new HashMap<String, 
Map<String, String>>();
+       private List<ConfigRef> configs = new ArrayList<ConfigRef>();
     private List<ArtifactRef> configFiles = new ArrayList<ArtifactRef>();
 
     public Feature(String name) {
@@ -55,8 +55,8 @@ public class Feature {
         return bundles;
     }
 
-    public Map<String, Map<String, String>> getConfigurations() {
-        return configs;
+       public List<ConfigRef> getConfigurations() {
+               return configs;
     }
 
     public List<ArtifactRef> getConfigFiles() {
@@ -71,8 +71,8 @@ public class Feature {
         bundles.add(bundle);
     }
 
-    public void addConfig(String name, Map<String, String> properties) {
-        configs.put(name, properties);
+       public void addConfig(ConfigRef config) {
+               configs.add(config);
     }
 
     public void addConfigFile(ArtifactRef configFile) {

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/Repository.java
----------------------------------------------------------------------
diff --git 
a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/Repository.java
 
b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/Repository.java
index f3210e2..5e539cf 100644
--- 
a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/Repository.java
+++ 
b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/model/Repository.java
@@ -111,6 +111,7 @@ public class Repository {
                     Element c = (Element) configNodes.item(j);
                     String cfgName = c.getAttribute("name");
                     String data = c.getTextContent();
+                                       String append = 
c.getAttribute("append");
                     Properties properties = new Properties();
                     properties.load(new ByteArrayInputStream(data.getBytes()));
                     Map<String, String> hashtable = new Hashtable<String, 
String>();
@@ -118,7 +119,7 @@ public class Repository {
                         String n = key.toString();
                         hashtable.put(n, properties.getProperty(n));
                     }
-                    f.addConfig(cfgName, hashtable);
+                                       f.addConfig(new ConfigRef(cfgName, 
hashtable, append));
                 }
                 NodeList configFileNodes = 
e.getElementsByTagName("configfile");
                 for (int j = 0; j < configFileNodes.getLength(); j++) {

http://git-wip-us.apache.org/repos/asf/karaf/blob/575ec6d9/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 401cf29..b726b28 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
@@ -22,6 +22,7 @@ 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.Dependency;
 import org.apache.karaf.features.Feature;
 
@@ -54,7 +55,8 @@ public class ExtendedFeature implements Feature {
     }
 
     @Override
-    public Map<String, Map<String, String>> getConfigurations() {
+    public List<ConfigInfo> getConfigurations() {
+//    public Map<String, Map<String, String>> getConfigurations() {
         return this.feature.getConfigurations();
     }
 

Reply via email to