You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by da...@apache.org on 2018/04/25 09:59:28 UTC

[sling-whiteboard] branch master updated: [Sling Feature Model] Split off IO packages into separate module.

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

davidb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git


The following commit(s) were added to refs/heads/master by this push:
     new dfba5b9  [Sling Feature Model] Split off IO packages into separate module.
dfba5b9 is described below

commit dfba5b9ac82293bd79de34a158adc3fe92148406
Author: David Bosschaert <da...@gmail.com>
AuthorDate: Wed Apr 25 10:58:43 2018 +0100

    [Sling Feature Model] Split off IO packages into separate module.
---
 featuremodel/feature-analyser/pom.xml              |   6 +
 featuremodel/feature-applicationbuilder/pom.xml    |   6 +
 .../{feature-support => feature-io}/pom.xml        |  56 +--
 .../apache/sling/feature/io/ArtifactHandler.java   |   0
 .../apache/sling/feature/io/ArtifactManager.java   |   0
 .../sling/feature/io/ArtifactManagerConfig.java    |   0
 .../org/apache/sling/feature/io/FileUtils.java     |   0
 .../feature/io/json/ApplicationJSONReader.java     |   0
 .../feature/io/json/ApplicationJSONWriter.java     |   0
 .../feature/io/json/ConfigurationJSONReader.java   |   0
 .../feature/io/json/ConfigurationJSONWriter.java   |   0
 .../sling/feature/io/json/FeatureJSONReader.java   |  11 +-
 .../sling/feature/io/json/FeatureJSONWriter.java   |  11 +-
 .../sling/feature/io/json/JSONConstants.java       |   0
 .../sling/feature/io/json/JSONReaderBase.java      |   0
 .../sling/feature/io/json/JSONWriterBase.java      |   0
 .../sling/feature/io/json/ManifestUtils.java       | 515 +++++++++++++++++++++
 .../apache/sling/feature/io/json/package-info.java |   0
 .../org/apache/sling/feature/io/package-info.java  |   0
 .../sling/feature/io/spi/ArtifactProvider.java     |   0
 .../feature/io/spi/ArtifactProviderContext.java    |   0
 .../apache/sling/feature/io/spi/package-info.java  |   0
 .../sling/feature/io/ArtifactManagerTest.java      |   0
 .../apache/sling/feature/io/FeatureUtilTest.java   |   0
 .../feature/io/json/FeatureJSONReaderTest.java     |   0
 .../feature/io/json/FeatureJSONWriterTest.java     |   0
 .../java/org/apache/sling/feature/io/json/U.java   |   0
 .../src/test/resources/features/repoinit.json      |   0
 .../src/test/resources/features/repoinit2.json     |   0
 .../src/test/resources/features/test.json}         |  41 +-
 .../src/test/resources/features/test2.json         |   0
 .../src/test/resources/features/test3.json         |   0
 featuremodel/feature-karaf/pom.xml                 |   6 +
 featuremodel/feature-launcher/pom.xml              |   5 +
 featuremodel/feature-modelconverter/pom.xml        |   6 +
 featuremodel/feature-resolver/pom.xml              |   6 +
 featuremodel/feature-support/pom.xml               |   6 +
 .../support/util/CapabilityMatcherTest.java        |   5 +-
 .../sling/feature/{io/json => support/util}/U.java |   2 +-
 featuremodel/osgifeature-maven-plugin/pom.xml      |   5 +
 featuremodel/pom.xml                               |   1 +
 41 files changed, 611 insertions(+), 77 deletions(-)

diff --git a/featuremodel/feature-analyser/pom.xml b/featuremodel/feature-analyser/pom.xml
index d600de8..a2bca4b 100644
--- a/featuremodel/feature-analyser/pom.xml
+++ b/featuremodel/feature-analyser/pom.xml
@@ -120,6 +120,12 @@
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.io</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature.support</artifactId>
             <version>0.0.1-SNAPSHOT</version>
             <scope>provided</scope>
diff --git a/featuremodel/feature-applicationbuilder/pom.xml b/featuremodel/feature-applicationbuilder/pom.xml
index 70f0bfb..3cb9e08 100644
--- a/featuremodel/feature-applicationbuilder/pom.xml
+++ b/featuremodel/feature-applicationbuilder/pom.xml
@@ -110,6 +110,12 @@
         </dependency>        
         <dependency>
             <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.io</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature.resolver</artifactId>
             <version>0.0.1-SNAPSHOT</version>
             <scope>provided</scope>
diff --git a/featuremodel/feature-support/pom.xml b/featuremodel/feature-io/pom.xml
similarity index 89%
copy from featuremodel/feature-support/pom.xml
copy to featuremodel/feature-io/pom.xml
index 61fa224..8053a89 100644
--- a/featuremodel/feature-support/pom.xml
+++ b/featuremodel/feature-io/pom.xml
@@ -20,25 +20,19 @@
         <relativePath />
     </parent>
 
