You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2017/05/29 08:12:55 UTC

svn commit: r1796586 - in /sling/whiteboard/cziegeler/provisioning-model: ./ src/main/java/org/apache/sling/feature/ src/main/java/org/apache/sling/feature/io/ src/main/java/org/apache/sling/provisioning/

Author: cziegeler
Date: Mon May 29 08:12:55 2017
New Revision: 1796586

URL: http://svn.apache.org/viewvc?rev=1796586&view=rev
Log:
Update model and use configurator format for configurations

Added:
    sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Bundles.java   (with props)
Removed:
    sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Bundle.java
    sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/provisioning/
Modified:
    sling/whiteboard/cziegeler/provisioning-model/pom.xml
    sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Application.java
    sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Artifact.java
    sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/ArtifactId.java
    sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Configuration.java
    sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Feature.java
    sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Include.java
    sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/io/FeatureReader.java

Modified: sling/whiteboard/cziegeler/provisioning-model/pom.xml
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/provisioning-model/pom.xml?rev=1796586&r1=1796585&r2=1796586&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/provisioning-model/pom.xml (original)
+++ sling/whiteboard/cziegeler/provisioning-model/pom.xml Mon May 29 08:12:55 2017
@@ -61,6 +61,18 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.converter</artifactId>
+            <version>0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.configurator</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
         	<groupId>junit</groupId>
         	<artifactId>junit</artifactId>
         </dependency>

