You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:57:02 UTC

[sling-org-apache-sling-provisioning-model] 06/34: Fully support Apach Felix config file format. Move from properties to getter and setter methods

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.provisioning.model-1.0.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git

commit fdc1ccfbdb313e85d643cd6120cebee3e6dda6f7
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Wed Sep 24 07:14:02 2014 +0000

    Fully support Apach Felix config file format. Move from properties to getter and setter methods
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/slingstart-model@1627238 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            |  16 +++
 .../sling/slingstart/model/SSMConfiguration.java   |  35 +++--
 .../sling/slingstart/model/SSMDeliverable.java     |  59 +++-----
 .../apache/sling/slingstart/model/SSMFeature.java  | 159 +++++++++++++--------
 .../sling/slingstart/model/SSMStartLevel.java      |  22 ++-
 .../slingstart/model/txt/TXTSSMModelReader.java    |  60 ++++++--
 .../slingstart/model/xml/XMLSSMModelReader.java    |  39 +++--
 .../slingstart/model/xml/XMLSSMModelWriter.java    |  46 +++---
 8 files changed, 292 insertions(+), 144 deletions(-)

diff --git a/pom.xml b/pom.xml
index 01b9346..96bca0b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,7 +46,23 @@
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
                 <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Embed-Dependency>
+                            org.apache.felix.configadmin;inline="org/apache/felix/cm/file/ConfigurationHandler.*"
+                        </Embed-Dependency>
+                    </instructions>
+                </configuration>
             </plugin>
         </plugins>
     </build>
+    <dependencies>
+      <!-- We use a class from the config admin implementation to read config files -->
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.configadmin</artifactId>
+            <version>1.2.8</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
 </project>
diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMConfiguration.java b/src/main/java/org/apache/sling/slingstart/model/SSMConfiguration.java
index fed5e88..1539c35 100644
--- a/src/main/java/org/apache/sling/slingstart/model/SSMConfiguration.java
+++ b/src/main/java/org/apache/sling/slingstart/model/SSMConfiguration.java
@@ -16,17 +16,33 @@
  */
 package org.apache.sling.slingstart.model;
 