-    <artifactId>org.apache.sling.feature.support</artifactId>
+    <artifactId>org.apache.sling.feature.io</artifactId>
     <version>0.0.1-SNAPSHOT</version>
     <packaging>bundle</packaging>
     
-    <name>Apache Sling Feature Support</name>
+    <name>Apache Sling Feature IO Module</name>
     <description>
-        Support classes for the feature tools
+        IO functionality for the Feature Model
     </description>
 
     <properties>
         <sling.java.version>8</sling.java.version>
     </properties>
 
-    <scm>
-        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/tooling/support/feature-support</connection>
-        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/tooling/support/feature-support</developerConnection>
-        <url>http://svn.apache.org/viewvc/sling/trunk/tooling/support/feature-support</url>
-    </scm>
-
     <build>
         <plugins>
             <plugin>
@@ -59,28 +53,6 @@
 
     <dependencies>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.annotation.versioning</artifactId>
-            <version>1.0.0</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>osgi.core</artifactId>
-            <version>6.0.0</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.feature</artifactId>
-            <version>0.0.1-SNAPSHOT</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.converter</artifactId>
             <version>0.1.0-SNAPSHOT</version>
@@ -104,6 +76,28 @@
             <version>1.0-alpha-1</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.annotation.versioning</artifactId>
+            <version>1.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.core</artifactId>
+            <version>6.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
 
         <!-- Testing -->
         <dependency>
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/ArtifactHandler.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/ArtifactHandler.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/ArtifactHandler.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/ArtifactHandler.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/ArtifactManager.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/ArtifactManager.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/ArtifactManager.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/ArtifactManager.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/ArtifactManagerConfig.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/ArtifactManagerConfig.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/ArtifactManagerConfig.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/ArtifactManagerConfig.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/FileUtils.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/FileUtils.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/FileUtils.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/FileUtils.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/ApplicationJSONReader.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/ApplicationJSONReader.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/ApplicationJSONReader.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/ApplicationJSONReader.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/ApplicationJSONWriter.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/ApplicationJSONWriter.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/ApplicationJSONWriter.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/ApplicationJSONWriter.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONReader.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONReader.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONReader.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONReader.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONWriter.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONWriter.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONWriter.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONWriter.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java
similarity index 97%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java
index 772f7ef..0d21598 100644
--- a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java
+++ b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java
@@ -39,9 +39,6 @@ import java.util.regex.Pattern;
 import javax.json.Json;
 import javax.json.JsonObject;
 
-import static org.apache.sling.feature.support.util.ManifestUtil.unmarshalAttribute;
-import static org.apache.sling.feature.support.util.ManifestUtil.unmarshalDirective;
-
 /**
  * This class offers a method to read a {@code Feature} using a {@code Reader} instance.
  */
@@ -354,7 +351,7 @@ public class FeatureJSONReader extends JSONReaderBase {
                     checkType("Requirement attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES), Map.class);
                     @SuppressWarnings("unchecked")
                     final Map<String, Object> attrs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_ATTRIBUTES);
-                    attrs.forEach(rethrowBiConsumer((key, value) -> unmarshalAttribute(key, handleResolveVars(value), attrMap::put)));
+                    attrs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalAttribute(key, handleResolveVars(value), attrMap::put)));
                 }
 
                 Map<String, String> dirMap = new HashMap<>();
@@ -362,7 +359,7 @@ public class FeatureJSONReader extends JSONReaderBase {
                     checkType("Requirement directives", obj.get(JSONConstants.REQCAP_DIRECTIVES), Map.class);
                     @SuppressWarnings("unchecked")
                     final Map<String, Object> dirs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_DIRECTIVES);
-                    dirs.forEach(rethrowBiConsumer((key, value) -> unmarshalDirective(key, handleResolveVars(value), dirMap::put)));
+                    dirs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalDirective(key, handleResolveVars(value), dirMap::put)));
                 }
 
                 final Requirement r = new RequirementImpl(null, handleResolveVars(obj.get(JSONConstants.REQCAP_NAMESPACE)).toString(), dirMap, attrMap);
@@ -393,7 +390,7 @@ public class FeatureJSONReader extends JSONReaderBase {
                     checkType("Capability attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES), Map.class);
                     @SuppressWarnings("unchecked")
                     final Map<String, Object> attrs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_ATTRIBUTES);
-                    attrs.forEach(rethrowBiConsumer((key, value) -> unmarshalAttribute(key, handleResolveVars(value), attrMap::put)));
+                    attrs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalAttribute(key, handleResolveVars(value), attrMap::put)));
                 }
 
                 Map<String, String> dirMap = new HashMap<>();