Modified: sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Application.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Application.java?rev=1796586&r1=1796585&r2=1796586&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Application.java (original)
+++ sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Application.java Mon May 29 08:12:55 2017
@@ -32,7 +32,7 @@ import java.util.List;
  */
 public class Application {
 
-    private final List<Bundle> bundles = new ArrayList<>();
+    private final Bundles bundles = new Bundles();
 
     private final List<Configuration> configurations = new ArrayList<>();
 
@@ -45,11 +45,10 @@ public class Application {
     private ArtifactId framework;
 
     /**
-     * Get the list of bundles.
-     * The list can be modified
-     * @return The list of bundles.
+     * Get the bundles
+     * @return The bundles object.
      */
-    public List<Bundle> getBundles() {
+    public Bundles getBundles() {
         return this.bundles;
     }
 

Modified: sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Artifact.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Artifact.java?rev=1796586&r1=1796585&r2=1796586&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Artifact.java (original)
+++ sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Artifact.java Mon May 29 08:12:55 2017
@@ -92,7 +92,7 @@ public class Artifact implements Compara
 
     @Override
     public String toString() {
-        return "Artifact [id=" + id.toMvnUrl().substring(4)
+        return "Artifact [id=" + id.toMvnId()
                 + "]";
     }
 }

Modified: sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/ArtifactId.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/ArtifactId.java?rev=1796586&r1=1796585&r2=1796586&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/ArtifactId.java (original)
+++ sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/ArtifactId.java Mon May 29 08:12:55 2017
@@ -84,7 +84,17 @@ public class ArtifactId implements Compa
         if ( url == null || !url.startsWith("mvn:") ) {
             throw new IllegalArgumentException("Invalid mvn url: " + url);
         }
-        final String content = url.substring(4);
+        return fromMvnId(url.substring(4));
+    }
+
+    /**
+     * Create a new artifact from a maven url,
+     * [ repository-url '!' ] group-id '/' artifact-id [ '/' [version] [ '/' [type] [ '/' classifier ] ] ] ]
+     * @param content The id
+     * @return A new artifact
+     * @throws IllegalArgumentException If the id is not valid
+     */
+    public static ArtifactId fromMvnId(final String content) {
         // ignore repository url
         int pos = content.indexOf('!');
         if ( pos != -1 ) {
@@ -136,7 +146,14 @@ public class ArtifactId implements Compa
      * @see #fromMvnUrl(String)
      */
     public String toMvnUrl() {
-        final StringBuilder sb = new StringBuilder("mvn:");
+        return toId(new StringBuilder("mvn:"));
+    }
+
+    public String toMvnId() {
+        return toId(new StringBuilder());
+    }
+
+    private String toId(final StringBuilder sb) {
         sb.append(this.groupId);
         sb.append('/');
         sb.append(this.artifactId);
@@ -258,6 +275,6 @@ public class ArtifactId implements Compa
 
     @Override
     public String toString() {
-        return toMvnUrl().substring(4);
+        return toMvnId();
     }
 }

Added: sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Bundles.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Bundles.java?rev=1796586&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Bundles.java (added)
+++ sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Bundles.java Mon May 29 08:12:55 2017
@@ -0,0 +1,74 @@
+/*
+ * 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.sling.feature;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Bundles groups bundle {@code Artifact}s by start level.
+ */
+public class Bundles {
+
+    private final Map<Integer, List<Artifact>> startLevelMap = new TreeMap<>();
+
+    /**
+     * Get the map of all bundles sorted by start level. The map is sorted
+     * and iterating over the keys is done in start level order.
+     * @return The map of bundles. The map is unmodifiable.
+     */
+    public Map<Integer, List<Artifact>> getBundlesByStartLevel() {
+        return Collections.unmodifiableMap(this.startLevelMap);
+    }
+
+    @Override
+    public String toString() {
+        return "Bundles [" + this.startLevelMap
+                + "]";
+    }
+
+    public void add(final int startLevel, final Artifact bundle) {
+        List<Artifact> list = this.startLevelMap.get(startLevel);
+        if ( list == null ) {
+            list = new ArrayList<>();
+            this.startLevelMap.put(startLevel, list);
+        }
+        list.add(bundle);
+    }
+
+    public boolean remove(final ArtifactId id) {
+        for(final Map.Entry<Integer, List<Artifact>> entry : this.startLevelMap.entrySet()) {
+            for(final Artifact artifact : entry.getValue()) {
+                if ( artifact.getId().equals(id)) {
+                    entry.getValue().remove(artifact);
+                    if ( entry.getValue().isEmpty() ) {
+                        this.startLevelMap.remove(entry.getKey());
+                    }
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public void clear() {
+        this.startLevelMap.clear();
+    }
+}

Propchange: sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Bundles.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Bundles.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Modified: sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Configuration.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Configuration.java?rev=1796586&r1=1796585&r2=1796586&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Configuration.java (original)
+++ sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Configuration.java Mon May 29 08:12:55 2017
@@ -23,13 +23,13 @@ import java.util.Hashtable;
 /**
  * A configuration has either
  * - a pid
- * - or a factory pid and an alias (pid)
+ * - or a factory pid and a name (pid)
  * and properties.
  */
 public class Configuration
     implements Comparable<Configuration> {
 
-    /** The pid. */
+    /** The pid or name for factory pids. */
     private final String pid;
 
     /** The factory pid. */
@@ -40,12 +40,29 @@ public class Configuration
 
     /**
      * Create a new configuration
-     * @param pid The pid or alias for a factory pid
+     * @param pid The pid
+     * @throws IllegalArgumentException If pid is {@code null}
+     */
+    public Configuration(final String pid) {
+        if ( pid == null ) {
+            throw new IllegalArgumentException("pid must not be null");
+        }
+        this.pid = pid;
+        this.factoryPid = null;
+    }
+
+    /**
+     * Create a new factor configuration
      * @param factoryPid The factory pid
+     * @param name The name of the factory pid
+     * @throws IllegalArgumentException If factoryPid or name is {@code null}
      */
-    public Configuration(final String pid, final String factoryPid) {
-        this.pid = (pid != null ? pid.trim() : null);
-        this.factoryPid = (factoryPid != null ? factoryPid.trim() : null);
+    public Configuration(final String factoryPid, final String name) {
+        if ( factoryPid == null || name == null ) {
+            throw new IllegalArgumentException("factoryPid and/or name must not be null");
+        }
+        this.pid = name;
+        this.factoryPid = factoryPid;
     }
 
     private int compareString(final String a, final String b) {
@@ -73,21 +90,35 @@ public class Configuration
 
     /**
      * Get the pid.
-     * If this is a factory configuration, it returns the alias for the configuration
-     * @return The pid.
+     * If this is a factory configuration, it returns {@code null}
+     * @return The pid or {@code null}
      */
     public String getPid() {
+        if ( this.isFactoryConfiguration() ) {
+            return null;
+        }
         return this.pid;
     }
 
     /**
      * Return the factory pid
-     * @return The factory pid or null.
+     * @return The factory pid or {@code null}.
      */
     public String getFactoryPid() {
         return this.factoryPid;
     }
 
+    public String getName() {
+        if ( this.isFactoryConfiguration() ) {
+            return this.pid;
+        }
+        return null;
+    }
+
+    public boolean isFactoryConfiguration() {
+        return this.factoryPid != null;
+    }
+
     /**
      * Get all properties of the configuration.
      * @return The properties
@@ -98,8 +129,13 @@ public class Configuration
 
     @Override
     public String toString() {
+        if ( this.isFactoryConfiguration() ) {
+            return "Factory Configuration [factoryPid=" + factoryPid
+                    + ", name=" + pid
+                    + ", properties=" + properties
+                    + "]";
+        }
         return "Configuration [pid=" + pid
-                + ", factoryPid=" + factoryPid
                 + ", properties=" + properties
                 + "]";
     }

Modified: sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Feature.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Feature.java?rev=1796586&r1=1796585&r2=1796586&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Feature.java (original)
+++ sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Feature.java Mon May 29 08:12:55 2017
@@ -18,8 +18,6 @@ package org.apache.sling.feature;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.SortedMap;
-import java.util.TreeMap;
 
 /**
  * A feature consists of
@@ -37,7 +35,7 @@ public class Feature implements Comparab
 
     private final ArtifactId id;
 
-    private final List<Bundle> bundles = new ArrayList<>();
+    private final Bundles bundles = new Bundles();
 
     private final List<Configuration> configurations = new ArrayList<>();
 
@@ -94,27 +92,13 @@ public class Feature implements Comparab
     }
 
     /**
-     * Get the list of bundles.
-     * The list can be modified
-     * @return The list of bundles.
+     * Get the bundles.
+     * @return The bundles object.
      */
-    public List<Bundle> getBundles() {
+    public Bundles getBundles() {
         return this.bundles;
     }
 
-    public SortedMap<Integer, List<Bundle>> getBundlesByStartLevel() {
-        final SortedMap<Integer, List<Bundle>> map = new TreeMap<>();
-        for(final Bundle b : this.getBundles()) {
-            List<Bundle> list = map.get(b.getStartLevel());
-            if ( list == null ) {
-                list = new ArrayList<>();
-                map.put(b.getStartLevel(), list);
-            }
-            list.add(b);
-        }
-        return map;
-    }
-
     public List<Configuration> getConfigurations() {
         return this.configurations;
     }
@@ -162,7 +146,7 @@ public class Feature implements Comparab
 
     @Override
     public String toString() {
-        return "Feature [id=" + this.getId().toMvnUrl().substring(4)
+        return "Feature [id=" + this.getId().toMvnId()
                 + ( this.getLocation() != null ? ", location=" + this.getLocation() : "")
                 + "]";
     }

Modified: sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Include.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Include.java?rev=1796586&r1=1796585&r2=1796586&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Include.java (original)
+++ sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/Include.java Mon May 29 08:12:55 2017
@@ -16,13 +16,28 @@
  */
 package org.apache.sling.feature;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 /**
- * A include is an inclusion of a feature
+ * A include is an inclusion of a feature with optional removals
  */
 public class Include implements Comparable<Include> {
 
     private final ArtifactId id;
 
+    private final List<String> configurationRemovals = new ArrayList<>();
+
+    private final List<ArtifactId> bundleRemovals = new ArrayList<>();
+
+    private final List<String> frameworkPropertiesRemovals = new ArrayList<>();
+
+    private final List<String> textExtensionRemovals = new ArrayList<>();
+
+    private final Map<String, List<Artifact>> artifactExtensionRemovals = new HashMap<>();
+
     /**
      * Construct a new Include.
      * @param id The id of the feature.
@@ -43,6 +58,26 @@ public class Include implements Comparab
         return this.id;
     }
 
+    public List<String> getConfigurationRemovals() {
+        return configurationRemovals;
+    }
+
+    public List<ArtifactId> getBundleRemovals() {
+        return bundleRemovals;
+    }
+
+    public List<String> getFrameworkPropertiesRemovals() {
+        return frameworkPropertiesRemovals;
+    }
+
+    public List<String> getTextExtensionRemovals() {
+        return textExtensionRemovals;
+    }
+
+    public Map<String, List<Artifact>> getArtifactExtensionRemovals() {
+        return artifactExtensionRemovals;
+    }
+
     @Override
     public int compareTo(final Include o) {
         return this.id.compareTo(o.id);
@@ -66,7 +101,7 @@ public class Include implements Comparab
 
     @Override
     public String toString() {
-        return "Include [id=" + id.toMvnUrl().substring(4)
+        return "Include [id=" + id.toMvnId()
                 + "]";
     }
 }

Modified: sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/io/FeatureReader.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/io/FeatureReader.java?rev=1796586&r1=1796585&r2=1796586&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/io/FeatureReader.java (original)
+++ sling/whiteboard/cziegeler/provisioning-model/src/main/java/org/apache/sling/feature/io/FeatureReader.java Mon May 29 08:12:55 2017
@@ -18,12 +18,30 @@ package org.apache.sling.feature.io;
 
 import java.io.IOException;
 import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
 import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
 
+import org.apache.felix.configurator.impl.TypeConverter;
+import org.apache.felix.configurator.impl.json.JSMin;
+import org.apache.felix.configurator.impl.json.JSONUtil;
+import org.apache.felix.configurator.impl.model.Config;
+import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Feature;
 
 /**
@@ -31,33 +49,231 @@ import org.apache.sling.feature.Feature;
  */
 public class FeatureReader {
 
-    private final String location;
-
+    /**
+     * Read a new feature from the reader
+     * The reader is not closed. It is up to the caller to close the reader.
+     * @param reader The reader for the feature
+     * @param location Optional location
+     * @return The read feature
+     * @throws IOException If an IO errors occurs or the JSON is invalid.
+     */
     public static Feature read(final Reader reader, final String location)
     throws IOException {
+        // minify JSON first (remove comments)
+        final String contents;
+        try ( final Writer out = new StringWriter()) {
+            final JSMin min = new JSMin(reader, out);
+            min.jsmin();
+            contents = out.toString();
+        }
+
         final FeatureReader mr = new FeatureReader(location);
-        return mr.readFeature(reader);
+        return mr.readFeature(new StringReader(contents));
     }
 
+    private static final String FEATURE_ID = "id";
+
+    private static final String FEATURE_BUNDLES = "bundles";
+
+    private static final String FEATURE_FRAMEWORK_PROPERTIES = "framework-properties";
+
+    private static final String FEATURE_CONFIGURATIONS = "configurations";
+
+    private static final String FEATURE_INCLUDES = "includes";
+
+    private static final String FEATURE_REQUIREMENTS = "requirements";
+
+    private static final String FEATURE_CAPABILITIES = "capabilities";
+
+    private Feature feature;
+
+    private final String location;
+
+    private final String exceptionPrefix;
+
+    /**
+     * Private constructor
+     * @param location Optional location
+     */
     private FeatureReader(final String location) {
         this.location = location;
+        if ( location == null ) {
+            exceptionPrefix = "";
+        } else {
+            exceptionPrefix = location + " : ";
+        }
     }
 
+    /**
+     * Read a full feature
+     * @param reader The reader
+     * @return The feature object
+     * @throws IOException If an IO error occurs or the JSON is not valid.
+     */
     private Feature readFeature(final Reader reader)
     throws IOException {
-        try {
-            final JsonObject obj = Json.createReader(reader).readObject();
+        final JsonObject json = Json.createReader(reader).readObject();
+
+        @SuppressWarnings("unchecked")
+        final Map<String, Object> map = (Map<String, Object>) getValue(json);
+
+        final Set<String> keySet = map.keySet();
+        keySet.removeAll(Arrays.asList(FEATURE_ID,
+                FEATURE_BUNDLES,
+                FEATURE_FRAMEWORK_PROPERTIES,
+                FEATURE_CONFIGURATIONS,
+                FEATURE_INCLUDES,
+                FEATURE_REQUIREMENTS,
+                FEATURE_CAPABILITIES));
+        if ( !map.containsKey(FEATURE_ID) ) {
+            throw new IOException("Feature id is missing in " + this.location);
+        }
+        final Object idObj = map.get(FEATURE_ID);
+        checkType(FEATURE_ID, idObj, String.class);
+
+        this.feature = new Feature(ArtifactId.fromMvnId(idObj.toString()));
+        this.feature.setLocation(this.location);
+
+        this.readBundles(map);
+        this.readFrameworkProperties(map);
+        this.readConfigurations(map);
+
+        return feature;
+    }
+
+    /**
+     * Read the bundles / start levels section
+     * @param map The map describing the feature
+     * @throws IOException If the json is invalid.
+     */
+    private void readBundles(final Map<String, Object> map) throws IOException {
+        if ( map.containsKey(FEATURE_BUNDLES)) {
+            final Object bundlesObj = map.get(FEATURE_BUNDLES);
+            checkType(FEATURE_BUNDLES, bundlesObj, Map.class);
+            @SuppressWarnings("unchecked")
+            final Map<String, Object> bundles = (Map<String, Object>) bundlesObj;
+            for(final String startLevelVal : bundles.keySet()) {
+                int startLevel = -1;
+                try {
+                    startLevel = Integer.valueOf(startLevelVal);
+                } catch ( final NumberFormatException nfe) {
+                    throw new IOException(this.exceptionPrefix + "Illegal start level '" + startLevelVal + "'");
+                }
+                final Object val = bundles.get(startLevelVal);
+                final List<Artifact> list = new ArrayList<>();
+                readArtifacts("startLevel", "bundle", list, val);
+                for(final Artifact a : list) {
+                    this.feature.getBundles().add(startLevel, a);
+                }
+            }
+        }
+    }
+
+    private void readArtifacts(final String section,
+            final String artifactType,
+            final List<Artifact> artifacts,
+            final Object listObj)
+    throws IOException {
+        checkType(section, listObj, List.class);
+        @SuppressWarnings("unchecked")
+        final List<Object> list = (List<Object>) listObj;
+        for(final Object entry : list) {
+            final Artifact artifact;
+            checkType(artifactType, entry, Map.class, String.class);
+            if ( entry instanceof String ) {
+                artifact = new Artifact(ArtifactId.fromMvnId(entry.toString()));
+            } else {
+                // TODO - Object
+                final Map<String, Object> bundleObj = (Map<String, Object>) entry;
+                artifact = null;
+            }
+            artifacts.add(artifact);
+        }
+    }
+
+    private void readConfigurations(final Map<String, Object> map) throws IOException {
+        if ( map.containsKey(FEATURE_CONFIGURATIONS) ) {
+            // TODO JSONUtil prints out warnings and errors through SystemLogger - we need to catch them
+            //      and throw an IOExceptio
+            final List<Config> configs = JSONUtil.readConfigurationsJSON(new TypeConverter(null),
+                    0, "", map);
+            for(final Config c : configs) {
+                if ( c.getEnvironments() != null ) {
+                    throw new IOException(this.exceptionPrefix + "Environments for configurations are not supported");
+                }
+                final int pos = c.getPid().indexOf('~');
+                final Configuration config;
+                if ( pos != -1 ) {
+                    config = new Configuration(c.getPid().substring(0, pos), c.getPid().substring(pos + 1));
+                } else {
+                    config = new Configuration(c.getPid());
+                }
+                this.feature.getConfigurations().add(config);
+            }
+        }
+    }
+
+    private void readFrameworkProperties(final Map<String, Object> map) throws IOException {
+        if ( map.containsKey(FEATURE_FRAMEWORK_PROPERTIES) ) {
+            final Object propsObj= map.get(FEATURE_FRAMEWORK_PROPERTIES);
+            checkType(FEATURE_FRAMEWORK_PROPERTIES, propsObj, Map.class);
+
+            @SuppressWarnings("unchecked")
+            final Map<String, Object> props = (Map<String, Object>) propsObj;
+            for(final Map.Entry<String, Object> entry : props.entrySet()) {
+                checkType("framework property value", entry.getValue(), String.class, Boolean.class, Number.class);
+                if ( feature.getFrameworkProperties().get(entry.getKey()) != null ) {
+                    throw new IOException(this.exceptionPrefix + "Duplicate framework property " + entry.getKey());
+                }
+                feature.getFrameworkProperties().put(entry.getKey(), entry.getValue().toString());
+            }
+
+        }
+    }
+
+    private void checkType(final String key, final Object val, Class<?>...types) throws IOException {
+        boolean valid = false;
+        for(final Class<?> c : types) {
+            if ( c.isInstance(val) ) {
+                valid = true;
+                break;
+            }
+        }
+        if ( !valid ) {
+            throw new IOException(this.exceptionPrefix + "Key " + key + " is not one of the allowed types " + Arrays.toString(types) + " : " + val.getClass());
+        }
+    }
 
-            final Set<String> keySet = obj.keySet();
-            keySet.remove("id");
-            final String id = obj.getString("id");
-            final Feature feature = new Feature(ArtifactId.fromMvnUrl("mvn:" + id));
-
-            final JsonObject bundles = obj.getJsonObject("bundles");
-            return feature;
-        } catch ( final IllegalArgumentException e) {
-            throw new IOException("Unable to read feature from " + this.location, e);
+    private static Object getValue(final JsonValue value) {
+        switch ( value.getValueType() ) {
+            // type NULL -> return null
+            case NULL : return null;
+            // type TRUE or FALSE -> return boolean
+            case FALSE : return false;
+            case TRUE : return true;
+            // type String -> return String
+            case STRING : return ((JsonString)value).getString();
+            // type Number -> return long or double
+            case NUMBER : final JsonNumber num = (JsonNumber)value;
+                          if (num.isIntegral()) {
+                               return num.longValue();
+                          }
+                          return num.doubleValue();
+            // type ARRAY -> return list and call this method for each value
+            case ARRAY : final List<Object> array = new ArrayList<>();
+                         for(final JsonValue x : ((JsonArray)value)) {
+                             array.add(getValue(x));
+                         }
+                         return array;
+            // type OBJECT -> return map
+            case OBJECT : final Map<String, Object> map = new HashMap<>();
+                          final JsonObject obj = (JsonObject)value;
+                          for(final Map.Entry<String, JsonValue> entry : obj.entrySet()) {
+                              map.put(entry.getKey(), getValue(entry.getValue()));
+                          }
+                          return map;
         }
+        return null;
     }
 }