+import java.util.Dictionary;
+import java.util.Hashtable;
+
 
 /**
  * Configuration
  */
 public class SSMConfiguration {
 
-    public String pid;
+    private final String pid;
+
+    private final String factoryPid;
+
+    private final Dictionary<String, Object> properties = new Hashtable<String, Object>();
 
-    public String factoryPid;
+    public SSMConfiguration(final String pid, final String factoryPid) {
+        this.pid = (pid != null ? pid.trim() : null);
+        this.factoryPid = (factoryPid != null ? factoryPid.trim() : null);
+    }
+
+    public String getPid() {
+        return this.pid;
+    }
 
-    public String properties;
+    public String getFactoryPid() {
+        return this.factoryPid;
+    }
 
     /**
      * validates the object and throws an IllegalStateException
@@ -38,11 +54,6 @@ public class SSMConfiguration {
      * @throws IllegalStateException
      */
     public void validate() {
-        // trim values first
-        if ( pid != null ) pid = pid.trim();
-        if ( factoryPid != null ) factoryPid = factoryPid.trim();
-        if ( properties != null ) properties = properties.trim();
-
         // check/correct values
         if ( pid == null || pid.isEmpty() ) {
             throw new IllegalStateException("pid");
@@ -59,6 +70,14 @@ public class SSMConfiguration {
         return false;
     }
 
+    public Dictionary<String, Object> getProperties() {
+        return this.properties;
+    }
+
+    public void addProperty(final String key, final Object value) {
+        this.properties.put(key, value);
+    }
+
     @Override
     public String toString() {
         return "SSMConfiguration [pid=" + pid + ", factoryPid=" + factoryPid
diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMDeliverable.java b/src/main/java/org/apache/sling/slingstart/model/SSMDeliverable.java
index 1ff8a9d..e7b28bc 100644
--- a/src/main/java/org/apache/sling/slingstart/model/SSMDeliverable.java
+++ b/src/main/java/org/apache/sling/slingstart/model/SSMDeliverable.java
@@ -19,7 +19,6 @@ package org.apache.sling.slingstart.model;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -34,12 +33,12 @@ import java.util.Map;
  */
 public class SSMDeliverable {
 
-    public final List<SSMFeature> features = new ArrayList<SSMFeature>();
+    private final List<SSMFeature> features = new ArrayList<SSMFeature>();
 
-    public Map<String, String> properties = new HashMap<String, String>();
+    private Map<String, String> properties = new HashMap<String, String>();
 
     public SSMDeliverable() {
-        this.features.add(new SSMFeature()); // global features
+        this.features.add(new SSMFeature(null)); // global features
     }
 
     /**
@@ -48,22 +47,13 @@ public class SSMDeliverable {
      * @return The feature or null.
      */
     private SSMFeature findFeature(final String[] runModes) {
+        final String[] sortedRunModes = SSMFeature.getSortedRunModesArray(runModes);
         SSMFeature result = null;
         for(final SSMFeature current : this.features) {
-            if ( runModes == null && current.runModes == null ) {
+            if ( Arrays.equals(sortedRunModes, current.getRunModes()) ) {
                 result = current;
                 break;
             }
-            if ( runModes != null && current.runModes != null ) {
-                final List<String> a = new ArrayList<String>(Arrays.asList(runModes));
-                final List<String> b = new ArrayList<String>(Arrays.asList(current.runModes));
-                Collections.sort(a);
-                Collections.sort(b);
-                if ( a.equals(b) ) {
-                    result = current;
-                    break;
-                }
-            }
         }
         return result;
     }
@@ -82,34 +72,17 @@ public class SSMDeliverable {
     public SSMFeature getOrCreateFeature(final String[] runModes) {
         SSMFeature result = findFeature(runModes);
         if ( result == null ) {
-            result = new SSMFeature();
-            result.runModes = runModes;
+            result = new SSMFeature(runModes);
             this.features.add(result);
-            Collections.sort(this.features, new Comparator<SSMFeature>() {
-
-                @Override
-                public int compare(final SSMFeature o1, final SSMFeature o2) {
-                    if ( o1.runModes == null ) {
-                        if ( o2.runModes == null ) {
-                            return 0;
-                        }
-                        return -1;
-                    }
-                    if ( o2.runModes == null ) {
-                        return 1;
-                    }
-                    final List<String> a = new ArrayList<String>(Arrays.asList(o1.runModes));
-                    final List<String> b = new ArrayList<String>(Arrays.asList(o2.runModes));
-                    Collections.sort(a);
-                    Collections.sort(b);
-
-                    return a.toString().compareTo(b.toString());
-                }
-            });
+            Collections.sort(this.features);
         }
         return result;
     }
 
+    public List<SSMFeature> getFeatures() {
+        return this.features;
+    }
+
     /**
      * validates the object and throws an IllegalStateException
      *
@@ -156,12 +129,20 @@ public class SSMDeliverable {
      */
     public void merge(final SSMDeliverable other) {
         for(final SSMFeature mode : other.features) {
-            final SSMFeature mergeFeature = this.getOrCreateFeature(mode.runModes);
+            final SSMFeature mergeFeature = this.getOrCreateFeature(mode.getRunModes());
             mergeFeature.merge(mode);
         }
         this.properties.putAll(other.properties);
     }
 
+    public Map<String, String> getProperties() {
+        return this.properties;
+    }
+
+    public void addProperty(final String key, final String value) {
+        this.properties.put(key, value);
+    }
+
     @Override
     public String toString() {
         return "SSMDeliverable [features=" + features + ", properties="
diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMFeature.java b/src/main/java/org/apache/sling/slingstart/model/SSMFeature.java
index 671ec19..26f0bcb 100644
--- a/src/main/java/org/apache/sling/slingstart/model/SSMFeature.java
+++ b/src/main/java/org/apache/sling/slingstart/model/SSMFeature.java
@@ -19,7 +19,7 @@ package org.apache.sling.slingstart.model;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Comparator;
+import java.util.Enumeration;
 import java.util.List;
 import java.util.Set;
 
@@ -34,7 +34,7 @@ import java.util.Set;
  * In addition to custom, user defined run modes, special run modes exists.
  * A special run mode name starts with a colon.
  */
-public class SSMFeature {
+public class SSMFeature implements Comparable<SSMFeature> {
 
     public static final String RUN_MODE_BASE = ":base";
 
@@ -44,13 +44,40 @@ public class SSMFeature {
 
     public static final String RUN_MODE_STANDALONE = ":standalone";
 
-    public String[] runModes;
+    private final String[] runModes;
 
-    public final List<SSMStartLevel> startLevels = new ArrayList<SSMStartLevel>();
+    private final List<SSMStartLevel> startLevels = new ArrayList<SSMStartLevel>();
 
-    public final List<SSMConfiguration> configurations = new ArrayList<SSMConfiguration>();
+    private final List<SSMConfiguration> configurations = new ArrayList<SSMConfiguration>();
 
-    public SSMSettings settings;
+    private SSMSettings settings;
+
+    public SSMFeature(final String[] runModes) {
+        this.runModes = getSortedRunModesArray(runModes);
+    }
+
+    public static String[] getSortedRunModesArray(final String[] runModes) {
+        // sort run modes
+        if ( runModes != null ) {
+            final List<String> list = new ArrayList<String>();
+            for(final String m : runModes) {
+                if ( m != null ) {
+                    if ( !m.trim().isEmpty() ) {
+                        list.add(m.trim());
+                    }
+                }
+            }
+            if ( list.size() > 0 ) {
+                Collections.sort(list);
+                return list.toArray(new String[list.size()]);
+            }
+        }
+        return null;
+    }
+
+    public String[] getRunModes() {
+        return this.runModes;
+    }
 
     /**
      * validates the object and throws an IllegalStateException
@@ -60,24 +87,14 @@ public class SSMFeature {
     public void validate() {
         if ( this.runModes != null ) {
             boolean hasSpecial = false;
-            final List<String> modes = new ArrayList<String>();
             for(String m : this.runModes) {
-                if ( m != null ) m = m.trim();
-                if ( m != null && !m.isEmpty()) {
-                    modes.add(m);
-                    if ( m.startsWith(":") ) {
-                        if ( hasSpecial ) {
-                            throw new IllegalStateException("Invalid modes " + Arrays.toString(this.runModes));
-                        }
-                        hasSpecial = true;
+                if ( m.startsWith(":") ) {
+                    if ( hasSpecial ) {
+                        throw new IllegalStateException("Invalid modes " + Arrays.toString(this.runModes));
                     }
+                    hasSpecial = true;
                 }
             }
-            if ( modes.size() == 0 ) {
-                this.runModes = null;
-            } else {
-                this.runModes = modes.toArray(new String[modes.size()]);
-            }
         }
         for(final SSMStartLevel sl : this.startLevels) {
             sl.validate();
@@ -140,25 +157,13 @@ public class SSMFeature {
      */
     public SSMStartLevel getOrCreateStartLevel(final int startLevel) {
         for(final SSMStartLevel sl : this.startLevels) {
-            if ( sl.level == startLevel ) {
+            if ( sl.getLevel() == startLevel ) {
                 return sl;
             }
         }
-        final SSMStartLevel sl = new SSMStartLevel();
-        sl.level = startLevel;
+        final SSMStartLevel sl = new SSMStartLevel(startLevel);
         this.startLevels.add(sl);
-        Collections.sort(this.startLevels, new Comparator<SSMStartLevel>() {
-
-            @Override
-            public int compare(SSMStartLevel o1, SSMStartLevel o2) {
-                if ( o1.level < o2.level ) {
-                    return -1;
-                } else if ( o1.level > o2.level ) {
-                    return 1;
-                }
-                return 0;
-            }
-        });
+        Collections.sort(this.startLevels);
         return sl;
     }
 
@@ -170,7 +175,7 @@ public class SSMFeature {
             // search for duplicates in other start levels
             for(final SSMArtifact artifact : sl.artifacts) {
                 for(final SSMStartLevel mySL : this.startLevels) {
-                    if ( mySL.level == sl.level ) {
+                    if ( mySL.getLevel() == sl.getLevel() ) {
                         continue;
                     }
                     final SSMArtifact myArtifact = mySL.search(artifact);
@@ -180,28 +185,15 @@ public class SSMFeature {
                 }
             }
 
-            final SSMStartLevel mergeSL = this.getOrCreateStartLevel(sl.level);
+            final SSMStartLevel mergeSL = this.getOrCreateStartLevel(sl.getLevel());
             mergeSL.merge(sl);
         }
         for(final SSMConfiguration config : mode.configurations) {
-            SSMConfiguration found = null;
-            for(final SSMConfiguration current : this.configurations) {
-                if ( config.factoryPid == null ) {
-                    if ( current.factoryPid == null && current.pid.equals(config.pid) ) {
-                        found = current;
-                        break;
-                    }
-                } else {
-                    if ( config.factoryPid.equals(current.factoryPid) && current.pid.equals(config.pid) ) {
-                        found = current;
-                        break;
-                    }
-                }
-            }
-            if ( found != null ) {
-                found.properties = config.properties;
-            } else {
-                this.configurations.add(config);
+            final SSMConfiguration found = getOrCreateConfiguration(config.getPid(), config.getFactoryPid());
+            final Enumeration<String> e = config.getProperties().keys();
+            while ( e.hasMoreElements() ) {
+                final String key = e.nextElement();
+                found.addProperty(key, config.getProperties().get(key));
             }
         }
         if ( this.settings == null && mode.settings != null ) {
@@ -217,17 +209,72 @@ public class SSMFeature {
      */
     public SSMConfiguration getConfiguration(final String pid) {
         for(final SSMConfiguration c : this.configurations) {
-            if ( pid.equals(c.pid) ) {
+            if ( pid.equals(c.getPid()) ) {
                 return c;
             }
         }
         return null;
     }
 
+    public SSMConfiguration getOrCreateConfiguration(final String pid, final String factoryPid) {
+        SSMConfiguration found = null;
+        for(final SSMConfiguration current : this.configurations) {
+            if ( factoryPid == null ) {
+                if ( current.getFactoryPid() == null && current.getPid().equals(pid) ) {
+                    found = current;
+                    break;
+                }
+            } else {
+                if ( factoryPid.equals(current.getFactoryPid()) && current.getPid().equals(pid) ) {
+                    found = current;
+                    break;
+                }
+            }
+        }
+        if ( found == null ) {
+            found = new SSMConfiguration(pid, factoryPid);
+            this.configurations.add(found);
+        }
+        return found;
+    }
+
+    public List<SSMStartLevel> getStartLevels() {
+        return this.startLevels;
+    }
+
+    public List<SSMConfiguration> getConfigurations() {
+        return this.configurations;
+    }
+
+    public SSMSettings getSettings() {
+        return this.settings;
+    }
+
+    /**
+     * @see java.lang.Comparable#compareTo(java.lang.Object)
+     */
+    @Override
+    public int compareTo( SSMFeature o2) {
+        if ( this.runModes == null ) {
+            if ( o2.runModes == null ) {
+                return 0;
+            }
+            return -1;
+        }
+        if ( o2.runModes == null ) {
+            return 1;
+        }
+        return Arrays.toString(this.runModes).compareTo(Arrays.toString(o2.runModes));
+    }
+
     @Override
     public String toString() {
         return "SSMFeature [runModes=" + Arrays.toString(runModes)
                 + ", startLevels=" + startLevels + ", configurations="
                 + configurations + ", settings=" + settings + "]";
     }
+
+    public void setSettings(final SSMSettings ssmSettings) {
+        this.settings = ssmSettings;
+    }
 }
diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMStartLevel.java b/src/main/java/org/apache/sling/slingstart/model/SSMStartLevel.java
index a254d0b..98899a9 100644
--- a/src/main/java/org/apache/sling/slingstart/model/SSMStartLevel.java
+++ b/src/main/java/org/apache/sling/slingstart/model/SSMStartLevel.java
@@ -23,12 +23,20 @@ import java.util.List;
  * A start level holds a set of artifacts.
  * A valid start level is positive, start level 0 means the default OSGi start level.
  */
-public class SSMStartLevel {
+public class SSMStartLevel implements Comparable<SSMStartLevel> {
 
-    public int level;
+    private final int level;
 
     public final List<SSMArtifact> artifacts = new ArrayList<SSMArtifact>();
 
+    public SSMStartLevel(final int level) {
+        this.level = level;
+    }
+
+    public int getLevel() {
+        return this.level;
+    }
+
     /**
      * validates the object and throws an IllegalStateException
      *
@@ -73,6 +81,16 @@ public class SSMStartLevel {
     }
 
     @Override
+    public int compareTo(final SSMStartLevel o) {
+        if ( this.level < o.level ) {
+            return -1;
+        } else if ( this.level > o.level ) {
+            return 1;
+        }
+        return 0;
+    }
+
+    @Override
     public String toString() {
         return "SSMStartLevel [level=" + level + ", artifacts=" + artifacts
                 + "]";
diff --git a/src/main/java/org/apache/sling/slingstart/model/txt/TXTSSMModelReader.java b/src/main/java/org/apache/sling/slingstart/model/txt/TXTSSMModelReader.java
index 6c73c22..fa0f3f4 100644
--- a/src/main/java/org/apache/sling/slingstart/model/txt/TXTSSMModelReader.java
+++ b/src/main/java/org/apache/sling/slingstart/model/txt/TXTSSMModelReader.java
@@ -16,11 +16,16 @@
  */
 package org.apache.sling.slingstart.model.txt;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.LineNumberReader;
 import java.io.Reader;
+import java.io.StringReader;
+import java.util.Dictionary;
+import java.util.Enumeration;
 import java.util.UUID;
 
+import org.apache.felix.cm.file.ConfigurationHandler;
 import org.apache.sling.slingstart.model.SSMArtifact;
 import org.apache.sling.slingstart.model.SSMConfiguration;
 import org.apache.sling.slingstart.model.SSMDeliverable;
@@ -87,41 +92,72 @@ public class TXTSSMModelReader {
                 feature.getOrCreateStartLevel(0).artifacts.add(artifact);
             } else if ( "config".equals(verb) ) {
                 final SSMFeature feature = model.getOrCreateFeature(null);
-                final SSMConfiguration config = new SSMConfiguration();
                 boolean felixFormat = false;
+                final String pid;
                 if (qualifier.endsWith(FELIX_FORMAT_SUFFIX)) {
                     felixFormat = true;
-                    config.pid = qualifier.split(" ")[0].trim();
+                    pid = qualifier.split(" ")[0].trim();
                 } else {
-                    config.pid = qualifier;
+                    pid = qualifier;
                 }
+                final SSMConfiguration config = feature.getOrCreateConfiguration(pid, null);
                 if ( props != null ) {
-                    config.properties = props.toString();
+                    processConfigurationProperties(config, props.toString(), felixFormat);
                 }
-                feature.configurations.add(config);
             } else if ( "config.factory".equals(verb) ) {
                 final SSMFeature feature = model.getOrCreateFeature(null);
-                final SSMConfiguration config = new SSMConfiguration();
                 boolean felixFormat = false;
+                final String factoryPid;
                 if (qualifier.endsWith(FELIX_FORMAT_SUFFIX)) {
                     felixFormat = true;
-                    config.factoryPid = qualifier.split(" ")[0].trim();
+                    factoryPid = qualifier.split(" ")[0].trim();
                 } else {
-                    config.factoryPid = qualifier;
+                    factoryPid = qualifier;
                 }
                 // create unique alias
-                config.pid = UUID.randomUUID().toString();
-
+                final SSMConfiguration config = feature.getOrCreateConfiguration(UUID.randomUUID().toString(), factoryPid);
                 if ( props != null ) {
-                    config.properties = props.toString();
+                    processConfigurationProperties(config, props.toString(), felixFormat);
                 }
-                feature.configurations.add(config);
             }
         }
 
         return model;
     }
 
+    private static void processConfigurationProperties(final SSMConfiguration config, final String textValue,
+            final boolean felixFormat)
+    throws IOException {
+        if ( felixFormat ) {
+            ByteArrayInputStream bais = null;
+            try {
+                bais = new ByteArrayInputStream(textValue.getBytes("UTF-8"));
+                @SuppressWarnings("unchecked")
+                final Dictionary<String, Object> props = ConfigurationHandler.read(bais);
+                final Enumeration<String> e = props.keys();
+                while ( e.hasMoreElements() ) {
+                    final String key = e.nextElement();
+                    config.addProperty(key, props.get(key));
+                }
+            } finally {
+                if ( bais != null ) {
+                    try {
+                        bais.close();
+                    } catch ( final IOException ignore ) {
+                        // ignore
+                    }
+                }
+            }
+        } else {
+            final LineNumberReader lnr = new LineNumberReader(new StringReader(textValue));
+            String line;
+            while ( (line = lnr.readLine()) != null ) {
+                final int pos = line.indexOf('=');
+                config.addProperty(line.substring(0, pos), line.substring(pos + 1));
+            }
+        }
+    }
+
     private static boolean ignore(final String line) {
         return line.trim().length() == 0 || line.startsWith("#");
     }
diff --git a/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelReader.java b/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelReader.java
index cc45c0e..c923044 100644
--- a/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelReader.java
+++ b/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelReader.java
@@ -16,16 +16,20 @@
  */
 package org.apache.sling.slingstart.model.xml;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.LineNumberReader;
 import java.io.Reader;
 import java.io.StringReader;
+import java.util.Dictionary;
+import java.util.Enumeration;
 import java.util.Stack;
 
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.parsers.SAXParser;
 import javax.xml.parsers.SAXParserFactory;
 
+import org.apache.felix.cm.file.ConfigurationHandler;
 import org.apache.sling.slingstart.model.SSMArtifact;
 import org.apache.sling.slingstart.model.SSMConfiguration;
 import org.apache.sling.slingstart.model.SSMDeliverable;
@@ -156,16 +160,13 @@ public class XMLSSMModelReader {
                                 artifact.type = atts.getValue("type");
                                 artifact.classifier = atts.getValue("classifier");
                             } else if ( this.mode == MODE.CONFIGURATION || this.mode == MODE.FEATURE_CONFIGURATION) {
-                                this.configuration = new SSMConfiguration();
-                                this.configuration.pid = atts.getValue("pid");
-                                this.configuration.factoryPid = atts.getValue("factory");
-                                this.feature.configurations.add(this.configuration);
+                                this.configuration = this.feature.getOrCreateConfiguration(atts.getValue("pid"), atts.getValue("factory"));
                                 this.text = new StringBuilder();
                             } else if ( this.mode == MODE.SETTINGS || this.mode == MODE.FEATURE_SETTINGS) {
-                                if ( this.feature.settings != null ) {
+                                if ( this.feature.getSettings() != null ) {
                                     throw new SAXException("Duplicate settings section");
                                 }
-                                this.feature.settings = new SSMSettings();
+                                this.feature.setSettings(new SSMSettings());
                                 this.text = new StringBuilder();
 
                             } else if ( this.mode == MODE.FEATURE ) {
@@ -215,10 +216,30 @@ public class XMLSSMModelReader {
                         if ( prevMode == MODE.STARTLEVEL || prevMode == MODE.FEATURE_STARTLEVEL ) {
                             this.startLevel = 0;
                         } else if ( prevMode == MODE.CONFIGURATION || prevMode == MODE.FEATURE_CONFIGURATION ) {
-                            this.configuration.properties = textValue;
+                            ByteArrayInputStream bais = null;
+                            try {
+                                bais = new ByteArrayInputStream(textValue.getBytes("UTF-8"));
+                                @SuppressWarnings("unchecked")
+                                final Dictionary<String, Object> props = ConfigurationHandler.read(bais);
+                                final Enumeration<String> e = props.keys();
+                                while ( e.hasMoreElements() ) {
+                                    final String key = e.nextElement();
+                                    this.configuration.addProperty(key, props.get(key));
+                                }
+                            } catch ( final IOException ioe ) {
+                                throw new SAXException(ioe);
+                            } finally {
+                                if ( bais != null ) {
+                                    try {
+                                        bais.close();
+                                    } catch ( final IOException ignore ) {
+                                        // ignore
+                                    }
+                                }
+                            }
                             this.configuration = null;
                         } else if ( prevMode == MODE.SETTINGS || prevMode == MODE.FEATURE_SETTINGS) {
-                            this.feature.settings.properties = textValue;
+                            this.feature.getSettings().properties = textValue;
                         } else if ( prevMode == MODE.FEATURE ) {
                             this.feature = result.getOrCreateFeature(null);
                             this.startLevel = 0;
@@ -231,7 +252,7 @@ public class XMLSSMModelReader {
                                     if ( pos == -1 || line.indexOf("=", pos + 1 ) != -1 ) {
                                         throw new SAXException("Invalid property definition: " + line);
                                     }
-                                    result.properties.put(line.substring(0, pos), line.substring(pos + 1));
+                                    result.addProperty(line.substring(0, pos), line.substring(pos + 1));
                                 }
                             } catch (final IOException io) {
                                 throw new SAXException(io);
diff --git a/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelWriter.java b/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelWriter.java
index 1b73e7a..510cdb7 100644
--- a/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelWriter.java
+++ b/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelWriter.java
@@ -16,11 +16,13 @@
  */
 package org.apache.sling.slingstart.model.xml;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.Writer;
 import java.util.Map;
 
+import org.apache.felix.cm.file.ConfigurationHandler;
 import org.apache.sling.slingstart.model.SSMArtifact;
 import org.apache.sling.slingstart.model.SSMConfiguration;
 import org.apache.sling.slingstart.model.SSMDeliverable;
@@ -33,10 +35,10 @@ import org.apache.sling.slingstart.model.SSMStartLevel;
 public class XMLSSMModelWriter {
 
     private static void printRunModeAttribute(final PrintWriter pw, final SSMFeature rmd) {
-        if ( rmd.runModes != null && rmd.runModes.length > 0 ) {
+        if ( rmd.getRunModes() != null && rmd.getRunModes().length > 0 ) {
             pw.print(" modes=\"");
             boolean first = true;
-            for(final String mode : rmd.runModes) {
+            for(final String mode : rmd.getRunModes()) {
                 if ( first ) {
                     first = false;
                 } else {
@@ -66,10 +68,10 @@ public class XMLSSMModelWriter {
         pw.println("<deliverable>");
 
         // properties
-        if ( subsystem.properties.size() > 0 ) {
+        if ( subsystem.getProperties().size() > 0 ) {
             pw.print(INDENT);
             pw.println("<properties><![CDATA[");
-            for(final Map.Entry<String, String> entry : subsystem.properties.entrySet()) {
+            for(final Map.Entry<String, String> entry : subsystem.getProperties().entrySet()) {
                 pw.print(INDENT);
                 pw.print(INDENT);
                 pw.print(entry.getKey());
@@ -79,10 +81,10 @@ public class XMLSSMModelWriter {
             pw.print(INDENT);
             pw.println("]]></properties>");
         }
-        for(final SSMFeature feature : subsystem.features) {
+        for(final SSMFeature feature : subsystem.getFeatures()) {
             // TODO - don't write out empty features
             String indent = INDENT;
-            if ( feature.runModes != null ) {
+            if ( feature.getRunModes() != null ) {
                 pw.print(indent);
                 pw.print("<feature");
                 printRunModeAttribute(pw, feature);
@@ -90,15 +92,15 @@ public class XMLSSMModelWriter {
                 indent = indent + INDENT;
             }
 
-            for(final SSMStartLevel startLevel : feature.startLevels) {
+            for(final SSMStartLevel startLevel : feature.getStartLevels()) {
                 if ( startLevel.artifacts.size() == 0 ) {
                     continue;
                 }
-                if ( startLevel.level != 0 ) {
+                if ( startLevel.getLevel() != 0 ) {
                     pw.print(indent);
                     pw.print("<startLevel");
                     pw.print(" level=\"");
-                    pw.print(String.valueOf(startLevel.level));
+                    pw.print(String.valueOf(startLevel.getLevel()));
                     pw.print("\"");
                     pw.println(">");
                     indent += INDENT;
@@ -124,38 +126,46 @@ public class XMLSSMModelWriter {
                     }
                     pw.println("/>");
                 }
-                if ( startLevel.level != 0 ) {
+                if ( startLevel.getLevel() != 0 ) {
                     indent = indent.substring(0, indent.length() - INDENT.length());
                     pw.print(indent);
                     pw.println("</startLevel>");
                 }
             }
 
-            for(final SSMConfiguration config : feature.configurations) {
+            for(final SSMConfiguration config : feature.getConfigurations()) {
                 pw.print(indent);
                 pw.print("<configuration ");
-                if ( config.factoryPid != null ) {
+                if ( config.getFactoryPid() != null ) {
                     pw.print("factory=\"");
-                    pw.print(escapeXml(config.factoryPid));
+                    pw.print(escapeXml(config.getFactoryPid()));
                     pw.print("\" ");
                 }
                 pw.print("pid=\"");
-                pw.print(escapeXml(config.pid));
+                pw.print(escapeXml(config.getPid()));
                 pw.println("\"><![CDATA[");
-                pw.println(config.properties);
+                final ByteArrayOutputStream os = new ByteArrayOutputStream();
+                try {
+                    ConfigurationHandler.write(os , config.getProperties());
+                } finally {
+                    os.close();
+                }
+                final String configString = new String(os.toByteArray(), "UTF-8");
+                pw.println(configString);
+                pw.println(config.getProperties());
                 pw.print(indent);
                 pw.println("]]></configuration>");
             }
 
-            if ( feature.settings != null ) {
+            if ( feature.getSettings() != null ) {
                 pw.print(indent);
                 pw.println("<settings><![CDATA[");
-                pw.println(feature.settings.properties);
+                pw.println(feature.getSettings().properties);
                 pw.print(indent);
                 pw.println("]]></settings>");
             }
 
-            if ( feature.runModes != null ) {
+            if ( feature.getRunModes() != null ) {
                 indent = indent.substring(0, indent.length() - INDENT.length());
                 pw.print(indent);
                 pw.println("</feature>");

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.