@@ -401,7 +398,7 @@ public class FeatureJSONReader extends JSONReaderBase {
                     checkType("Capability directives", obj.get(JSONConstants.REQCAP_DIRECTIVES), Map.class);
                     @SuppressWarnings("unchecked")
                     final Map<String, Object> dirs = (Map<String, Object>) obj.get(JSONConstants.REQCAP_DIRECTIVES);
-                    dirs.forEach(rethrowBiConsumer((key, value) -> unmarshalDirective(key, handleResolveVars(value), dirMap::put)));
+                    dirs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalDirective(key, handleResolveVars(value), dirMap::put)));
                 }
 
                 final Capability c = new CapabilityImpl(null, handleResolveVars(obj.get(JSONConstants.REQCAP_NAMESPACE)).toString(), dirMap, attrMap);
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java
similarity index 96%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java
index 423def4..57428e1 100644
--- a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java
+++ b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java
@@ -37,9 +37,6 @@ import javax.json.JsonWriter;
 import javax.json.JsonWriterFactory;
 import javax.json.stream.JsonGenerator;
 
-import static org.apache.sling.feature.support.util.ManifestUtil.marshalAttribute;
-import static org.apache.sling.feature.support.util.ManifestUtil.marshalDirective;
-
 /**
  * Simple JSON writer for a feature
  */
@@ -147,12 +144,12 @@ public class FeatureJSONWriter extends JSONWriterBase {
                 requirementObj.add(JSONConstants.REQCAP_NAMESPACE, req.getNamespace());
                 if ( !req.getAttributes().isEmpty() ) {
                     JsonObjectBuilder attrObj = Json.createObjectBuilder();
-                    req.getAttributes().forEach((key, value) -> marshalAttribute(key, value, attrObj::add));
+                    req.getAttributes().forEach((key, value) -> ManifestUtils.marshalAttribute(key, value, attrObj::add));
                     requirementObj.add(JSONConstants.REQCAP_ATTRIBUTES, attrObj.build());
                 }
                 if ( !req.getDirectives().isEmpty() ) {
                     JsonObjectBuilder reqObj = Json.createObjectBuilder();
-                    req.getDirectives().forEach((key, value) -> marshalDirective(key, value, reqObj::add));
+                    req.getDirectives().forEach((key, value) -> ManifestUtils.marshalDirective(key, value, reqObj::add));
                     requirementObj.add(JSONConstants.REQCAP_DIRECTIVES, reqObj.build());
                 }
                 requirements.add(requirementObj.build());
@@ -169,12 +166,12 @@ public class FeatureJSONWriter extends JSONWriterBase {
                 capabilityObj.add(JSONConstants.REQCAP_NAMESPACE, cap.getNamespace());
                 if ( !cap.getAttributes().isEmpty() ) {
                     JsonObjectBuilder attrObj = Json.createObjectBuilder();
-                    cap.getAttributes().forEach((key, value) -> marshalAttribute(key, value, attrObj::add));
+                    cap.getAttributes().forEach((key, value) -> ManifestUtils.marshalAttribute(key, value, attrObj::add));
                     capabilityObj.add(JSONConstants.REQCAP_ATTRIBUTES, attrObj.build());
                 }
                 if ( !cap.getDirectives().isEmpty() ) {
                     JsonObjectBuilder reqObj = Json.createObjectBuilder();
-                    cap.getDirectives().forEach((key, value) -> marshalDirective(key, value, reqObj::add));
+                    cap.getDirectives().forEach((key, value) -> ManifestUtils.marshalDirective(key, value, reqObj::add));
                     capabilityObj.add(JSONConstants.REQCAP_DIRECTIVES, reqObj.build());
                 }
                 capabilities.add(capabilityObj.build());
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/JSONConstants.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/JSONConstants.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/JSONConstants.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/JSONConstants.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/JSONReaderBase.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/JSONReaderBase.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/JSONReaderBase.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/JSONReaderBase.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/JSONWriterBase.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/JSONWriterBase.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/JSONWriterBase.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/JSONWriterBase.java
diff --git a/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/ManifestUtils.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/ManifestUtils.java
new file mode 100644
index 0000000..ee6585f
--- /dev/null
+++ b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/ManifestUtils.java
@@ -0,0 +1,515 @@
+/*
+ * 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.io.json;
+
+import org.apache.felix.utils.resource.CapabilityImpl;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Version;
+import org.osgi.resource.Capability;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+// This class can be picked up from Felix Utils once it has been moved there. At that point
+// this class can be removed.
+class ManifestUtils {
+    public static void unmarshalAttribute(String key, Object value, BiConsumer<String, Object> sink) throws IOException {
+        unmarshal(key + "=" + value, Capability::getAttributes, sink);
+    }
+
+    public static void unmarshalDirective(String key, Object value, BiConsumer<String, String> sink) throws IOException {
+        unmarshal(key + ":=" + value, Capability::getDirectives, sink);
+    }
+
+    private static <T> void unmarshal(String header, Function<Capability, Map<String, T>> lookup, BiConsumer<String, T> sink) throws IOException {
+        try {
+            convertProvideCapabilities(
+                    normalizeCapabilityClauses(parseStandardHeader("foo;" + header), "2"))
+                    .forEach(capability -> lookup.apply(capability).forEach(sink));
+        } catch (Exception e) {
+            throw new IOException(e);
+        }
+    }
+
+    public static void marshalAttribute(String key, Object value, BiConsumer<String, String> sink) {
+        marshal(key, value, sink);
+    }
+
+    public static void marshalDirective(String key, Object value, BiConsumer<String, String> sink) {
+        marshal(key, value, sink);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private static void marshal(String key, Object value, BiConsumer<String, String> sink) {
+        StringBuilder keyBuilder = new StringBuilder(key);
+        if (value instanceof  List) {
+            List list = (List) value;
+            keyBuilder.append(":List");
+            if (!list.isEmpty()) {
+                String type = type(list.get(0));
+                if (!type.equals("String")) {
+                    keyBuilder.append('<').append(type).append('>');
+                }
+                value = list.stream().map(
+                        v -> v.toString().replace(",", "\\,")
+                ).collect(Collectors.joining(","));
+            }
+            else {
+                value = "";
+            }
+        }
+        else {
+            String type = type(value);
+            if (!type.equals("String")) {
+                keyBuilder.append(':').append(type);
+            }
+        }
+        sink.accept(keyBuilder.toString(), value.toString());
+    }
+
+    private static String type(Object value) {
+        if (value instanceof Long) {
+            return "Long";
+        }
+        else if (value instanceof Double)
+        {
+            return "Double";
+        }
+        else if (value instanceof Version)
+        {
+            return "Version";
+        }
+        else
+        {
+            return "String";
+        }
+    }
+
+    public static List<Capability> convertProvideCapabilities(
+            List<ParsedHeaderClause> clauses)
+            throws BundleException
+    {
+        List<Capability> capList = new ArrayList<>();
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (String path : clause.m_paths)
+            {
+                if (path.startsWith("osgi.wiring."))
+                {
+                    throw new BundleException("Manifest cannot use Provide-Capability for '"
+                            + path
+                            + "' namespace.");
+                }
+
+                Capability capability = new CapabilityImpl(null, path, clause.m_dirs, clause.m_attrs);
+                // Create package capability and add to capability list.
+                capList.add(capability);
+            }
+        }
+
+        return capList;
+    }
+
+    public static List<ParsedHeaderClause> normalizeCapabilityClauses(
+            List<ParsedHeaderClause> clauses, String mv)
+            throws BundleException
+    {
+
+        if (!mv.equals("2") && !clauses.isEmpty())
+        {
+            // Should we error here if we are not an R4 bundle?
+        }
+
+        // Convert attributes into specified types.
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (Entry<String, String> entry : clause.m_types.entrySet())
+            {
+                String type = entry.getValue();
+                if (!type.equals("String"))
+                {
+                    if (type.equals("Double"))
+                    {
+                        clause.m_attrs.put(
+                                entry.getKey(),
+                                new Double(clause.m_attrs.get(entry.getKey()).toString().trim()));
+                    }
+                    else if (type.equals("Version"))
+                    {
+                        clause.m_attrs.put(
+                                entry.getKey(),
+                                new Version(clause.m_attrs.get(entry.getKey()).toString().trim()));
+                    }
+                    else if (type.equals("Long"))
+                    {
+                        clause.m_attrs.put(
+                                entry.getKey(),
+                                new Long(clause.m_attrs.get(entry.getKey()).toString().trim()));
+                    }
+                    else if (type.startsWith("List"))
+                    {
+                        int startIdx = type.indexOf('<');
+                        int endIdx = type.indexOf('>');
+                        if (((startIdx > 0) && (endIdx <= startIdx))
+                                || ((startIdx < 0) && (endIdx > 0)))
+                        {
+                            throw new BundleException(
+                                    "Invalid Provide-Capability attribute list type for '"
+                                            + entry.getKey()
+                                            + "' : "
+                                            + type);
+                        }
+
+                        String listType = "String";
+                        if (endIdx > startIdx)
+                        {
+                            listType = type.substring(startIdx + 1, endIdx).trim();
+                        }
+
+                        List<String> tokens = parseDelimitedString(
+                                clause.m_attrs.get(entry.getKey()).toString(), ",", false);
+                        List<Object> values = new ArrayList<>(tokens.size());
+                        for (String token : tokens)
+                        {
+                            if (listType.equals("String"))
+                            {
+                                values.add(token);
+                            }
+                            else if (listType.equals("Double"))
+                            {
+                                values.add(new Double(token.trim()));
+                            }
+                            else if (listType.equals("Version"))
+                            {
+                                values.add(new Version(token.trim()));
+                            }
+                            else if (listType.equals("Long"))
+                            {
+                                values.add(new Long(token.trim()));
+                            }
+                            else
+                            {
+                                throw new BundleException(
+                                        "Unknown Provide-Capability attribute list type for '"
+                                                + entry.getKey()
+                                                + "' : "
+                                                + type);
+                            }
+                        }
+                        clause.m_attrs.put(
+                                entry.getKey(),
+                                values);
+                    }
+                    else
+                    {
+                        throw new BundleException(
+                                "Unknown Provide-Capability attribute type for '"
+                                        + entry.getKey()
+                                        + "' : "
+                                        + type);
+                    }
+                }
+            }
+        }
+
+        return clauses;
+    }
+
+    private static final char EOF = (char) -1;
+
+    private static char charAt(int pos, String headers, int length)
+    {
+        if (pos >= length)
+        {
+            return EOF;
+        }
+        return headers.charAt(pos);
+    }
+
+    private static final int CLAUSE_START = 0;
+    private static final int PARAMETER_START = 1;
+    private static final int KEY = 2;
+    private static final int DIRECTIVE_OR_TYPEDATTRIBUTE = 4;
+    private static final int ARGUMENT = 8;
+    private static final int VALUE = 16;
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static List<ParsedHeaderClause> parseStandardHeader(String header)
+    {
+        List<ParsedHeaderClause> clauses = new ArrayList<>();
+        if (header == null)
+        {
+            return clauses;
+        }
+        ParsedHeaderClause clause = null;
+        String key = null;
+        Map targetMap = null;
+        int state = CLAUSE_START;
+        int currentPosition = 0;
+        int startPosition = 0;
+        int length = header.length();
+        boolean quoted = false;
+        boolean escaped = false;
+
+        char currentChar = EOF;
+        do
+        {
+            currentChar = charAt(currentPosition, header, length);
+            switch (state)
+            {
+                case CLAUSE_START:
+                    clause = new ParsedHeaderClause(
+                            new ArrayList<>(),
+                            new HashMap<>(),
+                            new HashMap<>(),
+                            new HashMap<>());
+                    clauses.add(clause);
+                    state = PARAMETER_START;
+                case PARAMETER_START:
+                    startPosition = currentPosition;
+                    state = KEY;
+                case KEY:
+                    switch (currentChar)
+                    {
+                        case ':':
+                        case '=':
+                            key = header.substring(startPosition, currentPosition).trim();
+                            startPosition = currentPosition + 1;
+                            targetMap = clause.m_attrs;
+                            state = currentChar == ':' ? DIRECTIVE_OR_TYPEDATTRIBUTE : ARGUMENT;
+                            break;
+                        case EOF:
+                        case ',':
+                        case ';':
+                            clause.m_paths.add(header.substring(startPosition, currentPosition).trim());
+                            state = currentChar == ',' ? CLAUSE_START : PARAMETER_START;
+                            break;
+                        default:
+                            break;
+                    }
+                    currentPosition++;
+                    break;
+                case DIRECTIVE_OR_TYPEDATTRIBUTE:
+                    switch(currentChar)
+                    {
+                        case '=':
+                            if (startPosition != currentPosition)
+                            {
+                                clause.m_types.put(key, header.substring(startPosition, currentPosition).trim());
+                            }
+                            else
+                            {
+                                targetMap = clause.m_dirs;
+                            }
+                            state = ARGUMENT;
+                            startPosition = currentPosition + 1;
+                            break;
+                        default:
+                            break;
+                    }
+                    currentPosition++;
+                    break;
+                case ARGUMENT:
+                    if (currentChar == '\"')
+                    {
+                        quoted = true;
+                        currentPosition++;
+                    }
+                    else
+                    {
+                        quoted = false;
+                    }
+                    if (!Character.isWhitespace(currentChar)) {
+                        state = VALUE;
+                    }
+                    else {
+                        currentPosition++;
+                    }
+                    break;
+                case VALUE:
+                    if (escaped)
+                    {
+                        escaped = false;
+                    }
+                    else
+                    {
+                        if (currentChar == '\\' )
+                        {
+                            escaped = true;
+                        }
+                        else if (quoted && currentChar == '\"')
+                        {
+                            quoted = false;
+                        }
+                        else if (!quoted)
+                        {
+                            String value = null;
+                            switch(currentChar)
+                            {
+                                case EOF:
+                                case ';':
+                                case ',':
+                                    value = header.substring(startPosition, currentPosition).trim();
+                                    if (value.startsWith("\"") && value.endsWith("\""))
+                                    {
+                                        value = value.substring(1, value.length() - 1);
+                                    }
+                                    if (targetMap.put(key, value) != null)
+                                    {
+                                        throw new IllegalArgumentException(
+                                                "Duplicate '" + key + "' in: " + header);
+                                    }
+                                    state = currentChar == ';' ? PARAMETER_START : CLAUSE_START;
+                                    break;
+                                default:
+                                    break;
+                            }
+                        }
+                    }
+                    currentPosition++;
+                    break;
+                default:
+                    break;
+            }
+        } while ( currentChar != EOF);
+
+        if (state > PARAMETER_START)
+        {
+            throw new IllegalArgumentException("Unable to parse header: " + header);
+        }
+        return clauses;
+    }
+
+    /**
+     * Parses delimited string and returns an array containing the tokens. This
+     * parser obeys quotes, so the delimiter character will be ignored if it is
+     * inside of a quote. This method assumes that the quote character is not
+     * included in the set of delimiter characters.
+     * @param value the delimited string to parse.
+     * @param delim the characters delimiting the tokens.
+     * @return a list of string or an empty list if there are none.
+     **/
+    public static List<String> parseDelimitedString(String value, String delim, boolean trim)
+    {
+        if (value == null)
+        {
+            value = "";
+        }
+
+        List<String> list = new ArrayList<>();
+
+        int CHAR = 1;
+        int DELIMITER = 2;
+        int STARTQUOTE = 4;
+        int ENDQUOTE = 8;
+
+        StringBuffer sb = new StringBuffer();
+
+        int expecting = (CHAR | DELIMITER | STARTQUOTE);
+
+        boolean isEscaped = false;
+        for (int i = 0; i < value.length(); i++)
+        {
+            char c = value.charAt(i);
+
+            boolean isDelimiter = (delim.indexOf(c) >= 0);
+
+            if (!isEscaped && (c == '\\'))
+            {
+                isEscaped = true;
+                continue;
+            }
+
+            if (isEscaped)
+            {
+                sb.append(c);
+            }
+            else if (isDelimiter && ((expecting & DELIMITER) > 0))
+            {
+                if (trim)
+                {
+                    list.add(sb.toString().trim());
+                }
+                else
+                {
+                    list.add(sb.toString());
+                }
+                sb.delete(0, sb.length());
+                expecting = (CHAR | DELIMITER | STARTQUOTE);
+            }
+            else if ((c == '"') && ((expecting & STARTQUOTE) > 0))
+            {
+                sb.append(c);
+                expecting = CHAR | ENDQUOTE;
+            }
+            else if ((c == '"') && ((expecting & ENDQUOTE) > 0))
+            {
+                sb.append(c);
+                expecting = (CHAR | STARTQUOTE | DELIMITER);
+            }
+            else if ((expecting & CHAR) > 0)
+            {
+                sb.append(c);
+            }
+            else
+            {
+                throw new IllegalArgumentException("Invalid delimited string: " + value);
+            }
+
+            isEscaped = false;
+        }
+
+        if (sb.length() > 0)
+        {
+            if (trim)
+            {
+                list.add(sb.toString().trim());
+            }
+            else
+            {
+                list.add(sb.toString());
+            }
+        }
+
+        return list;
+    }
+
+    static class ParsedHeaderClause
+    {
+        public final List<String> m_paths;
+        public final Map<String, String> m_dirs;
+        public final Map<String, Object> m_attrs;
+        public final Map<String, String> m_types;
+
+        public ParsedHeaderClause(
+                List<String> paths, Map<String, String> dirs, Map<String, Object> attrs,
+                Map<String, String> types)
+        {
+            m_paths = paths;
+            m_dirs = dirs;
+            m_attrs = attrs;
+            m_types = types;
+        }
+    }
+}
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/package-info.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/package-info.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/json/package-info.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/json/package-info.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/package-info.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/package-info.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/package-info.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/package-info.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/spi/ArtifactProvider.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/spi/ArtifactProvider.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/spi/ArtifactProvider.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/spi/ArtifactProvider.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/spi/ArtifactProviderContext.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/spi/ArtifactProviderContext.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/spi/ArtifactProviderContext.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/spi/ArtifactProviderContext.java
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/spi/package-info.java b/featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/spi/package-info.java
similarity index 100%
rename from featuremodel/feature-support/src/main/java/org/apache/sling/feature/io/spi/package-info.java
rename to featuremodel/feature-io/src/main/java/org/apache/sling/feature/io/spi/package-info.java
diff --git a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/io/ArtifactManagerTest.java b/featuremodel/feature-io/src/test/java/org/apache/sling/feature/io/ArtifactManagerTest.java
similarity index 100%
rename from featuremodel/feature-support/src/test/java/org/apache/sling/feature/io/ArtifactManagerTest.java
rename to featuremodel/feature-io/src/test/java/org/apache/sling/feature/io/ArtifactManagerTest.java
diff --git a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/io/FeatureUtilTest.java b/featuremodel/feature-io/src/test/java/org/apache/sling/feature/io/FeatureUtilTest.java
similarity index 100%
rename from featuremodel/feature-support/src/test/java/org/apache/sling/feature/io/FeatureUtilTest.java
rename to featuremodel/feature-io/src/test/java/org/apache/sling/feature/io/FeatureUtilTest.java
diff --git a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/io/json/FeatureJSONReaderTest.java b/featuremodel/feature-io/src/test/java/org/apache/sling/feature/io/json/FeatureJSONReaderTest.java
similarity index 100%
rename from featuremodel/feature-support/src/test/java/org/apache/sling/feature/io/json/FeatureJSONReaderTest.java
rename to featuremodel/feature-io/src/test/java/org/apache/sling/feature/io/json/FeatureJSONReaderTest.java
diff --git a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/io/json/FeatureJSONWriterTest.java b/featuremodel/feature-io/src/test/java/org/apache/sling/feature/io/json/FeatureJSONWriterTest.java
similarity index 100%
rename from featuremodel/feature-support/src/test/java/org/apache/sling/feature/io/json/FeatureJSONWriterTest.java
rename to featuremodel/feature-io/src/test/java/org/apache/sling/feature/io/json/FeatureJSONWriterTest.java
diff --git a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/io/json/U.java b/featuremodel/feature-io/src/test/java/org/apache/sling/feature/io/json/U.java
similarity index 100%
copy from featuremodel/feature-support/src/test/java/org/apache/sling/feature/io/json/U.java
copy to featuremodel/feature-io/src/test/java/org/apache/sling/feature/io/json/U.java
diff --git a/featuremodel/feature-support/src/test/resources/features/repoinit.json b/featuremodel/feature-io/src/test/resources/features/repoinit.json
similarity index 100%
rename from featuremodel/feature-support/src/test/resources/features/repoinit.json
rename to featuremodel/feature-io/src/test/resources/features/repoinit.json
diff --git a/featuremodel/feature-support/src/test/resources/features/repoinit2.json b/featuremodel/feature-io/src/test/resources/features/repoinit2.json
similarity index 100%
rename from featuremodel/feature-support/src/test/resources/features/repoinit2.json
rename to featuremodel/feature-io/src/test/resources/features/repoinit2.json
diff --git a/featuremodel/feature-support/src/test/resources/features/test2.json b/featuremodel/feature-io/src/test/resources/features/test.json
similarity index 63%
copy from featuremodel/feature-support/src/test/resources/features/test2.json
copy to featuremodel/feature-io/src/test/resources/features/test.json
index 0e5c1c6..8ae346c 100644
--- a/featuremodel/feature-support/src/test/resources/features/test2.json
+++ b/featuremodel/feature-io/src/test/resources/features/test.json
@@ -1,22 +1,10 @@
 {
-    "model-version": "1",
-    "id" : "org.apache.sling/test2/1.1",
-
-    "variables": {
-         "common.version": "1.2.3",
-         "contract.name": "JavaServlet",
-         "ab_config": "right!",
-         "c_config": "really?",
-         "includever": "9",
-         "ns": "contract",
-         "sling.gid": "org.apache.sling",
-         "something": "something",
-         "svc": "service"
-    },
+    "id" : "org.apache.sling/test-feature/1.1",
+    "description": "The feature description",
 
     "includes" : [
          {
-             "id" : "${sling.gid}/sling/${includever}",
+             "id" : "org.apache.sling/sling/9",
              "removals" : {
                  "configurations" : [
                  ],
@@ -29,9 +17,9 @@
     ],
     "requirements" : [
           {
-              "namespace" : "osgi.${ns}",
+              "namespace" : "osgi.contract",
               "directives" : {
-                  "filter" : "(&(osgi.contract=${contract.name})(&(version>=3.0)(!(version>=4.0))))"
+                  "filter" : "(&(osgi.contract=JavaServlet)(&(version>=3.0)(!(version>=4.0))))"
               }
           }
     ],
@@ -47,12 +35,12 @@
              }
         },
         {
-             "namespace" : "osgi.${svc}",
+             "namespace" : "osgi.service",
              "attributes" : {
-                  "objectClass:List<String>" : "org.osgi.${svc}.http.runtime.HttpServiceRuntime"
+                  "objectClass:List<String>" : "org.osgi.service.http.runtime.HttpServiceRuntime"
              },
              "directives" : {
-                  "uses" : "org.osgi.${svc}.http.runtime,org.osgi.${svc}.http.runtime.dto"
+                  "uses" : "org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto"
              }
         },
         {
@@ -69,7 +57,7 @@
     ],
     "framework-properties" : {
         "foo" : 1,
-        "brave" : "${something}",
+        "brave" : "something",
         "org.apache.felix.scr.directory" : "launchpad/scr"
     },
     "bundles" :[
@@ -87,10 +75,9 @@
               "start-order" : 1
             },
             {
-              "id" : "org.apache.sling/foo-xyz/${common.version}",
+              "id" : "org.apache.sling/foo-xyz/1.2.3",
               "start-order" : 2
-            },
-            "org.apache.sling/bar-xyz/${common.version}"
+            }
     ],
     "configurations" : {
         "my.pid" : {
@@ -98,10 +85,8 @@
            "bar" : "test",
            "number:Integer" : 7
         },
-        "my.pid2" : {
-           "a.value" : "aa${ab_config}",
-           "b.value" : "${ab_config}bb",
-           "c.value" : "c${c_config}c"
+        "my.factory.pid~name" : {
+           "a.value" : "yeah"
         }
     }
 }
\ No newline at end of file
diff --git a/featuremodel/feature-support/src/test/resources/features/test2.json b/featuremodel/feature-io/src/test/resources/features/test2.json
similarity index 100%
rename from featuremodel/feature-support/src/test/resources/features/test2.json
rename to featuremodel/feature-io/src/test/resources/features/test2.json
diff --git a/featuremodel/feature-support/src/test/resources/features/test3.json b/featuremodel/feature-io/src/test/resources/features/test3.json
similarity index 100%
rename from featuremodel/feature-support/src/test/resources/features/test3.json
rename to featuremodel/feature-io/src/test/resources/features/test3.json
diff --git a/featuremodel/feature-karaf/pom.xml b/featuremodel/feature-karaf/pom.xml
index 450870a..315afec 100644
--- a/featuremodel/feature-karaf/pom.xml
+++ b/featuremodel/feature-karaf/pom.xml
@@ -70,6 +70,12 @@
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.io</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature.support</artifactId>
             <version>0.0.1-SNAPSHOT</version>
             <scope>provided</scope>
diff --git a/featuremodel/feature-launcher/pom.xml b/featuremodel/feature-launcher/pom.xml
index fb1574b..a71522d 100644
--- a/featuremodel/feature-launcher/pom.xml
+++ b/featuremodel/feature-launcher/pom.xml
@@ -102,6 +102,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.io</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature.resolver</artifactId>
             <version>0.0.1-SNAPSHOT</version>
         </dependency>
diff --git a/featuremodel/feature-modelconverter/pom.xml b/featuremodel/feature-modelconverter/pom.xml
index c77652f..8386c38 100644
--- a/featuremodel/feature-modelconverter/pom.xml
+++ b/featuremodel/feature-modelconverter/pom.xml
@@ -131,6 +131,12 @@
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.io</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature.resolver</artifactId>
             <version>0.0.1-SNAPSHOT</version>
             <scope>provided</scope>
diff --git a/featuremodel/feature-resolver/pom.xml b/featuremodel/feature-resolver/pom.xml
index c18928a..c52badb 100644
--- a/featuremodel/feature-resolver/pom.xml
+++ b/featuremodel/feature-resolver/pom.xml
@@ -74,6 +74,12 @@
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.io</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature.support</artifactId>
             <version>0.0.1-SNAPSHOT</version>
             <scope>provided</scope>
diff --git a/featuremodel/feature-support/pom.xml b/featuremodel/feature-support/pom.xml
index 61fa224..97b9729 100644
--- a/featuremodel/feature-support/pom.xml
+++ b/featuremodel/feature-support/pom.xml
@@ -81,6 +81,12 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.io</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.converter</artifactId>
             <version>0.1.0-SNAPSHOT</version>
diff --git a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/util/CapabilityMatcherTest.java b/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/util/CapabilityMatcherTest.java
index 048e6e2..af69b4a 100644
--- a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/util/CapabilityMatcherTest.java
+++ b/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/util/CapabilityMatcherTest.java
@@ -16,13 +16,12 @@
  */
 package org.apache.sling.feature.support.util;
 
-import static junit.framework.TestCase.assertTrue;
-
 import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.io.json.U;
 import org.junit.Test;
 import org.osgi.resource.Requirement;
 
+import static junit.framework.TestCase.assertTrue;
+
 public class CapabilityMatcherTest {
     @Test public void testCapabilityMatching() throws Exception {
         Feature feature = U.readFeature("test");
diff --git a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/io/json/U.java b/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/util/U.java
similarity index 98%
rename from featuremodel/feature-support/src/test/java/org/apache/sling/feature/io/json/U.java
rename to featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/util/U.java
index 904f158..aff22c8 100644
--- a/featuremodel/feature-support/src/test/java/org/apache/sling/feature/io/json/U.java
+++ b/featuremodel/feature-support/src/test/java/org/apache/sling/feature/support/util/U.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sling.feature.io.json;
+package org.apache.sling.feature.support.util;
 
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Feature;
diff --git a/featuremodel/osgifeature-maven-plugin/pom.xml b/featuremodel/osgifeature-maven-plugin/pom.xml
index 3235f5d..ec05534 100644
--- a/featuremodel/osgifeature-maven-plugin/pom.xml
+++ b/featuremodel/osgifeature-maven-plugin/pom.xml
@@ -115,6 +115,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.io</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature.support</artifactId>
             <version>0.0.1-SNAPSHOT</version>
         </dependency>
diff --git a/featuremodel/pom.xml b/featuremodel/pom.xml
index c15c4e7..1864df6 100644
--- a/featuremodel/pom.xml
+++ b/featuremodel/pom.xml
@@ -59,6 +59,7 @@
         <module>feature</module> 
         <module>feature-analyser</module>
         <module>feature-applicationbuilder</module>
+        <module>feature-io</module>
         <module>feature-karaf</module>
         <module>feature-launcher</module>
         <module>feature-modelconverter</module>

-- 
To stop receiving notification emails like this one, please contact
davidb@apache.org.