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 2020/04/24 12:24:29 UTC

[sling-org-apache-sling-feature-io] branch master updated: Deprecate module, all contents has been merged into org.apache.sling.feature

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 2d9fdc8  Deprecate module, all contents has been merged into org.apache.sling.feature
2d9fdc8 is described below

commit 2d9fdc8818ba91ad358b3636fc3933707d7c6718
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Fri Apr 24 14:24:14 2020 +0200

    Deprecate module, all contents has been merged into org.apache.sling.feature
---
 .asf.yaml                                          |   6 +
 Jenkinsfile                                        |  20 -
 bnd.bnd                                            |   3 -
 design/feature-model.json                          | 123 ----
 pom.xml                                            | 169 -----
 readme.md                                          |   9 +-
 .../apache/sling/feature/io/CloseShieldWriter.java |  43 --
 .../apache/sling/feature/io/ConfiguratorUtil.java  |  80 ---
 .../java/org/apache/sling/feature/io/IOUtils.java  | 300 ---------
 .../sling/feature/io/archive/ArchiveReader.java    | 180 ------
 .../sling/feature/io/archive/ArchiveWriter.java    | 191 ------
 .../sling/feature/io/archive/package-info.java     |  23 -
 .../feature/io/artifacts/ArtifactHandler.java      |  71 --
 .../feature/io/artifacts/ArtifactManager.java      | 527 ---------------
 .../io/artifacts/ArtifactManagerConfig.java        | 183 ------
 .../sling/feature/io/artifacts/package-info.java   |  23 -
 .../feature/io/artifacts/spi/ArtifactProvider.java |  58 --
 .../io/artifacts/spi/ArtifactProviderContext.java  |  49 --
 .../feature/io/artifacts/spi/package-info.java     |  23 -
 .../feature/io/json/ConfigurationJSONReader.java   |  73 ---
 .../feature/io/json/ConfigurationJSONWriter.java   |  69 --
 .../sling/feature/io/json/FeatureJSONReader.java   | 716 ---------------------
 .../sling/feature/io/json/FeatureJSONWriter.java   | 415 ------------
 .../sling/feature/io/json/JSONConstants.java       |  85 ---
 .../sling/feature/io/json/ManifestUtils.java       | 518 ---------------
 .../apache/sling/feature/io/json/package-info.java |  23 -
 .../org/apache/sling/feature/io/package-info.java  |  23 -
 .../META-INF/feature/Feature-1.0.0.schema.json     | 226 -------
 .../sling/feature/io/ConfiguratorUtilTest.java     | 126 ----
 .../org/apache/sling/feature/io/IOUtilsTest.java   | 170 -----
 .../feature/io/archive/ArchiveWriterTest.java      | 120 ----
 .../feature/io/artifacts/ArtifactManagerTest.java  |  96 ---
 .../sling/feature/io/json/ArtifactsExtensions.java |  57 --
 .../feature/io/json/FeatureJSONReaderTest.java     | 116 ----
 .../feature/io/json/FeatureJSONWriterTest.java     | 135 ----
 .../java/org/apache/sling/feature/io/json/U.java   |  73 ---
 .../resources/features/artifacts-extension.json    |   9 -
 src/test/resources/features/complete.json          |   4 -
 src/test/resources/features/feature-model.json     | 123 ----
 src/test/resources/features/final.json             |   5 -
 src/test/resources/features/repoinit.json          |   4 -
 src/test/resources/features/repoinit2.json         |   7 -
 src/test/resources/features/test.json              |  92 ---
 src/test/resources/features/test2.json             |   7 -
 src/test/resources/features/test3.json             |   7 -
 45 files changed, 11 insertions(+), 5369 deletions(-)

diff --git a/.asf.yaml b/.asf.yaml
new file mode 100644
index 0000000..0060433
--- /dev/null
+++ b/.asf.yaml
@@ -0,0 +1,6 @@
+github:
+  description: "Apache Sling Feature Model IO"
+  homepage: "https://sling.apache.org/"
+  labels:
+    - sling
+    - deprecated
diff --git a/Jenkinsfile b/Jenkinsfile
deleted file mode 100644
index f582519..0000000
--- a/Jenkinsfile
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * 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.
- */
-
-slingOsgiBundleBuild()
diff --git a/bnd.bnd b/bnd.bnd
deleted file mode 100644
index 62f82d8..0000000
--- a/bnd.bnd
+++ /dev/null
@@ -1,3 +0,0 @@
--conditionalpackage: org.apache.felix.utils.collections,\\
-                     org.apache.felix.utils.resource,\\
-                     org.apache.felix.utils.version
diff --git a/design/feature-model.json b/design/feature-model.json
deleted file mode 100644
index aff0e66..0000000
--- a/design/feature-model.json
+++ /dev/null
@@ -1,123 +0,0 @@
-{
-    // this is a comment
-    "id": "org.apache.sling:my.app:slingosgifeature:my-classifier:1.0",
-
-    "title": "A title for the feature. (optional)",
-    "description": "A description for the feature. (optional)",
-    "vendor": "The feature vendor, for example 'Apache Software Foundation'. (optional)",
-    "license": "The license of this feature file, for example 'ASL-2'. (optional)",
-
-    // A complete feature has no external dependencies
-    "complete": true,
-
-    // A final feature cannot be used as a prototype for another feature
-    "final": false,
-
-    // variables used in configuration and framework properties are substituted at launch time.
-    "variables": {
-        "cfgvar": "somedefault",
-        "org.abc.xyz": "1.2.3",
-
-        // When converting to provisioning model, if you need a special name
-        "provisioning.model.name": ":boot"
-    },
-
-    // A prototype is another feature that is used as a prototype for this one
-    // Bundles, configurations and framework properties can be removed from the
-    //prototype. Bundles with the same artifact ID defined in the feature override
-    // bundles with this artifact ID in the Prototype
-    "prototype": 
-        {
-            "id": "org.apache.sling:some-other-feature:1.2.3",
-            "removals": {
-                "configurations": [],
-                "bundles": [],
-                "framework-properties": []
-            }
-        },
-
-    // Requirements over and above the requirements in the bundles referenced by
-    // feature.
-    "requirements": [
-        {
-            "namespace": "osgi.contract",
-            "directives": {
-                "filter": "(&(osgi.contract=JavaServlet)(version=3.1))"
-            }
-        }
-    ],
-
-    // Capabilities over and above the capabilities provided by the bundles referenced
-    // by the feature.
-    "capabilities": [
-        {
-            "namespace": "osgi.implementation",
-            "attributes": {
-                "osgi.implementation": "osgi.http",
-                "version:Version": "1.1"
-            },
-            "directives": {
-                "uses": "javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard"
-            }
-        },
-        {
-            "namespace": "osgi.service",
-            "attributes": {
-                "objectClass:List<String>": "org.osgi.service.http.runtime.HttpServiceRuntime"
-            },
-            "directives": {
-                "uses": "org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto"
-            }
-        }
-    ],
-
-    // Framework properties to be provided to the running OSGi Framework
-    "framework-properties": {
-        "foo": 1,
-        "org.osgi.framework.storage": "${tempdir}",
-        "org.apache.felix.scr.directory": "launchpad/scr"
-    },
-
-    // The bundles that are part of the feature. Bundles are referenced using Maven
-    // coordinates and can have additional metadata associated with them. Bundles can
-    // specified as either a simple string (the Maven coordinates of the bundle) or
-    // as an object with 'id' and additional metadata.
-    "bundles": [
-        {
-            "id": "org.apache.sling:security-server:2.2.0",
-            "hash": "4632463464363646436",
-
-            // This is the relative start order inside the feature
-            "start-order": 5
-        },
-        {
-            "id": "org.apache.sling:application-bundle:2.0.0",
-            "start-order": 10
-        },
-        "org.apache.sling:foo-xyz:1.2.3"
-    ],
-
-    // The configurations are specified following the format defined by the OSGi Configurator
-    // specification: https://osgi.org/specification/osgi.cmpn/7.0.0/service.configurator.html
-    // Variables declared in the variables section can be used for late binding of variables
-    // they can be specified with the Launcher, or the default from the variables section is used.
-    // Factory configurations can be specified using the named factory syntax, which separates
-    // The factory PID and the name with a tilde '~'
-    "configurations": {
-        "my.pid": {
-            "foo": 5,
-            "something-enabled": false,
-            "bar": "${cfgvar}",
-
-            // The tempdir variable is not specified at the variables section.
-            // It needs to be provided at launch, otherwise the launch will stop.
-            "tempdir": "${tempdir}",
-
-
-            "number:Integer": 7
-        },
-        "my.factory.pid~name": {
-           "a.value":"yeah"
-        }
-    }
-}
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index 871ad0f..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,169 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<!--
-    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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <parent>
-        <groupId>org.apache.sling</groupId>
-        <artifactId>sling-bundle-parent</artifactId>
-        <version>38</version>
-        <relativePath />
-    </parent>
-
-    <artifactId>org.apache.sling.feature.io</artifactId>
-    <version>1.3.1-SNAPSHOT</version>
-
-    <name>Apache Sling Feature IO Module</name>
-    <description>
-        IO functionality for the Feature Model
-    </description>
-
-    <properties>
-        <sling.java.version>8</sling.java.version>
-    </properties>
-
-    <scm>
-        <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-io.git</connection>
-        <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-io.git</developerConnection>
-        <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-feature-io.git</url>
-        <tag>HEAD</tag>
-    </scm>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>biz.aQute.bnd</groupId>
-                <artifactId>bnd-baseline-maven-plugin</artifactId>
-                <configuration>
-                    <skip>true</skip>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.rat</groupId>
-                <artifactId>apache-rat-plugin</artifactId>
-                <configuration>
-                    <excludes>
-                        <exclude>readme.md</exclude>
-                    </excludes>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-shade-plugin</artifactId>
-                <version>3.2.1</version>
-                <executions>
-                    <execution>
-                        <phase>package</phase>
-                        <goals>
-                            <goal>shade</goal>
-                        </goals>
-                        <configuration>
-                            <createSourcesJar>true</createSourcesJar>
-                            <shadeSourcesContent>true</shadeSourcesContent>
-                            <relocations>
-                                <relocation>
-                                    <pattern>org.apache.felix.utils</pattern>
-                                    <shadedPattern>org.apache.sling.feature.io.impl.felix</shadedPattern>
-                                </relocation>
-                            </relocations>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.felix</groupId>
-            <artifactId>org.apache.felix.converter</artifactId>
-            <version>1.0.12</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.felix</groupId>
-            <artifactId>org.apache.felix.cm.json</artifactId>
-            <version>1.0.2</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.felix</groupId>
-            <artifactId>org.apache.felix.utils</artifactId>
-            <version>1.11.4</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.geronimo.specs</groupId>
-            <artifactId>geronimo-json_1.1_spec</artifactId>
-            <version>1.2</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.feature</artifactId>
-            <version>1.1.8</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.annotation.versioning</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.framework</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.resource</artifactId>
-            <version>1.0.0</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-        </dependency>
-
-        <!-- Testing -->
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <version>3.3.3</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.johnzon</groupId>
-            <artifactId>johnzon-core</artifactId>
-            <version>1.2.3</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.util.function</artifactId>
-            <version>1.0.0</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-lang3</artifactId>
-            <version>3.9</version>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-</project>
diff --git a/readme.md b/readme.md
index c68e599..fa623ab 100644
--- a/readme.md
+++ b/readme.md
@@ -1,9 +1,10 @@
 [<img src="http://sling.apache.org/res/logos/sling.png"/>](http://sling.apache.org)
 
- [![Build Status](https://builds.apache.org/buildStatus/icon?job=sling-org-apache-sling-feature-io-1.8)](https://builds.apache.org/view/S-Z/view/Sling/job/sling-org-apache-sling-feature-io-1.8) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.sling/org.apache.sling.feature.io/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.apache.sling%22%20a%3A%22org.apache.sling.feature.io%22) [![JavaDocs](https://www.javadoc.io/badge/org.apache.sling/org. [...]
 
-# IO Functionality for the Sling Feature Model
+# Sling Feature Model IO (deprecated)
 
-This project contains functionality to deal with the IO needs of the feature model. This includes interfacing with Maven Repositories and reading and writing Feature Model JSON files.
+This module is no longer maintained as a separate module, all its contents has been merged into
+https://github.com/apache/sling-org-apache-sling-feature/blob/master/readme.md
 
-For further documentation see: https://github.com/apache/sling-org-apache-sling-feature/blob/master/readme.md
+
+For reference use branch [maintenance](https://github.com/apache/sling-org-apache-sling-feature-io/tree/maintenance).
diff --git a/src/main/java/org/apache/sling/feature/io/CloseShieldWriter.java b/src/main/java/org/apache/sling/feature/io/CloseShieldWriter.java
deleted file mode 100644
index f5add8b..0000000
--- a/src/main/java/org/apache/sling/feature/io/CloseShieldWriter.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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;
-
-import java.io.FilterWriter;
-import java.io.IOException;
-import java.io.Writer;
-
-/**
- * Proxy writer that prevents the underlying writer from being closed.
- * <p>
- * This class is typically used in cases where a writer needs to be passed to a component that wants to explicitly close
- * the writer even if other components would still use the writer for output.
- * </p>
- * @deprecated
- */
-@Deprecated
-public class CloseShieldWriter extends FilterWriter {
-
-    public CloseShieldWriter(Writer out) {
-        super(out);
-    }
-
-    @Override
-    public void close() throws IOException {
-        // NOOP
-    }
-
-}
diff --git a/src/main/java/org/apache/sling/feature/io/ConfiguratorUtil.java b/src/main/java/org/apache/sling/feature/io/ConfiguratorUtil.java
deleted file mode 100644
index 2428db3..0000000
--- a/src/main/java/org/apache/sling/feature/io/ConfiguratorUtil.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Collections;
-import java.util.Dictionary;
-
-import javax.json.Json;
-import javax.json.stream.JsonGenerator;
-import javax.json.stream.JsonGeneratorFactory;
-
-import org.apache.felix.cm.json.Configurations;
-import org.apache.sling.feature.Configuration;
-
-/**
- * Helper class to write JSON structures as defined in
- * <a href="https://osgi.org/specification/osgi.cmpn/7.0.0/service.configurator.html#d0e131765">OSGi Configurator Specification 1.0</a>.
- * @deprecated Use {@link org.apache.felix.cm.json.Configurations} instead.
- */
-@Deprecated
-public class ConfiguratorUtil {
-
-    private ConfiguratorUtil() {
-    }
-
-    protected static final JsonGenerator newGenerator(final Writer writer) {
-        JsonGeneratorFactory generatorFactory = Json.createGeneratorFactory(Collections.singletonMap(JsonGenerator.PRETTY_PRINTING, true));
-
-        // prevent closing of the underlying writer
-        Writer closeShieldWriter = new CloseShieldWriter(writer);
-        return generatorFactory.createGenerator(closeShieldWriter);
-    }
-
-    /** Write the OSGi configuration to a JSON structure.
-     * The writer is not closed.
-     *
-     * @param writer Writer
-     * @param props The configuration properties to write */
-    public static void writeConfiguration(final Writer writer, final Dictionary<String, Object> props) {
-        final Object artifactId = props.remove(Configuration.PROP_ARTIFACT_ID);
-        try {
-            Configurations.buildWriter().build(writer).writeConfiguration(props);
-        } catch (IOException e) {
-            throw new RuntimeException("Unable to write configuration.", e);
-        } finally {
-            if ( artifactId != null ) {
-                props.put(Configuration.PROP_ARTIFACT_ID, artifactId);
-            }
-        }
-    }
-
-    public static void writeConfiguration(final JsonGenerator generator, final Dictionary<String, Object> props) {
-        final Object artifactId = props.remove(Configuration.PROP_ARTIFACT_ID);
-        try {
-            Configurations.buildWriter().build(generator).writeConfiguration(props);
-        } catch (IOException e) {
-            throw new RuntimeException("Unable to write configuration.", e);
-        } finally {
-            if ( artifactId != null ) {
-                props.put(Configuration.PROP_ARTIFACT_ID, artifactId);
-            }
-        }
-    }
-}
diff --git a/src/main/java/org/apache/sling/feature/io/IOUtils.java b/src/main/java/org/apache/sling/feature/io/IOUtils.java
deleted file mode 100644
index fa64815..0000000
--- a/src/main/java/org/apache/sling/feature/io/IOUtils.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * 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;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.JarURLConnection;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.file.Files;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.jar.JarFile;
-
-public class IOUtils {
-
-    /** The extension for a reference file. */
-    public static final String EXTENSION_REF_FILE = ".ref";
-
-    /** The extension for a feature file. */
-    public static final String EXTENSION_FEATURE_FILE = ".json";
-
-    /** The default directory to search for features. */
-    public static final String DEFAULT_DIRECTORY = "features";
-
-    /** The default name of the feature file. */
-    public static final String DEFAULT_FEATURE_FILE = "feature" + EXTENSION_FEATURE_FILE;
-
-    /**
-     * Parse a feature reference file
-     * @param file The file
-     * @return The referenced features
-     * @throws IOException If reading fails
-     */
-    public static List<String> parseFeatureRefFile(final File file)
-    throws IOException {
-        final List<String> result = new ArrayList<>();
-        final List<String> lines = Files.readAllLines(file.toPath());
-        for(String line : lines) {
-            line = line.trim();
-            if ( !line.isEmpty() && !line.startsWith("#") ) {
-                if ( line.indexOf(':') == -1 ) {
-                    result.add(new File(line).getAbsolutePath());
-                } else {
-                    result.add(line);
-                }
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Get the list of feature files.
-     * If the provided list of files is {@code null} or an empty array, the default is used.
-     * The default checks for the following places, the first one found is used. If none is
-     * found an empty list is returned.
-     * <ol>
-     *   <li>A directory named {@link #DEFAULT_DIRECTORY} in the current directory
-     *   <li>A file named {@link #DEFAULT_FEATURE_FILE} in the current directory
-     *   <li>A directory named {@link #DEFAULT_DIRECTORY} in the home directory
-     *   <li>A file named {@link #DEFAULT_FEATURE_FILE} in the home directory
-     * </ol>
-     *
-     * The list of files is processed one after the other. If it is relative, it is
-     * first tried to be resolved against the current directory and then against the
-     * home directory.
-     * If an entry denotes a directory, all children ending in {@link #EXTENSION_FEATURE_FILE} or
-     * {@link #EXTENSION_REF_FILE} of that directory are read.
-     * If a file ends in {@link #EXTENSION_REF_FILE} the contents is read and every line not
-     * starting with the hash sign is considered a reference to a feature artifact.
-     *
-     * @param homeDirectory If relative files should be resolved, this is the directory to use
-     * @param files Optional list of files. If none is provided, a default is used.
-     * @return The list of files.
-     * @throws IOException If an error occurs.
-     */
-    public static List<String> getFeatureFiles(final File homeDirectory, final String... files)
-    throws IOException {
-        String[] featureFiles = files;
-        if ( featureFiles == null || featureFiles.length == 0 ) {
-            // Default value - check feature directory otherwise features file
-            final File[] candidates = new File[] {
-                    new File(homeDirectory, DEFAULT_DIRECTORY),
-                    new File(homeDirectory, DEFAULT_FEATURE_FILE),
-                    new File(DEFAULT_DIRECTORY),
-                    new File(DEFAULT_FEATURE_FILE)
-            };
-            File f = null;
-            for(final File c : candidates) {
-                if ( c.exists() ) {
-                    f = c;
-                    break;
-                }
-            }
-            // nothing found, we default to the first candidate and fail later
-            if ( f == null ) {
-                f = candidates[0];
-            }
-
-            featureFiles = new String[] {f.getAbsolutePath()};
-        }
-
-        final List<String> paths = new ArrayList<>();
-        for(final String name : featureFiles) {
-            // check for absolute
-            if ( name.indexOf(':') > 1 ) {
-                paths.add(name);
-            } else {
-                // file or relative
-                File f = null;
-                final File test = new File(name);
-                if ( test.isAbsolute() ) {
-                    f = test;
-                } else {
-                    final File[] candidates = {
-                            new File(homeDirectory, name),
-                            new File(homeDirectory, DEFAULT_DIRECTORY + File.separatorChar + name),
-                            new File(name),
-                            new File(DEFAULT_DIRECTORY + File.separatorChar + name),
-                    };
-                    for(final File c : candidates) {
-                        if ( c.exists() && c.isFile() ) {
-                            f = c;
-                            break;
-                        }
-                    }
-                }
-
-                if ( f != null && f.exists() ) {
-                    if ( f.isFile() ) {
-                        processFile(paths, f);
-                    } else {
-                        processDir(paths, f);
-                    }
-                } else {
-                    // we simply add the path and fail later on
-                    paths.add(new File(name).getAbsolutePath());
-                }
-            }
-        }
-
-        Collections.sort(paths, FEATURE_PATH_COMP);
-        return paths;
-    }
-
-    /**
-     * Get a File from a local URL (if possible)
-     *
-     * @param url a local url (like a file: url or a jar:file: url
-     * @param cache if an attempt should be made to download the content of the url locally
-     *              if it can not be presented as a file directly
-     * @param tmpDir the tmpDir to use (null for default)
-     * @return the file the url points to (or null if none) - or a tmp file if cache is true and the url could be cached
-     * @throws IOException
-     */
-    public static File getFileFromURL(URL url, boolean cache, File tmpDir) throws IOException {
-        File result;
-        if (url.getProtocol().equals("file")) {
-            try {
-                result = new File(url.toURI());
-            } catch (URISyntaxException e) {
-                result = new File(url.getPath());
-            }
-        } else if (url.getProtocol().equals("jar")) {
-            String innerURL = url.getPath();
-            if (innerURL.endsWith("!/") && innerURL.indexOf("!/") == innerURL.lastIndexOf("!/")) {
-                innerURL = innerURL.substring(0, innerURL.indexOf("!/"));
-                try {
-                    result = getFileFromURL(new URL(innerURL), cache, tmpDir);
-                } catch (IOException ex) {
-                    result = null;
-                }
-            }
-            else {
-                result = null;
-            }
-        }
-        else {
-            result = null;
-        }
-
-        if ((result == null || !result.exists()) && cache) {
-            File tmp = File.createTempFile("jar", ".jar", tmpDir);
-            try (InputStream input = url.openStream(); OutputStream output = new FileOutputStream(tmp)) {
-                byte[] buffer =new byte[64 * 1024];
-                for (int i = input.read(buffer); i != -1;i = input.read(buffer)) {
-                    output.write(buffer, 0, i);
-                }
-            }
-            result = tmp;
-        }
-        return result;
-    }
-
-    /**
-     * Get a JarFile from a local URL (if possible)
-     *
-     * @param url a local url (like a file: url or a jar:file: url
-     * @param cache if an attempt should be made to download the content of the url locally
-     *              if it can not be presented as a jarfile directly
-     * @param tmpDir the tmpDir to use (null for default)
-     *
-     * @return the jarfile the url points to
-     * @throws IOException if the url can't be represented as a jarfile
-     */
-    public static JarFile getJarFileFromURL(URL url, boolean cache, File tmpDir) throws IOException {
-        try {
-            URL targetURL = url;
-            if (!url.getProtocol().equals("jar")) {
-                targetURL = new URL("jar:" + toURLString(url) + "!/");
-            }
-            else if (!url.getPath().endsWith("!/")) {
-                targetURL = new URL(toURLString(url) + "!/");
-            }
-            return ((JarURLConnection) targetURL.openConnection()).getJarFile();
-        } catch (IOException ex) {
-            File file = getFileFromURL(url, cache, tmpDir);
-            if (file != null) {
-                return new JarFile(file);
-            }
-            else {
-                throw ex;
-            }
-        }
-    }
-
-    private static String toURLString(URL url) {
-        try {
-            return url.toURI().toURL().toString();
-        } catch (URISyntaxException | MalformedURLException e) {
-            return url.toString();
-        }
-    }
-    static final Comparator<String> FEATURE_PATH_COMP = new Comparator<String>() {
-        @Override
-        public int compare(final String o1, final String o2) {
-            // windows path conversion
-            final String key1 = o1.replace(File.separatorChar, '/');
-            final String key2 = o2.replace(File.separatorChar, '/');
-
-            final int lastSlash1 = key1.lastIndexOf('/');
-            final int lastSlash2 = key2.lastIndexOf('/');
-            if ( lastSlash1 == -1 || lastSlash2 == -1 ) {
-                return o1.compareTo(o2);
-            }
-            final String path1 = key1.substring(0, lastSlash1 + 1);
-            final String path2 = key2.substring(0, lastSlash2 + 1);
-            if ( path1.equals(path2) ) {
-                return o1.compareTo(o2);
-            }
-            if ( path1.startsWith(path2) ) {
-                return 1;
-            } else if ( path2.startsWith(path1) ) {
-                return -1;
-            }
-            return o1.compareTo(o2);
-        }
-    };
-
-    private static void processDir(final List<String> paths, final File dir)
-    throws IOException {
-        for(final File f : dir.listFiles()) {
-            if ( f.isFile() && !f.getName().startsWith(".")) {
-                // check if file is a reference
-                if ( f.getName().endsWith(EXTENSION_REF_FILE) || f.getName().endsWith(EXTENSION_FEATURE_FILE) ) {
-                    processFile(paths, f);
-                }
-            }
-        }
-    }
-
-    private static void processFile(final List<String> paths, final File f)
-    throws IOException {
-        if ( f.getName().endsWith(EXTENSION_REF_FILE) ) {
-            paths.addAll(parseFeatureRefFile(f));
-        } else {
-            paths.add(f.getAbsolutePath());
-        }
-    }
-}
diff --git a/src/main/java/org/apache/sling/feature/io/archive/ArchiveReader.java b/src/main/java/org/apache/sling/feature/io/archive/ArchiveReader.java
deleted file mode 100644
index 80c6e35..0000000
--- a/src/main/java/org/apache/sling/feature/io/archive/ArchiveReader.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * 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.archive;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
-import java.util.jar.Manifest;
-import java.util.stream.Collectors;
-
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.ExtensionType;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.io.json.FeatureJSONReader;
-
-/**
- * The feature archive reader can be used to read an archive based on a feature
- * model. The archive contains the model and all artifacts.
- */
-public class ArchiveReader {
-
-    @FunctionalInterface
-    public interface ArtifactConsumer {
-
-        /**
-         * Consume the artifact from the archive The input stream must not be closed by
-         * the consumer.
-         *
-         * @param artifactId The artifact id
-         * @param is         The input stream for the artifact
-         * @throws IOException If the artifact can't be consumed
-         */
-        void consume(ArtifactId artifactId, final InputStream is) throws IOException;
-    }
-
-    /**
-     * Read a feature model archive. The input stream is not closed. It is up to the
-     * caller to close the input stream.
-     *
-     * @param in       The input stream to read from.
-     * @param consumer The plugin consuming the binaries, including the features.
-     *                 If no consumer is provided, only the features will be returned.
-     * @return The feature models mentioned in the manifest of the archive
-     * @throws IOException If anything goes wrong
-     */
-    public static Set<Feature> read(final InputStream in,
-                             final ArtifactConsumer consumer)
-    throws IOException {
-        final JarInputStream jis = new JarInputStream(in);
-
-        // validate manifest and get feature ids
-        final String[] featureIds = checkHeaderAndExtractContents(jis.getManifest());
-        final List<String> featurePaths = Arrays.asList(featureIds).stream()
-                .map(id -> ArtifactId.parse(id).toMvnPath()).collect(Collectors.toList());
-
-
-        // read contents
-        final Set<Feature> features = new HashSet<>();
-        final Set<ArtifactId> artifacts = new HashSet<>();
-
-        JarEntry entry = null;
-        while ( ( entry = jis.getNextJarEntry() ) != null ) {
-            if (!entry.isDirectory() && !entry.getName().startsWith("META-INF/")) {
-                final ArtifactId id = ArtifactId.fromMvnPath(entry.getName());
-
-                if (featurePaths.contains(entry.getName())) {
-                    // feature - read to string first
-                    final String contents;
-                    try ( final StringWriter writer = new StringWriter()) {
-                        // don't close the input stream
-                        final Reader reader = new InputStreamReader(jis, "UTF-8");
-                        final char[] buffer = new char[2048];
-                        int l;
-                        while ( (l = reader.read(buffer)) > 0) {
-                            writer.write(buffer, 0, l);
-                        }
-                        writer.flush();
-                        contents = writer.toString();
-                    }
-                    // add to features
-                    try ( final Reader reader = new StringReader(contents) ) {
-                        final Feature feature = FeatureJSONReader.read(reader, entry.getName());
-                        features.add(feature);
-                    }
-                    // pass to consumer
-                    if ( consumer != null ) {
-                        try ( final InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8))) {
-                            consumer.consume(id, is);
-                        }
-                    }
-                } else {
-                    // artifact
-                    if (consumer != null) {
-                        consumer.consume(id, jis);
-                    }
-                    artifacts.add(id);
-                }
-            }
-            jis.closeEntry();
-        }
-        if (features.isEmpty()) {
-            throw new IOException("Not a feature model archive - feature file is missing.");
-        }
-
-        // check whether all artifacts from the models are in the archive
-        for (final Feature feature : features) {
-            for (final Artifact a : feature.getBundles()) {
-                if (!artifacts.contains(a.getId())) {
-                    throw new IOException("Artifact " + a.getId().toMvnId() + " is missing in archive");
-                }
-            }
-
-            for (final Extension e : feature.getExtensions()) {
-                if (e.getType() == ExtensionType.ARTIFACTS) {
-                    for (final Artifact a : e.getArtifacts()) {
-                        if (!artifacts.contains(a.getId())) {
-                            throw new IOException("Artifact " + a.getId().toMvnId() + " is missing in archive");
-                        }
-                    }
-                }
-            }
-        }
-        return features;
-    }
-
-    private static String[] checkHeaderAndExtractContents(final Manifest manifest) throws IOException {
-        if (manifest == null) {
-            throw new IOException("Not a feature model archive - manifest is missing.");
-        }
-        // check version header
-        final String version = manifest.getMainAttributes().getValue(ArchiveWriter.VERSION_HEADER);
-        if (version == null) {
-            throw new IOException("Not a feature model archive - version manifest header is missing.");
-        }
-        // validate version header
-        try {
-            final int number = Integer.valueOf(version);
-            if (number < 1 || number > ArchiveWriter.ARCHIVE_VERSION) {
-                throw new IOException("Not a feature model archive - invalid manifest header value: " + version);
-            }
-        } catch (final NumberFormatException nfe) {
-            throw new IOException("Not a feature model archive - invalid manifest header value: " + version);
-        }
-
-        // check contents header
-        final String contents = manifest.getMainAttributes().getValue(ArchiveWriter.CONTENTS_HEADER);
-        if (contents == null) {
-            throw new IOException("Not a feature model archive - contents manifest header is missing.");
-        }
-
-        return contents.split(",");
-    }
-}
diff --git a/src/main/java/org/apache/sling/feature/io/archive/ArchiveWriter.java b/src/main/java/org/apache/sling/feature/io/archive/ArchiveWriter.java
deleted file mode 100644
index 3207e56..0000000
--- a/src/main/java/org/apache/sling/feature/io/archive/ArchiveWriter.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * 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.archive;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.Writer;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.jar.JarEntry;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
-import java.util.stream.Collectors;
-import java.util.zip.Deflater;
-
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.ExtensionType;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.builder.ArtifactProvider;
-import org.apache.sling.feature.io.json.FeatureJSONReader;
-import org.apache.sling.feature.io.json.FeatureJSONWriter;
-
-/**
- * The feature archive writer can be used to create an archive based on a
- * feature model. The archive contains the feature model file and all artifacts
- * using a maven repository layout.
- */
-public class ArchiveWriter {
-
-    /** The manifest header marking an archive as a feature archive. */
-    public static final String VERSION_HEADER = "Feature-Archive-Version";
-
-    /** The manifest header listing the features in this archive. */
-    public static final String CONTENTS_HEADER = "Feature-Archive-Contents";
-
-    /** Current support version of the feature model archive. */
-    public static final int ARCHIVE_VERSION = 1;
-
-    /**
-     * Create a feature model archive. The output stream will not be closed by this
-     * method. The caller must call {@link JarOutputStream#close()}
-     * on the return output stream. The caller can
-     * add additional files through the return stream. However, the files
-     * should not be compressed (which is the default for the output stream).
-     *
-     * A feature model can be in different states: it might be a partial feature
-     * model, a complete feature model or an assembled feature model. This method
-     * takes the feature model as provided and only writes the listed bundles and
-     * artifacts of this feature model into the archive. In general, the best
-     * approach for sharing features is to archive {@link Feature#isComplete()
-     * complete} features.
-     *
-     * @param out          The output stream to write to
-     * @param baseManifest Optional base manifest used for creating the manifest.
-     * @param provider     The artifact provider
-     * @param features     The features model to archive
-     * @return The jar output stream.
-     * @throws IOException If anything goes wrong
-     */
-    public static JarOutputStream write(final OutputStream out,
-            final Manifest baseManifest,
-            final ArtifactProvider provider, final Feature... features)
-    throws IOException {
-        // create manifest
-        final Manifest manifest = (baseManifest == null ? new Manifest() : new Manifest(baseManifest));
-        manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
-        manifest.getMainAttributes().putValue(VERSION_HEADER, String.valueOf(ARCHIVE_VERSION));
-        manifest.getMainAttributes().putValue(CONTENTS_HEADER, String.join(",", Arrays.asList(features).stream()
-                .map(feature -> feature.getId().toMvnId()).collect(Collectors.toList())));
-
-        final Set<ArtifactId> artifacts = new HashSet<>();
-        final byte[] buffer = new byte[1024*1024*256];
-
-        // create archive
-        final JarOutputStream jos = new JarOutputStream(out, manifest);
-
-        // write everything without compression
-        jos.setLevel(Deflater.NO_COMPRESSION);
-        for (final Feature feature : features) {
-            writeFeature(artifacts, feature, provider, jos, buffer);
-        }
-
-
-
-        for (final Feature feature : features) {
-            for (final Artifact a : feature.getBundles()) {
-                writeArtifact(artifacts, provider, a, jos, buffer);
-            }
-
-            for (final Extension e : feature.getExtensions()) {
-                if (e.getType() == ExtensionType.ARTIFACTS) {
-                    final boolean isFeature = Extension.EXTENSION_NAME_ASSEMBLED_FEATURES.equals(e.getName());
-                    for (final Artifact a : e.getArtifacts()) {
-                        if ( isFeature ) {
-                            writeFeature(artifacts, provider, a.getId(), jos, buffer);
-                        } else {
-                            writeArtifact(artifacts, provider, a, jos, buffer);
-                        }
-                    }
-                }
-            }
-        }
-        return jos;
-    }
-
-    private static void writeFeature(final Set<ArtifactId> artifacts,
-            final Feature feature,
-            final ArtifactProvider provider,
-            final JarOutputStream jos, final byte[] buffer) throws IOException {
-        if ( artifacts.add(feature.getId())) {
-            final JarEntry entry = new JarEntry(feature.getId().toMvnPath());
-            jos.putNextEntry(entry);
-            final Writer writer = new OutputStreamWriter(jos, StandardCharsets.UTF_8);
-            FeatureJSONWriter.write(writer, feature);
-            writer.flush();
-            jos.closeEntry();
-
-            if ( feature.getPrototype() != null ) {
-                writeFeature(artifacts, provider, feature.getPrototype().getId(), jos, buffer);
-            }
-        }
-    }
-
-    private static void writeFeature(final Set<ArtifactId> artifacts,
-            final ArtifactProvider provider,
-            final ArtifactId featureId,
-            final JarOutputStream jos, final byte[] buffer) throws IOException {
-        if ( !artifacts.contains(featureId)) {
-            final URL url = provider.provide(featureId);
-            try ( final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                  final InputStream is = url.openStream()) {
-                int l = 0;
-                while ( (l = is.read(buffer)) > 0 ) {
-                    baos.write(buffer, 0, l);
-                }
-                final String contents = new String(baos.toByteArray(), StandardCharsets.UTF_8);
-                try ( final Reader reader = new StringReader(contents)) {
-                    final Feature feature = FeatureJSONReader.read(reader, featureId.toMvnId());
-                    writeFeature(artifacts, feature, provider, jos, buffer);
-                }
-            }
-        }
-    }
-
-    private static void writeArtifact(final Set<ArtifactId> artifacts,
-            final ArtifactProvider provider,
-            final Artifact artifact,
-            final JarOutputStream jos,
-            final byte[] buffer) throws IOException {
-        if ( artifacts.add(artifact.getId())) {
-            final JarEntry artifactEntry = new JarEntry(artifact.getId().toMvnPath());
-            jos.putNextEntry(artifactEntry);
-
-            final URL url = provider.provide(artifact.getId());
-            if (url == null) {
-                throw new IOException("Unable to find artifact " + artifact.getId().toMvnId());
-            }
-            try (final InputStream is = url.openStream()) {
-                int l = 0;
-                while ( (l = is.read(buffer)) > 0 ) {
-                    jos.write(buffer, 0, l);
-                }
-            }
-            jos.closeEntry();
-        }
-    }
-}
diff --git a/src/main/java/org/apache/sling/feature/io/archive/package-info.java b/src/main/java/org/apache/sling/feature/io/archive/package-info.java
deleted file mode 100644
index 46413c3..0000000
--- a/src/main/java/org/apache/sling/feature/io/archive/package-info.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.
- */
-
-@org.osgi.annotation.versioning.Version("1.0.0")
-package org.apache.sling.feature.io.archive;
-
-
diff --git a/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactHandler.java b/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactHandler.java
deleted file mode 100644
index c4d87ce..0000000
--- a/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactHandler.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.artifacts;
-
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-/**
- * A handler provides a file object for an artifact.
- */
-public class ArtifactHandler {
-
-    private final String url;
-
-    private final URL localURL;
-
-    /**
-     * Create a new handler.
-     *
-     * @param url  The url of the artifact
-     * @param localURL The local URL for the artifact
-     */
-    public ArtifactHandler(final String url, final URL localURL) {
-        this.url = url;
-        this.localURL = localURL;
-    }
-
-    /**
-     * Create a new handler.
-     *
-     * @param file The file for the artifact
-     * @throws MalformedURLException
-     * @since 1.1.0
-     */
-    public ArtifactHandler(final File file) throws MalformedURLException {
-        this(file.toURI().toString(), file.toURI().toURL());
-    }
-
-    /**
-     * Get the url of the artifact
-     *
-     * @return The url.
-     */
-    public String getUrl() {
-        return url;
-    }
-
-    /**
-     * Get a local url for the artifact
-     *
-     * @return The file
-     */
-    public URL getLocalURL() {
-        return localURL;
-    }
-}
diff --git a/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManager.java b/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManager.java
deleted file mode 100644
index d1515ac..0000000
--- a/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManager.java
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * 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.artifacts;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.lang.ProcessBuilder.Redirect;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.ServiceLoader;
-
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.builder.FeatureProvider;
-import org.apache.sling.feature.io.artifacts.spi.ArtifactProvider;
-import org.apache.sling.feature.io.artifacts.spi.ArtifactProviderContext;
-import org.apache.sling.feature.io.json.FeatureJSONReader;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The artifact manager is the central service to get artifacts.
- * It uses {@link ArtifactProvider}s to get artifacts. The
- * providers are loaded using the service loader.
- */
-public class ArtifactManager
-        implements AutoCloseable, org.apache.sling.feature.builder.ArtifactProvider {
-
-    private final Logger logger = LoggerFactory.getLogger(this.getClass());
-
-    /** The map of providers. */
-    private final Map<String, ArtifactProvider> providers;
-
-    /** The configuration */
-    private final ArtifactManagerConfig config;
-
-    /**
-     * Get an artifact manager based on the configuration
-     * @param config The configuration
-     * @return The artifact manager
-     * @throws IOException If the manager can't be initialized
-     */
-    public static ArtifactManager getArtifactManager(final ArtifactManagerConfig config) throws IOException {
-        final ServiceLoader<ArtifactProvider> loader = ServiceLoader.load(ArtifactProvider.class);
-        final Map<String, ArtifactProvider> providers = new HashMap<>();
-        for(final ArtifactProvider provider : loader) {
-            providers.put(provider.getProtocol(), provider);
-        }
-
-        final String[] repositoryURLs = new String[config.getRepositoryUrls().length];
-        int index = 0;
-        for(final String urlString : config.getRepositoryUrls()) {
-            repositoryURLs[index] = urlString;
-            index++;
-        }
-        // default
-        if ( !providers.containsKey("*") ) {
-            providers.put("*", new DefaultArtifactHandler());
-        }
-
-        return new ArtifactManager(config, providers);
-    }
-
-    /**
-     * Internal constructor for the manager
-     * @param config The configuration
-     * @param providers The provider map
-     * @throws IOException If the manager can't be initialized
-     */
-    ArtifactManager(final ArtifactManagerConfig config, final Map<String, ArtifactProvider> providers)
-    throws IOException {
-        this.config = config;
-        this.providers = providers;
-        try {
-            for(final ArtifactProvider provider : this.providers.values()) {
-                provider.init(config);
-            }
-        } catch ( final IOException io) {
-            shutdown();
-            throw io;
-        }
-    }
-
-    /**
-     * Shutdown the artifact manager.
-     */
-    public void shutdown() {
-        for(final ArtifactProvider provider : this.providers.values()) {
-            provider.shutdown();
-        }
-        this.providers.clear();
-    }
-
-    @Override
-    public void close() {
-        shutdown();
-    }
-
-    @Override
-    public URL provide(final ArtifactId id) {
-        try {
-            final ArtifactHandler handler = this.getArtifactHandler(id.toMvnUrl());
-            return handler.getLocalURL();
-        } catch (final IOException e) {
-            // ignore
-            return null;
-        }
-    }
-
-    /**
-     * Return a feature provider based on this artifact manager
-     *
-     * @return A feature provider
-     * @since 1.1.0
-     */
-    public FeatureProvider toFeatureProvider() {
-        return (id -> {
-            try {
-                final ArtifactHandler handler = this.getArtifactHandler(id.toMvnUrl());
-                try (final Reader r = new InputStreamReader(handler.getLocalURL().openStream(), "UTF-8")) {
-                    final Feature f = FeatureJSONReader.read(r, handler.getUrl());
-                    return f;
-                }
-            } catch (final IOException e) {
-                // ignore
-                return null;
-            }
-        });
-    }
-
-    private final URL getArtifactFromProviders(final String url, final String relativeCachePath) throws IOException {
-        final int pos = url.indexOf(":");
-        final String scheme = url.substring(0, pos);
-
-        ArtifactProvider provider = this.providers.get(scheme);
-        if ( provider == null ) {
-            provider = this.providers.get("*");
-        }
-        if ( provider == null ) {
-            throw new IOException("No URL provider found for " + url);
-        }
-        return provider.getArtifact(url, relativeCachePath);
-    }
-
-    /**
-     * Get the full artifact url and file for an artifact.
-     *
-     * @param url Artifact url or relative path.
-     * @return Absolute url and file in the form of a handler.
-     * @throws IOException If something goes wrong or the artifact can't be found.
-     */
-    public ArtifactHandler getArtifactHandler(final String url) throws IOException {
-        logger.debug("Trying to get artifact for {}", url);
-
-        final String path;
-
-        ArtifactId artifactId = null;
-
-        if ( url.startsWith("mvn:") ) {
-            // mvn url
-            try {
-                artifactId = ArtifactId.fromMvnUrl(url);
-                path = artifactId.toMvnPath();
-            } catch (final IllegalArgumentException iae) {
-                throw new IOException(iae.getMessage(), iae);
-            }
-        } else if ( url.startsWith(":") ) {
-            // repository path
-            path = url.substring(1);
-
-        } else if ( url.indexOf(":/") > 0 ) {
-
-            // absolute URL
-            int pos = url.indexOf(":/") + 2;
-            while ( url.charAt(pos) == '/') {
-                pos++;
-            }
-            final URL file = this.getArtifactFromProviders(url, url.substring(pos));
-            if ( file == null ) {
-                throw new IOException("Artifact " + url + " not found.");
-            }
-            return new ArtifactHandler(url, file);
-
-        } else {
-            // file (either relative or absolute)
-            final File f = new File(url);
-            if ( !f.exists()) {
-                throw new IOException("Artifact " + url + " not found.");
-            }
-            return new ArtifactHandler(f);
-        }
-        logger.debug("Querying repositories for {}", path);
-
-        for(final String repoUrl : this.config.getRepositoryUrls()) {
-            final StringBuilder builder = new StringBuilder();
-            builder.append(repoUrl);
-            builder.append('/');
-            builder.append(path);
-
-            final String artifactUrl = builder.toString();
-            final int pos = artifactUrl.indexOf(":");
-            final String scheme = artifactUrl.substring(0, pos);
-
-            ArtifactProvider handler = this.providers.get(scheme);
-            if ( handler == null ) {
-                handler = this.providers.get("*");
-            }
-            if ( handler == null ) {
-                throw new IOException("No URL handler found for " + artifactUrl);
-            }
-
-            logger.debug("Checking {} to get artifact from {}", handler, artifactUrl);
-
-            final URL file = handler.getArtifact(artifactUrl, path);
-            if ( file != null ) {
-                logger.debug("Found artifact {}", artifactUrl);
-                return new ArtifactHandler(artifactUrl, file);
-            }
-
-            // check for SNAPSHOT
-            final int lastSlash = artifactUrl.lastIndexOf('/');
-            final int startSnapshot = artifactUrl.indexOf("-SNAPSHOT", lastSlash + 1);
-
-            if ( startSnapshot > -1 ) {
-                // special snapshot handling
-                final String metadataUrl = artifactUrl.substring(0, lastSlash) + "/maven-metadata.xml";
-                try {
-                    final ArtifactHandler metadataHandler = this.getArtifactHandler(metadataUrl);
-
-                    final String contents = getFileContents(metadataHandler);
-
-                    final String latestVersion = getLatestSnapshot(contents);
-                    if ( latestVersion != null ) {
-                        final String name = artifactUrl.substring(lastSlash); // includes slash
-                        final String fullURL = artifactUrl.substring(0, lastSlash) + name.replace("SNAPSHOT", latestVersion);
-                        int pos2 = fullURL.indexOf(":/") + 2;
-                        while ( fullURL.charAt(pos2) == '/') {
-                            pos2++;
-                        }
-                        final URL file2 = this.getArtifactFromProviders(fullURL, path);
-                        if ( file2 == null ) {
-                            throw new IOException("Artifact " + fullURL + " not found.");
-                        }
-                        return new ArtifactHandler(artifactUrl, file2);
-                    }
-                } catch ( final IOException ignore ) {
-                    // we ignore this but report the original 404
-                }
-            }
-        }
-
-        // if we have an artifact id and using mvn is enabled, we try this as a last
-        // resort
-        if (artifactId != null && this.config.isUseMvn()) {
-            final File file = getArtifactFromMvn(artifactId);
-            if (file != null) {
-                return new ArtifactHandler(file);
-            }
-        }
-        throw new IOException("Artifact " + url + " not found in any repository.");
-    }
-
-    protected String getFileContents(final ArtifactHandler handler) throws IOException {
-        final StringBuilder sb = new StringBuilder();
-        try (BufferedReader reader = new BufferedReader(new InputStreamReader(handler.getLocalURL().openStream(), "UTF-8"))) {
-            for(String line = reader.readLine(); line != null; line = reader.readLine()) {
-                sb.append(line).append('\n');
-            }
-        }
-
-        return sb.toString();
-    }
-
-    public static String getValue(final String xml, final String[] xpath) {
-        String value = null;
-        int pos = 0;
-        for(final String name : xpath) {
-            final String element = '<' + name + '>';
-
-            pos = xml.indexOf(element, pos);
-            if ( pos == -1 ) {
-                final String elementWithAttributes = '<' + name + ' ';
-                pos = xml.indexOf(elementWithAttributes, pos);
-                if ( pos == -1 ) {
-                    break;
-                }
-            }
-            pos = xml.indexOf('>', pos) + 1;
-        }
-        if ( pos != -1 ) {
-            final int endPos = xml.indexOf("</", pos);
-            if ( endPos != -1 ) {
-                value = xml.substring(pos, endPos).trim();
-            }
-        }
-        return value;
-    }
-
-    public static String getLatestSnapshot(final String mavenMetadata) {
-        final String timestamp = getValue(mavenMetadata, new String[] {"metadata", "versioning", "snapshot", "timestamp"});
-        final String buildNumber = getValue(mavenMetadata, new String[] {"metadata", "versioning", "snapshot", "buildNumber"});
-
-        if ( timestamp != null && buildNumber != null ) {
-            return timestamp + '-' + buildNumber;
-        }
-
-        return null;
-    }
-
-    private static final class DefaultArtifactHandler implements ArtifactProvider {
-
-        private final Logger logger = LoggerFactory.getLogger(this.getClass());
-
-        private volatile File cacheDir;
-
-        private volatile ArtifactProviderContext config;
-
-        @Override
-        public String getProtocol() {
-            return "*";
-        }
-
-        @Override
-        public void init(final ArtifactProviderContext config) throws IOException {
-            this.cacheDir = config.getCacheDirectory();
-            this.config = config;
-        }
-
-        @Override
-        public void shutdown() {
-            this.config = null;
-            this.cacheDir = null;
-        }
-
-        @Override
-        public URL getArtifact(final String url, final String relativeCachePath) {
-            logger.debug("Checking url to be local file {}", url);
-            // check if this is already a local file
-            try {
-                final File f = new File(new URL(url).toURI());
-                if ( f.exists() ) {
-                    this.config.incLocalArtifacts();
-                    return f.toURI().toURL();
-                }
-                return null;
-            } catch ( final URISyntaxException ise) {
-                // ignore
-            } catch ( final IllegalArgumentException iae) {
-                // ignore
-            } catch ( final MalformedURLException mue) {
-                // ignore
-            }
-            logger.debug("Checking remote url {}", url);
-            try {
-                // check for url
-                if ( url.indexOf(":") == -1 ) {
-                    return null;
-                }
-
-                final String filePath = (this.cacheDir.getAbsolutePath() + File.separatorChar + relativeCachePath).replace('/', File.separatorChar);
-                final File cacheFile = new File(filePath);
-
-                if ( !cacheFile.exists() ) {
-                    cacheFile.getParentFile().mkdirs();
-                    final URL u = new URL(url);
-                    final URLConnection con = u.openConnection();
-                    final String userInfo = u.getUserInfo();
-                    if (userInfo != null) {
-                        con.addRequestProperty("Authorization", "Basic " + Base64.getEncoder().encodeToString(u.toURI().getUserInfo().getBytes("UTF-8")));
-                    }
-                    con.connect();
-
-                    final InputStream readIS = con.getInputStream();
-                    final byte[] buffer = new byte[32768];
-                    int l;
-                    OutputStream os = null;
-                    try {
-                        os = new FileOutputStream(cacheFile);
-                        while ( (l = readIS.read(buffer)) >= 0 ) {
-                            os.write(buffer, 0, l);
-                        }
-                    } finally {
-                        try {
-                            readIS.close();
-                        } catch ( final IOException ignore) {
-                            // ignore
-                        }
-                        if ( os != null ) {
-                            try {
-                                os.close();
-                            } catch ( final IOException ignore ) {
-                                // ignore
-
-                            }
-                        }
-                    }
-                    this.config.incDownloadedArtifacts();
-                } else {
-                    this.config.incCachedArtifacts();
-                }
-                return cacheFile.toURI().toURL();
-            } catch ( final FileNotFoundException e) {
-                logger.trace("File not found here (keep on looking): '{}'", url);
-                // Do not report if the file does not exist as we cycle through the various sources
-                return null;
-            } catch ( final Exception e) {
-                logger.info("Artifact not found in one repository", e);
-                // ignore for now
-                return null;
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "DefaultArtifactHandler";
-        }
-    }
-
-    private File getArtifactFromMvn(final ArtifactId artifactId) {
-        final String filePath = this.config.getMvnHome()
-                .concat(artifactId.toMvnPath().replace('/', File.separatorChar));
-        logger.debug("Trying to fetch artifact {} from local mvn repository {}", artifactId.toMvnId(), filePath);
-        final File f = new File(filePath);
-        if (!f.exists() || !f.isFile() || !f.canRead()) {
-            logger.debug("Trying to download {}", artifactId.toMvnId());
-            try {
-                this.downloadArtifact(artifactId);
-            } catch (final IOException ioe) {
-                logger.debug("Error downloading file.", ioe);
-            }
-            if (!f.exists() || !f.isFile() || !f.canRead()) {
-                logger.info("Artifact not found {}", artifactId.toMvnId());
-
-                return null;
-            }
-        }
-        return f;
-    }
-
-    /**
-     * Download artifact from maven
-     *
-     * @throws IOException
-     */
-    private void downloadArtifact(final ArtifactId artifactId) throws IOException {
-        // create fake pom
-        final Path dir = Files.createTempDirectory(null);
-        try {
-            final List<String> lines = new ArrayList<String>();
-            lines.add(
-                    "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">");
-            lines.add("    <modelVersion>4.0.0</modelVersion>");
-            lines.add("    <groupId>org.apache.sling</groupId>");
-            lines.add("    <artifactId>temp-artifact</artifactId>");
-            lines.add("    <version>1-SNAPSHOT</version>");
-            lines.add("    <dependencies>");
-            lines.add("        <dependency>");
-            lines.add("            <groupId>".concat(artifactId.getGroupId()).concat("</groupId>"));
-            lines.add("            <artifactId>".concat(artifactId.getArtifactId()).concat("</artifactId>"));
-            lines.add("            <version>".concat(artifactId.getVersion()).concat("</version>"));
-            if (artifactId.getClassifier() != null) {
-                lines.add("            <classifier>".concat(artifactId.getClassifier()).concat("</classifier>"));
-            }
-            if (!"bundle".equals(artifactId.getType()) && !"jar".equals(artifactId.getType())) {
-                lines.add("            <type>".concat(artifactId.getType()).concat("</type>"));
-            }
-            lines.add("            <scope>provided</scope>");
-            lines.add("        </dependency>");
-            lines.add("    </dependencies>");
-            lines.add("</project>");
-            logger.debug("Writing pom to {}", dir);
-            Files.write(dir.resolve("pom.xml"), lines, Charset.forName("UTF-8"));
-
-            final File output = dir.resolve("output.txt").toFile();
-            final File error = dir.resolve("error.txt").toFile();
-
-            // invoke maven
-            logger.debug("Invoking mvn...");
-            final ProcessBuilder pb = new ProcessBuilder("mvn", "verify");
-            pb.directory(dir.toFile());
-            pb.redirectOutput(Redirect.to(output));
-            pb.redirectError(Redirect.to(error));
-
-            final Process p = pb.start();
-            try {
-                p.waitFor();
-            } catch (final InterruptedException e) {
-                Thread.currentThread().interrupt();
-            }
-
-        } finally {
-            Files.walk(dir).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
-        }
-    }
-}
diff --git a/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManagerConfig.java b/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManagerConfig.java
deleted file mode 100644
index 82e6476..0000000
--- a/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManagerConfig.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * 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.artifacts;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.sling.feature.io.artifacts.spi.ArtifactProviderContext;
-
-/**
- * This class holds the configuration of artifact manager.
- */
-public class ArtifactManagerConfig implements ArtifactProviderContext {
-
-    /** The repository urls. */
-    private volatile String[] repositoryUrls;
-
-    /** The cache directory. */
-    private volatile File cacheDirectory;
-
-    /** Metrics for artifacts used from the cache. */
-    private final AtomicLong cachedArtifacts = new AtomicLong();
-
-    /** Metrics for artifacts needed to be downloaded. */
-    private final AtomicLong downloadedArtifacts = new AtomicLong();
-
-    /** Metrics for artifacts read locally. */
-    private final AtomicLong localArtifacts = new AtomicLong();
-
-    /** Whether locally mvn command can be used to download artifacts. */
-    private volatile boolean useMvn = false;
-
-    /**
-     * The .m2 directory.
-     */
-    private final String repoHome;
-
-    /**
-     * Create a new configuration object. Set the default values
-     */
-    public ArtifactManagerConfig() {
-        // set defaults
-        this.repositoryUrls = new String[] {
-                "file://" + new File(System.getProperty("user.home")).toURI().getPath() + ".m2/repository",
-                "https://repo.maven.apache.org/maven2",
-                "https://repository.apache.org/content/groups/snapshots"
-                };
-        try {
-            this.cacheDirectory = Files.createTempDirectory("slingfeature").toFile();
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-        this.repoHome = System.getProperty("user.home") + "/.m2/repository/";
-    }
-
-    /**
-     * Set the repository urls
-     * @param urls The repository urls
-     */
-    public void setRepositoryUrls(final String[] urls) {
-        if ( urls == null || urls.length == 0 ) {
-            this.repositoryUrls = null;
-        } else {
-            this.repositoryUrls = new String[urls.length];
-            System.arraycopy(urls, 0, this.repositoryUrls, 0, urls.length);
-            for(int i=0; i<this.repositoryUrls.length; i++) {
-                if ( this.repositoryUrls[i].endsWith("/") ) {
-                    this.repositoryUrls[i] = this.repositoryUrls[i].substring(0, this.repositoryUrls[i].length() - 1);
-                }
-            }
-        }
-    }
-
-    /**
-     * Get the repository urls.
-     * A repository url does not end with a slash.
-     * @return The repository urls.
-     */
-    public String[] getRepositoryUrls() {
-        return repositoryUrls;
-    }
-
-    /**
-     * Get the cache directory
-     * @return The cache directory.
-     */
-    @Override
-    public File getCacheDirectory() {
-        return cacheDirectory;
-    }
-
-    /**
-     * Set the cache directory
-     * @param dir The cache directory
-     */
-    public void setCacheDirectory(final File dir) {
-        this.cacheDirectory = dir;
-    }
-
-    @Override
-    public void incCachedArtifacts() {
-        this.cachedArtifacts.incrementAndGet();
-    }
-
-    @Override
-    public void incDownloadedArtifacts() {
-        this.downloadedArtifacts.incrementAndGet();
-    }
-
-    @Override
-    public void incLocalArtifacts() {
-        this.localArtifacts.incrementAndGet();
-    }
-
-    /**
-     * Get the number of cached artifacts
-     * @return The number of cached artifacts
-     */
-    public long getCachedArtifacts() {
-        return this.cachedArtifacts.get();
-    }
-
-    /**
-     * Get the number of downloaded artifacts
-     * @return The number of downloaded artifacts
-     */
-    public long getDownloadedArtifacts() {
-        return this.downloadedArtifacts.get();
-    }
-
-    /**
-     * Get the number of local artifacts
-     * @return The number of local artifacts
-     */
-    public long getLocalArtifacts() {
-        return this.localArtifacts.get();
-    }
-
-    /**
-     * Should mvn be used if an artifact can't be found in the repositories
-     *
-     * @return Whether mvn command should be used.
-     * @since 1.1.0
-     */
-    public boolean isUseMvn() {
-        return useMvn;
-    }
-
-    /**
-     * Set whether mvn should be used to get artifacts.
-     *
-     * @param useMvn flag for enabling mvn
-     * @since 1.1.0
-     */
-    public void setUseMvn(final boolean useMvn) {
-        this.useMvn = useMvn;
-    }
-
-    /**
-     * Return mvn home
-     * 
-     * @since 1.1.0
-     */
-    String getMvnHome() {
-        return this.repoHome;
-    }
-}
diff --git a/src/main/java/org/apache/sling/feature/io/artifacts/package-info.java b/src/main/java/org/apache/sling/feature/io/artifacts/package-info.java
deleted file mode 100644
index f2229fb..0000000
--- a/src/main/java/org/apache/sling/feature/io/artifacts/package-info.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.
- */
-
-@org.osgi.annotation.versioning.Version("1.1.0")
-package org.apache.sling.feature.io.artifacts;
-
-
diff --git a/src/main/java/org/apache/sling/feature/io/artifacts/spi/ArtifactProvider.java b/src/main/java/org/apache/sling/feature/io/artifacts/spi/ArtifactProvider.java
deleted file mode 100644
index 5127ac5..0000000
--- a/src/main/java/org/apache/sling/feature/io/artifacts/spi/ArtifactProvider.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.artifacts.spi;
-
-import java.io.IOException;
-import java.net.URL;
-
-import org.osgi.annotation.versioning.ConsumerType;
-
-/**
- * The artifact provider is an extension point for providing artifacts
- * from different sources, like for example s3.
- */
-@ConsumerType
-public interface ArtifactProvider {
-
-    /**
-     * The protocol name of the provider, e.g. "s3"
-     * @return The protocol name.
-     */
-    String getProtocol();
-
-    /**
-     * Initialize the provider.
-     * @param context The context
-     * @throws IOException If the provider can't be initialized.
-     */
-    void init(ArtifactProviderContext context) throws IOException;
-
-    /**
-     * Shutdown the provider.
-     */
-    void shutdown();
-
-    /**
-     * Get a local file for the artifact URL.
-     *
-     * @param url Artifact url
-     * @param relativeCachePath A relative path that can be used as a cache path
-     *                          by the provider. The path does not start with a slash.
-     * @return A local url if the artifact exists or {@code null}
-     */
-    URL getArtifact(String url, String relativeCachePath);
-}
diff --git a/src/main/java/org/apache/sling/feature/io/artifacts/spi/ArtifactProviderContext.java b/src/main/java/org/apache/sling/feature/io/artifacts/spi/ArtifactProviderContext.java
deleted file mode 100644
index dccfbfc..0000000
--- a/src/main/java/org/apache/sling/feature/io/artifacts/spi/ArtifactProviderContext.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.artifacts.spi;
-
-import java.io.File;
-
-import org.osgi.annotation.versioning.ProviderType;
-
-/**
- * This is the context for the artifact providers
- */
-@ProviderType
-public interface ArtifactProviderContext {
-
-    /**
-     * Get the cache directory
-     * @return The cache directory.
-     */
-    File getCacheDirectory();
-
-    /**
-     * Inform about an artifact found in the cache.
-     */
-    void incCachedArtifacts();
-
-    /**
-     * Inform about an artifact being downloaded
-     */
-    void incDownloadedArtifacts();
-
-    /**
-     * Inform about an artifact found locally.
-     */
-    void incLocalArtifacts();
-}
diff --git a/src/main/java/org/apache/sling/feature/io/artifacts/spi/package-info.java b/src/main/java/org/apache/sling/feature/io/artifacts/spi/package-info.java
deleted file mode 100644
index bb0c1f0..0000000
--- a/src/main/java/org/apache/sling/feature/io/artifacts/spi/package-info.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.
- */
-
-@org.osgi.annotation.versioning.Version("1.0.0")
-package org.apache.sling.feature.io.artifacts.spi;
-
-
diff --git a/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONReader.java b/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONReader.java
deleted file mode 100644
index 7d4c555..0000000
--- a/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONReader.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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 java.io.IOException;
-import java.io.Reader;
-import java.util.Hashtable;
-import java.util.Map;
-
-import org.apache.felix.cm.json.ConfigurationReader;
-import org.apache.felix.cm.json.ConfigurationResource;
-import org.apache.sling.feature.Configuration;
-import org.apache.sling.feature.Configurations;
-
-/**
- * JSON Reader for configurations.
- */
-public class ConfigurationJSONReader {
-
-    /**
-     * Read a map of configurations from the reader
-     * The reader is not closed. It is up to the caller to close the reader.
-     *
-     * @param reader The reader for the configuration
-     * @param location Optional location
-     * @return The read configurations
-     * @throws IOException If an IO errors occurs or the JSON is invalid.
-     */
-    public static Configurations read(final Reader reader, final String location)
-    throws IOException {
-        try {
-            final ConfigurationJSONReader mr = new ConfigurationJSONReader();
-            return mr.readConfigurations(location, reader);
-        } catch (final IllegalStateException | IllegalArgumentException e) {
-            throw new IOException(e);
-        }
-    }
-
-    Configurations readConfigurations(final String location, final Reader reader) throws IOException {
-        final Configurations result = new Configurations();
-
-        final ConfigurationReader cfgReader = org.apache.felix.cm.json.Configurations
-            .buildReader()
-            .withIdentifier(location)
-            .verifyAsBundleResource(true)
-            .build(reader);
-        final ConfigurationResource rsrc = cfgReader.readConfigurationResource();
-        for(Map.Entry<String, Hashtable<String, Object>> entry : rsrc.getConfigurations().entrySet() ) {
-            final Configuration cf = new Configuration(entry.getKey());
-            for(final Map.Entry<String, Object> prop : entry.getValue().entrySet()) {
-                cf.getProperties().put(prop.getKey(), prop.getValue());
-            }
-        }
-
-        return result;
-    }
-}
-
-
diff --git a/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONWriter.java b/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONWriter.java
deleted file mode 100644
index e189e3b..0000000
--- a/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONWriter.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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 java.io.IOException;
-import java.io.Writer;
-import java.util.Collections;
-import java.util.Hashtable;
-
-import org.apache.felix.cm.json.ConfigurationResource;
-import org.apache.felix.cm.json.ConfigurationWriter;
-import org.apache.sling.feature.Configuration;
-import org.apache.sling.feature.Configurations;
-
-/** JSON writer for configurations */
-public class ConfigurationJSONWriter {
-
-    /** Writes the configurations to the writer. The writer is not closed.
-     *
-     * @param writer Writer
-     * @param configs List of configurations
-     * @throws IOException If writing fails */
-    public static void write(final Writer writer, final Configurations configs)
-            throws IOException {
-        final ConfigurationJSONWriter w = new ConfigurationJSONWriter();
-        w.writeConfigurations(writer, configs);
-    }
-
-    private void writeConfigurations(final Writer writer, final Configurations configs)
-            throws IOException {
-
-        final ConfigurationWriter cfgWriter = org.apache.felix.cm.json.Configurations
-            .buildWriter()
-            .build(writer);
-
-        final ConfigurationResource rsrc = new ConfigurationResource();
-        for(final Configuration cfg : configs) {
-            final Hashtable<String, Object> properties;
-            if ( cfg.getProperties() instanceof Hashtable && cfg.getProperties().get(Configuration.PROP_ARTIFACT_ID) == null ) {
-                properties = (Hashtable<String, Object>)cfg.getProperties();
-            } else {
-                properties = org.apache.felix.cm.json.Configurations.newConfiguration();
-                for(final String name : Collections.list(cfg.getProperties().keys()) ) {
-                    if ( !Configuration.PROP_ARTIFACT_ID.equals(name) ) {
-                        properties.put(name, cfg.getProperties().get(name));
-                    }
-                }
-            }
-            rsrc.getConfigurations().put(cfg.getPid(), properties);
-        }
-        cfgWriter.writeConfigurationResource(rsrc);
-    }
-
-
-}
diff --git a/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java b/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java
deleted file mode 100644
index 71c1e07..0000000
--- a/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java
+++ /dev/null
@@ -1,716 +0,0 @@
-/*
- * 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 java.io.IOException;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.BiConsumer;
-
-import javax.json.Json;
-import javax.json.JsonArray;
-import javax.json.JsonException;
-import javax.json.JsonObject;
-import javax.json.JsonString;
-import javax.json.JsonStructure;
-import javax.json.JsonValue;
-import javax.json.JsonValue.ValueType;
-
-import org.apache.felix.cm.json.ConfigurationReader;
-import org.apache.felix.cm.json.ConfigurationResource;
-import org.apache.felix.utils.resource.CapabilityImpl;
-import org.apache.felix.utils.resource.RequirementImpl;
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Bundles;
-import org.apache.sling.feature.Configuration;
-import org.apache.sling.feature.Configurations;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.ExtensionState;
-import org.apache.sling.feature.ExtensionType;
-import org.apache.sling.feature.Extensions;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.MatchingRequirement;
-import org.apache.sling.feature.Prototype;
-import org.osgi.resource.Capability;
-import org.osgi.resource.Resource;
-
-/**
- * This class offers a static method to read a {@code Feature} using a {@code Reader} instance.
- */
-public class FeatureJSONReader {
-
-    /**
-     * 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 {
-        try {
-            final FeatureJSONReader mr = new FeatureJSONReader(location);
-            return mr.readFeature(reader);
-        } catch (final IllegalStateException | IllegalArgumentException | JsonException e) {
-            throw new IOException(e);
-        }
-    }
-
-    /** The read feature. */
-    private Feature feature;
-
-    /** The optional location. */
-    private final String location;
-
-    /** Exception prefix containing the location (if set) */
-    private final String exceptionPrefix;
-
-    /**
-     * private constructor
-     * @param location Optional location
-     */
-    private FeatureJSONReader(final String location) {
-        this.location = location;
-        if ( location == null ) {
-            exceptionPrefix = "";
-        } else {
-            exceptionPrefix = location.concat(" : ");
-        }
-    }
-
-    /**
-     * Get the feature id
-     * @param json The feature json
-     * @return The artifact id
-     * @throws IOException If the id is missing
-     */
-    private ArtifactId getFeatureId(final JsonObject json) throws IOException {
-        if ( !json.containsKey(JSONConstants.FEATURE_ID) ) {
-            throw new IOException(this.exceptionPrefix.concat("Feature id is missing"));
-        }
-        return checkTypeArtifactId(JSONConstants.FEATURE_ID, json.get(JSONConstants.FEATURE_ID));
-    }
-
-    private String getProperty(final JsonObject json, final String key) throws IOException {
-        final JsonValue val = json.get(key);
-        if ( val != null ) {
-            return checkTypeString(key, val);
-        }
-        return null;
-    }
-
-    /**
-     * Read the variables section
-     * @param json The json describing the feature or application
-     * @param kvMap The variables will be written to this Key Value Map
-     * @throws IOException If the json is invalid.
-     */
-    private void readVariables(JsonObject json, Map<String,String> kvMap) throws IOException {
-        if (json.containsKey(JSONConstants.FEATURE_VARIABLES)) {
-            final JsonValue variablesObj = json.get(JSONConstants.FEATURE_VARIABLES);
-
-            for (final Map.Entry<String, JsonValue> entry : checkTypeObject(JSONConstants.FEATURE_VARIABLES, variablesObj).entrySet()) {
-                final String key = entry.getKey();
-                // skip comments
-                if ( !key.startsWith("#") ) {
-                    if (kvMap.get(key) != null) {
-                        throw new IOException(this.exceptionPrefix.concat("Duplicate variable ").concat(key));
-                    }
-                    final String value = checkScalarType("variable value", entry.getValue(), true);
-                    kvMap.put(key, value);
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Read the bundles / start levels section
-     * @param json The json object describing the feature
-     * @param container The bundles container
-     * @param configContainer The configurations container
-     * @throws IOException If the json is invalid.
-     */
-    private void readBundles(
-            final JsonObject json,
-            final Bundles container,
-            final Configurations configContainer) throws IOException {
-        if ( json.containsKey(JSONConstants.FEATURE_BUNDLES)) {
-            final List<Artifact> list = new ArrayList<>();
-            readArtifacts(JSONConstants.FEATURE_BUNDLES, "bundle", list, json.get(JSONConstants.FEATURE_BUNDLES), configContainer);
-
-            for(final Artifact a : list) {
-                if ( container.containsExact(a.getId())) {
-                    throw new IOException(exceptionPrefix + "Duplicate identical bundle " + a.getId().toMvnId());
-                }
-                try {
-                    // check start order
-                    a.getStartOrder();
-                } catch ( final IllegalArgumentException nfe) {
-                    throw new IOException(exceptionPrefix + "Illegal start order '" + a.getMetadata().get(Artifact.KEY_START_ORDER) + "'");
-                }
-                container.add(a);
-            }
-        }
-    }
-
-    private void readArtifacts(final String section,
-            final String artifactType,
-            final List<Artifact> artifacts,
-            final JsonValue listObj,
-            final Configurations container)
-    throws IOException {
-        for(final JsonValue entry : checkTypeArray(section, listObj)) {
-            final Artifact artifact;
-            checkTypeObjectOrString(artifactType, entry);
-            if ( entry.getValueType() == ValueType.STRING ) {
-                // skip comments
-                if ( ((JsonString)entry).getString().startsWith("#") ) {
-                    continue;
-                }
-                artifact = new Artifact(checkTypeArtifactId(artifactType, entry));
-            } else {
-                final JsonObject bundleObj = (JsonObject) entry;
-                if ( !bundleObj.containsKey(JSONConstants.ARTIFACT_ID) ) {
-                    throw new IOException(exceptionPrefix.concat(" ").concat(artifactType).concat(" is missing required artifact id"));
-                }
-                final ArtifactId id = checkTypeArtifactId(artifactType.concat(" ").concat(JSONConstants.ARTIFACT_ID), bundleObj.get(JSONConstants.ARTIFACT_ID));
-
-                artifact = new Artifact(id);
-                for(final Map.Entry<String, JsonValue> metadataEntry : bundleObj.entrySet()) {
-                    final String key = metadataEntry.getKey();
-                    // skip comments
-                    if ( key.startsWith("#") ) {
-                        continue;
-                    }
-                    if ( JSONConstants.ARTIFACT_KNOWN_PROPERTIES.contains(key) ) {
-                        continue;
-                    }
-                    final String mval = checkScalarType(artifactType.concat(" metadata ").concat(key), metadataEntry.getValue(), false);
-                    artifact.getMetadata().put(key, mval);
-                }
-                if ( bundleObj.containsKey(JSONConstants.FEATURE_CONFIGURATIONS) ) {
-                    final JsonObject cfgs = checkTypeObject(artifactType.concat(" configurations"), bundleObj.get(JSONConstants.FEATURE_CONFIGURATIONS));
-                    addConfigurations(cfgs, artifact, container);
-                }
-            }
-            artifacts.add(artifact);
-        }
-    }
-
-    private void addConfigurations(final JsonObject json,
-            final Artifact artifact,
-            final Configurations container) throws IOException {
-        final ConfigurationReader reader = org.apache.felix.cm.json.Configurations.buildReader()
-                .verifyAsBundleResource(true)
-                .withIdentifier(this.location)
-                .build(json);
-        final ConfigurationResource rsrc = reader.readConfigurationResource();
-        if ( !reader.getIgnoredErrors().isEmpty() ) {
-            final StringBuilder builder = new StringBuilder(exceptionPrefix);
-            builder.append("Errors in configurations:");
-            for(final String w : reader.getIgnoredErrors()) {
-                builder.append("\n");
-                builder.append(w);
-            }
-            throw new IOException(builder.toString());
-        }
-
-        for(final Map.Entry<String, Hashtable<String, Object>> c : rsrc.getConfigurations().entrySet()) {
-            final Configuration config = new Configuration(c.getKey());
-
-            for(final Map.Entry<String, Object> prop : c.getValue().entrySet()) {
-                config.getProperties().put(prop.getKey(), prop.getValue());
-            }
-            if ( config.getProperties().get(Configuration.PROP_ARTIFACT_ID) != null ) {
-                throw new IOException(exceptionPrefix.concat("Configuration must not define property ").concat(Configuration.PROP_ARTIFACT_ID));
-            }
-            if ( artifact != null ) {
-                config.getProperties().put(Configuration.PROP_ARTIFACT_ID, artifact.getId().toMvnId());
-            }
-            for(final Configuration current : container) {
-                if ( current.equals(config) ) {
-                    throw new IOException(exceptionPrefix.concat("Duplicate configuration ").concat(config.getPid()));
-                }
-            }
-            container.add(config);
-        }
-    }
-
-
-    private void readConfigurations(final JsonObject json,
-            final Configurations container) throws IOException {
-        if ( json.containsKey(JSONConstants.FEATURE_CONFIGURATIONS) ) {
-            final JsonObject cfgs = checkTypeObject(JSONConstants.FEATURE_CONFIGURATIONS, json.get(JSONConstants.FEATURE_CONFIGURATIONS));
-            addConfigurations(cfgs, null, container);
-        }
-    }
-
-    private void readFrameworkProperties(final JsonObject json,
-            final Map<String,String> container) throws IOException {
-        if ( json.containsKey(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES) ) {
-            final JsonValue propsObj= json.get(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES);
-
-            for(final Map.Entry<String, JsonValue> entry : checkTypeObject(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES, propsObj).entrySet()) {
-                // skip comments
-                if ( entry.getKey().startsWith("#") ) {
-                    continue;
-                }
-                if ( container.get(entry.getKey()) != null ) {
-                    throw new IOException(this.exceptionPrefix.concat("Duplicate framework property ").concat(entry.getKey()));
-                }
-                final String value = checkScalarType("framework property value", entry.getValue(), false);
-                container.put(entry.getKey(), value);
-            }
-
-        }
-    }
-
-    private void readExtensions(final JsonObject json,
-            final List<String> keywords,
-            final Extensions container,
-            final Configurations configContainer) throws IOException {
-        final Set<String> keySet = new HashSet<>(json.keySet());
-        keySet.removeAll(keywords);
-        // the remaining keys are considered extensions!
-        for(final String key : keySet) {
-            if ( key.startsWith("#") ) {
-                // skip comments
-                continue;
-            }
-            final int pos = key.indexOf(':');
-            final String postfix = pos == -1 ? null : key.substring(pos + 1);
-            final int sep = (postfix == null ? key.indexOf('|') : postfix.indexOf('|'));
-            final String name;
-            final String type;
-            final String state;
-            if ( pos == -1 ) {
-                type = ExtensionType.ARTIFACTS.name();
-                if ( sep == -1 ) {
-                    name = key;
-                    state = ExtensionState.OPTIONAL.name();
-                } else {
-                    name = key.substring(0, sep);
-                    state = key.substring(sep + 1);
-                }
-            } else {
-                name = key.substring(0, pos);
-                if ( sep == -1 ) {
-                    type = postfix;
-                    state = ExtensionState.OPTIONAL.name();
-                } else {
-                    type = postfix.substring(0, sep);
-                    state = postfix.substring(sep + 1);
-                }
-            }
-            if ( JSONConstants.FEATURE_KNOWN_PROPERTIES.contains(name) ) {
-                throw new IOException(this.exceptionPrefix.concat("Extension is using reserved name : ").concat(name));
-            }
-            if ( container.getByName(name) != null ) {
-                throw new IOException(exceptionPrefix.concat("Duplicate extension with name ").concat(name));
-            }
-
-            final ExtensionType extType = ExtensionType.valueOf(type);
-            final ExtensionState extState;
-            if (ExtensionState.OPTIONAL.name().equalsIgnoreCase(state)) {
-                extState = ExtensionState.OPTIONAL;
-            } else if (ExtensionState.REQUIRED.name().equalsIgnoreCase(state)) {
-                extState = ExtensionState.REQUIRED;
-            } else if (ExtensionState.TRANSIENT.name().equalsIgnoreCase(state)) {
-                extState = ExtensionState.TRANSIENT;
-            } else {
-                final boolean opt = Boolean.valueOf(state).booleanValue();
-                extState = opt ? ExtensionState.REQUIRED : ExtensionState.OPTIONAL;
-            }
-
-            final Extension ext = new Extension(extType, name, extState);
-            final JsonValue value = json.get(key);
-            switch ( extType ) {
-                case ARTIFACTS : final List<Artifact> list = new ArrayList<>();
-                                 readArtifacts("Extension ".concat(name), "artifact", list, value, configContainer);
-                                 for(final Artifact a : list) {
-                                     if ( ext.getArtifacts().contains(a) ) {
-                                         throw new IOException(exceptionPrefix.concat("Duplicate artifact in extension ").concat(name).concat(" : ").concat(a.getId().toMvnId()));
-                                     }
-                                     ext.getArtifacts().add(a);
-                                 }
-                                 break;
-                case JSON : if ( value.getValueType() != ValueType.ARRAY && value.getValueType() != ValueType.OBJECT ) {
-                                throw new IOException(this.exceptionPrefix.concat("JSON Extension ").concat(name).concat(" is neither an object nor an array : ").concat(value.getValueType().name()));
-                            }
-                            ext.setJSONStructure((JsonStructure)value);
-                            break;
-                case TEXT : if ( value.getValueType() != ValueType.ARRAY && value.getValueType() != ValueType.STRING ) {
-                                throw new IOException(this.exceptionPrefix.concat("Text Extension ").concat(name).concat(" is neither a string nor an array : ").concat(value.getValueType().name()));
-                            }
-                            if ( value.getValueType() == ValueType.STRING ) {
-                                // string
-                                ext.setText(((JsonString)value).getString());
-                            } else {
-                                // list (array of strings)
-                                final StringBuilder sb = new StringBuilder();
-                                for(final JsonValue o : value.asJsonArray()) {
-                                    final String textValue = checkTypeString("Text Extension ".concat(name).concat(", value ").concat(o.toString()), o);
-                                    sb.append(textValue);
-                                    sb.append('\n');
-                                }
-                                ext.setText(sb.toString());
-                            }
-                            break;
-            }
-
-            container.add(ext);
-        }
-    }
-
-    /**
-     * Check if the value is a scalar type
-     * @param key A key for the error message
-     * @param value The value to check
-     * @param allowNull Whether null is allowed as value
-     * @return A string representing the value or {@code null}
-     * @throws IOException If the value is not of the specified types
-     */
-    private String checkScalarType(final String key, final JsonValue value, boolean allowNull) throws IOException {
-        if ( allowNull && value.getValueType() == ValueType.NULL ) {
-            return null;
-        }
-        if ( value.getValueType() == ValueType.STRING || value.getValueType() == ValueType.NUMBER || value.getValueType() == ValueType.FALSE || value.getValueType() == ValueType.TRUE ) {
-            return org.apache.felix.cm.json.Configurations.convertToObject(value).toString();
-        }
-        throw new IOException(this.exceptionPrefix.concat("Key ").concat(key).concat(" is not one of the allowed types string, number or boolean : ").concat(value.getValueType().name()));
-    }
-
-    /**
-     * Check if the value is an object or a string
-     * @param key A key for the error message
-     * @param value The value to check
-     * @throws IOException If the value is not of the specified types
-     */
-    private void checkTypeObjectOrString(final String key, final JsonValue value) throws IOException {
-        if ( value.getValueType() != ValueType.STRING && value.getValueType() != ValueType.OBJECT ) {
-            throw new IOException(this.exceptionPrefix.concat("Key ").concat(key).concat(" is neither a string nor an object : ").concat(value.getValueType().name()));
-        }
-    }
-
-    /**
-     * Check if the value is a boolean
-     * @param key A key for the error message
-     * @param value The value to check
-     * @return The boolean value
-     * @throws IOException If the value is not of the specified types
-     */
-    private boolean checkTypeBoolean(final String key, final JsonValue value) throws IOException {
-        if ( value.getValueType() == ValueType.TRUE || value.getValueType() == ValueType.FALSE ) {
-            return (Boolean)org.apache.felix.cm.json.Configurations.convertToObject(value);
-        }
-        throw new IOException(this.exceptionPrefix.concat("Key ").concat(key).concat(" is not of type boolean : ").concat(value.getValueType().name()));
-    }
-
-    /**
-     * Check if the value is an artifact id
-     * @param key A key for the error message
-     * @param value The value to check
-     * @return The artifact id
-     * @throws IOException If the value is not a string and not a valid artifact id
-     */
-    private ArtifactId checkTypeArtifactId(final String key, final JsonValue value) throws IOException {
-        final String textValue = checkTypeString(key, value);
-        try {
-            return ArtifactId.parse(textValue);
-        } catch ( final IllegalArgumentException iae) {
-            throw new IOException(this.exceptionPrefix.concat("Key ").concat(key).concat(" is not a valid artifact id : ").concat(textValue));
-        }
-    }
-
-    /**
-     * Check if the value is a string
-     * @param key A key for the error message
-     * @param value The value to check
-     * @return The string value
-     * @throws IOException If the value is not a string
-     */
-    private String checkTypeString(final String key, final JsonValue value) throws IOException {
-        if ( value.getValueType() == ValueType.STRING) {
-            return ((JsonString)value).getString();
-        }
-        throw new IOException(this.exceptionPrefix.concat("Key ").concat(key).concat(" is not of type string : ").concat(value.getValueType().name()));
-    }
-
-    /**
-     * Check if the value is an object
-     * @param key A key for the error message
-     * @param value The value to check
-     * @return The object
-     * @throws IOException If the value is not an object
-     */
-    private JsonObject checkTypeObject(final String key, final JsonValue value) throws IOException {
-        if ( value.getValueType() == ValueType.OBJECT) {
-            return value.asJsonObject();
-        }
-        throw new IOException(this.exceptionPrefix.concat("Key ").concat(key).concat(" is not of type object : ").concat(value.getValueType().name()));
-    }
-
-    /**
-     * Check if the value is an array
-     * @param key A key for the error message
-     * @param value The value to check
-     * @return The array
-     * @throws IOException If the value is not of the specified types
-     */
-    private JsonArray checkTypeArray(final String key, final JsonValue value) throws IOException {
-        if ( value.getValueType() == ValueType.ARRAY) {
-            return value.asJsonArray();
-        }
-        throw new IOException(this.exceptionPrefix.concat("Key ").concat(key).concat(" is not of type array : ").concat(value.getValueType().name()));
-    }
-
-    private Prototype readPrototype(final JsonObject json) throws IOException {
-        if ( json.containsKey(JSONConstants.FEATURE_PROTOTYPE)) {
-            final JsonValue prototypeObj = json.get(JSONConstants.FEATURE_PROTOTYPE);
-            checkTypeObjectOrString(JSONConstants.FEATURE_PROTOTYPE, prototypeObj);
-
-            final Prototype prototype;
-            if ( prototypeObj.getValueType() == ValueType.STRING ) {
-                prototype = new Prototype(checkTypeArtifactId(JSONConstants.FEATURE_PROTOTYPE, prototypeObj));
-            } else {
-                final JsonObject obj = (JsonObject) prototypeObj;
-                if ( !obj.containsKey(JSONConstants.ARTIFACT_ID) ) {
-                    throw new IOException(exceptionPrefix.concat(" prototype is missing required artifact id"));
-                }
-                prototype = new Prototype(checkTypeArtifactId("Prototype ".concat(JSONConstants.ARTIFACT_ID), obj.get(JSONConstants.ARTIFACT_ID)));
-
-                if ( obj.containsKey(JSONConstants.PROTOTYPE_REMOVALS) ) {
-                    final JsonObject removalObj = checkTypeObject("Prototype removals", obj.get(JSONConstants.PROTOTYPE_REMOVALS));
-                    if ( removalObj.containsKey(JSONConstants.FEATURE_BUNDLES) ) {
-                        for(final JsonValue val : checkTypeArray("Prototype removal bundles", removalObj.get(JSONConstants.FEATURE_BUNDLES))) {
-                            if ( checkTypeString("Prototype removal bundles", val).startsWith("#")) {
-                                continue;
-                            }
-                            prototype.getBundleRemovals().add(checkTypeArtifactId("Prototype removal bundles", val));
-                        }
-                    }
-                    if ( removalObj.containsKey(JSONConstants.FEATURE_CONFIGURATIONS) ) {
-                        for(final JsonValue val : checkTypeArray("Prototype removal configuration", removalObj.get(JSONConstants.FEATURE_CONFIGURATIONS))) {
-                            final String propVal = checkTypeString("Prototype removal configuration", val);
-                            if ( propVal.startsWith("#") ) {
-                                continue;
-                            }
-                            prototype.getConfigurationRemovals().add(propVal);
-                        }
-                    }
-                    if ( removalObj.containsKey(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES) ) {
-                        for(final JsonValue val : checkTypeArray("Prototype removal framework properties", removalObj.get(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES))) {
-                            final String propVal = checkTypeString("Prototype removal framework properties", val);
-                            if ( propVal.startsWith("#") ) {
-                                continue;
-                            }
-                            prototype.getFrameworkPropertiesRemovals().add(propVal);
-                        }
-                    }
-                    if ( removalObj.containsKey(JSONConstants.PROTOTYPE_EXTENSION_REMOVALS) ) {
-                        for(final JsonValue val : checkTypeArray("Prototype removal extensions", removalObj.get(JSONConstants.PROTOTYPE_EXTENSION_REMOVALS))) {
-                            checkTypeObjectOrString("Prototype removal extension", val);
-                            if ( val.getValueType() == ValueType.STRING ) {
-                                final String propVal = org.apache.felix.cm.json.Configurations.convertToObject(val).toString();
-                                if ( propVal.startsWith("#")) {
-                                    continue;
-                                }
-                                prototype.getExtensionRemovals().add(propVal);
-                            } else {
-                                final JsonObject removalMap = (JsonObject)val;
-                                final JsonValue nameObj = removalMap.get("name");
-                                final String name = checkTypeString("Prototype removal extension", nameObj);
-                                if ( removalMap.containsKey("artifacts") ) {
-                                    final List<ArtifactId> ids = new ArrayList<>();
-                                    for(final JsonValue aid : checkTypeArray("Prototype removal extension artifacts", removalMap.get("artifacts"))) {
-                                        if ( checkTypeString("Prototype removal extension artifact", aid).startsWith("#")) {
-                                            continue;
-                                        }
-                                        ids.add(checkTypeArtifactId("Prototype removal extension artifact", aid));
-                                    }
-                                    prototype.getArtifactExtensionRemovals().put(name, ids);
-                                } else {
-                                    prototype.getExtensionRemovals().add(name);
-                                }
-                            }
-                        }
-                    }
-                    readRequirements(removalObj, prototype.getRequirementRemovals());
-                    readCapabilities(removalObj, prototype.getCapabilityRemovals());
-
-                }
-            }
-            return prototype;
-        }
-        return null;
-    }
-
-    private void readRequirements(final JsonObject json, final List<MatchingRequirement> container)
-            throws IOException {
-        if ( json.containsKey(JSONConstants.FEATURE_REQUIREMENTS)) {
-            for(final JsonValue req : checkTypeArray(JSONConstants.FEATURE_REQUIREMENTS, json.get(JSONConstants.FEATURE_REQUIREMENTS))) {
-                final JsonObject obj = checkTypeObject("Requirement", req);
-
-                if ( !obj.containsKey(JSONConstants.REQCAP_NAMESPACE) ) {
-                    throw new IOException(this.exceptionPrefix.concat("Namespace is missing for requirement"));
-                }
-                final String namespace = checkTypeString("Requirement namespace", obj.get(JSONConstants.REQCAP_NAMESPACE));
-
-                Map<String, Object> attrMap = new HashMap<>();
-                if ( obj.containsKey(JSONConstants.REQCAP_ATTRIBUTES) ) {
-                    final JsonObject attrs = checkTypeObject("Requirement attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES));
-                    attrs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalAttribute(key, value, attrMap::put)));
-                }
-
-                Map<String, String> dirMap = new HashMap<>();
-                if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
-                    final JsonObject dirs = checkTypeObject("Requirement directives", obj.get(JSONConstants.REQCAP_DIRECTIVES));
-                    dirs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalDirective(key, value, dirMap::put)));
-                }
-
-                final MatchingRequirement r = new MatchingRequirementImpl(null,
-                        namespace, dirMap, attrMap);
-                container.add(r);
-            }
-        }
-    }
-
-    private void readCapabilities(final JsonObject json, final List<Capability> container) throws IOException {
-        if ( json.containsKey(JSONConstants.FEATURE_CAPABILITIES)) {
-            for(final JsonValue cap : checkTypeArray(JSONConstants.FEATURE_REQUIREMENTS, json.get(JSONConstants.FEATURE_CAPABILITIES))) {
-                final JsonObject obj = checkTypeObject("Capability", cap);
-
-                if ( !obj.containsKey(JSONConstants.REQCAP_NAMESPACE) ) {
-                    throw new IOException(this.exceptionPrefix.concat("Namespace is missing for capability"));
-                }
-                final String namespace = checkTypeString("Capability namespace", obj.get(JSONConstants.REQCAP_NAMESPACE));
-
-                Map<String, Object> attrMap = new HashMap<>();
-                if ( obj.containsKey(JSONConstants.REQCAP_ATTRIBUTES) ) {
-                    final JsonObject attrs = checkTypeObject("Capability attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES));
-                    attrs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalAttribute(key, value, attrMap::put)));
-                }
-
-                Map<String, String> dirMap = new HashMap<>();
-                if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
-                    final JsonObject dirs = checkTypeObject("Capability directives", obj.get(JSONConstants.REQCAP_DIRECTIVES));
-                    dirs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalDirective(key, value, dirMap::put)));
-                }
-
-                final Capability c = new CapabilityImpl(null, namespace, dirMap, attrMap);
-                container.add(c);
-            }
-        }
-    }
-
-    @FunctionalInterface
-    private interface BiConsumer_WithExceptions<T, V, E extends Exception> {
-        void accept(T t, V u) throws E;
-    }
-
-    private static <T, V, E extends Exception> BiConsumer<T, V> rethrowBiConsumer(BiConsumer_WithExceptions<T, V, E> biConsumer) {
-        return (t, u) -> {
-            try {
-                biConsumer.accept(t, u);
-            } catch (Exception exception) {
-                throwAsUnchecked(exception);
-            }
-        };
-    }
-
-    @SuppressWarnings ("unchecked")
-    private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
-        throw (E) exception;
-    }
-
-    private static class MatchingRequirementImpl extends RequirementImpl implements MatchingRequirement {
-
-        public MatchingRequirementImpl(Resource res, String ns, Map<String, String> dirs, Map<String, Object> attrs) {
-            super(res, ns, dirs, attrs);
-        }
-    }
-
-    /**
-     * 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 {
-        final JsonObject json = Json.createReader(org.apache.felix.cm.json.Configurations.jsonCommentAwareReader(reader)).readObject();
-
-        checkModelVersion(json);
-
-        final ArtifactId featureId = this.getFeatureId(json);
-        this.feature = new Feature(featureId);
-        this.feature.setLocation(this.location);
-
-        // final flag
-        if (json.containsKey(JSONConstants.FEATURE_FINAL)) {
-            this.feature.setFinal(checkTypeBoolean(JSONConstants.FEATURE_FINAL, json.get(JSONConstants.FEATURE_FINAL)));
-        }
-
-        // complete flag
-        if (json.containsKey(JSONConstants.FEATURE_COMPLETE)) {
-            this.feature.setComplete(checkTypeBoolean(JSONConstants.FEATURE_COMPLETE, json.get(JSONConstants.FEATURE_COMPLETE)));
-        }
-
-        // title, description, vendor and license
-        this.feature.setTitle(getProperty(json, JSONConstants.FEATURE_TITLE));
-        this.feature.setDescription(getProperty(json, JSONConstants.FEATURE_DESCRIPTION));
-        this.feature.setVendor(getProperty(json, JSONConstants.FEATURE_VENDOR));
-        this.feature.setLicense(getProperty(json, JSONConstants.FEATURE_LICENSE));
-
-        this.readVariables(json, feature.getVariables());
-        this.readBundles(json, feature.getBundles(), feature.getConfigurations());
-        this.readFrameworkProperties(json, feature.getFrameworkProperties());
-        this.readConfigurations(json, feature.getConfigurations());
-
-        this.readCapabilities(json, feature.getCapabilities());
-        this.readRequirements(json, feature.getRequirements());
-        this.feature.setPrototype(this.readPrototype(json));
-
-        this.readExtensions(json,
-                JSONConstants.FEATURE_KNOWN_PROPERTIES,
-                this.feature.getExtensions(), this.feature.getConfigurations());
-
-        return this.feature;
-    }
-
-    private void checkModelVersion(final JsonObject json) throws IOException {
-        String modelVersion = getProperty(json, JSONConstants.FEATURE_MODEL_VERSION);
-        if (modelVersion == null) {
-            modelVersion = "1";
-        }
-        if (!"1".equals(modelVersion)) {
-            throw new IOException(this.exceptionPrefix.concat("Unsupported model version: ").concat(modelVersion));
-        }
-    }
-}
-
-
diff --git a/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java b/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java
deleted file mode 100644
index cf33b13..0000000
--- a/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * 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 java.io.FilterWriter;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
-import javax.json.Json;
-import javax.json.stream.JsonGenerator;
-import javax.json.stream.JsonGeneratorFactory;
-
-import org.apache.felix.cm.json.ConfigurationResource;
-import org.apache.felix.cm.json.ConfigurationWriter;
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Bundles;
-import org.apache.sling.feature.Configuration;
-import org.apache.sling.feature.Configurations;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.ExtensionType;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.MatchingRequirement;
-import org.apache.sling.feature.Prototype;
-import org.osgi.resource.Capability;
-import org.osgi.resource.Requirement;
-
-/**
- * This class offers a static method to write a feature using a writer.
- */
-public class FeatureJSONWriter {
-
-    /**
-     * Writes the feature to the writer.
-     * The writer is not closed.
-     * @param writer Writer
-     * @param feature Feature
-     * @throws IOException If writing fails
-     */
-    public static void write(final Writer writer, final Feature feature)
-    throws IOException {
-        final FeatureJSONWriter w = new FeatureJSONWriter();
-        w.writeFeature(writer, feature);
-    }
-
-    private final JsonGeneratorFactory generatorFactory = Json.createGeneratorFactory(Collections.singletonMap(JsonGenerator.PRETTY_PRINTING, true));
-
-    private final JsonGenerator newGenerator(final Writer writer) {
-        // prevent closing of the underlying writer
-        return generatorFactory.createGenerator(new FilterWriter(writer) {
-
-            @Override
-            public void close() throws IOException {
-                super.flush();
-            }
-        });
-    }
-
-    private void writeBundles(final JsonGenerator generator,
-            final Bundles bundles,
-            final Configurations allConfigs) {
-        // bundles
-        if ( !bundles.isEmpty() ) {
-            generator.writeStartArray(JSONConstants.FEATURE_BUNDLES);
-
-            for(final Artifact artifact : bundles) {
-                final Configurations cfgs = new Configurations();
-                for(final Configuration cfg : allConfigs) {
-                    final String artifactProp = (String)cfg.getProperties().get(Configuration.PROP_ARTIFACT_ID);
-                    if ( artifact.getId().toMvnId().equals(artifactProp) ) {
-                        cfgs.add(cfg);
-                    }
-                }
-                Map<String,String> md = artifact.getMetadata();
-                if ( md.isEmpty() && cfgs.isEmpty() ) {
-                    generator.write(artifact.getId().toMvnId());
-                } else {
-                    generator.writeStartObject();
-                    generator.write(JSONConstants.ARTIFACT_ID, artifact.getId().toMvnId());
-
-                    Object runmodes = md.remove("runmodes");
-                    if (runmodes instanceof String) {
-                        md.put("run-modes", (String) runmodes);
-                    }
-
-                    for(final Map.Entry<String, String> me : md.entrySet()) {
-                        generator.write(me.getKey(), me.getValue());
-                    }
-
-                    generator.writeEnd();
-                }
-            }
-
-            generator.writeEnd();
-        }
-    }
-
-    /**
-     * Write the list of configurations into a "configurations" element
-     * @param generator The json generator
-     * @param cfgs The list of configurations
-     * @throws IOException
-     */
-    private void writeConfigurations(final JsonGenerator generator, final Configurations cfgs) throws IOException {
-        if ( cfgs.isEmpty() ) {
-            return;
-        }
-
-        generator.writeKey(JSONConstants.FEATURE_CONFIGURATIONS);
-
-        final ConfigurationWriter cfgWriter = org.apache.felix.cm.json.Configurations
-                .buildWriter()
-                .build(generator);
-
-        final ConfigurationResource rsrc = new ConfigurationResource();
-        for(final Configuration cfg : cfgs) {
-            final Hashtable<String, Object> properties;
-            if ( cfg.getProperties() instanceof Hashtable && cfg.getProperties().get(Configuration.PROP_ARTIFACT_ID) == null ) {
-                properties = (Hashtable<String, Object>)cfg.getProperties();
-            } else {
-                properties = org.apache.felix.cm.json.Configurations.newConfiguration();
-                for(final String name : Collections.list(cfg.getProperties().keys()) ) {
-                    if ( !Configuration.PROP_ARTIFACT_ID.equals(name) ) {
-                        properties.put(name, cfg.getProperties().get(name));
-                    }
-                }
-            }
-            rsrc.getConfigurations().put(cfg.getPid(), properties);
-        }
-        cfgWriter.writeConfigurationResource(rsrc);
-    }
-
-    private void writeVariables(final JsonGenerator generator, final Map<String,String> vars) {
-        if ( !vars.isEmpty()) {
-            generator.writeStartObject(JSONConstants.FEATURE_VARIABLES);
-
-            for (final Map.Entry<String, String> entry : vars.entrySet()) {
-                String val = entry.getValue();
-                if (val != null)
-                    generator.write(entry.getKey(), val);
-                else
-                    generator.writeNull(entry.getKey());
-            }
-
-            generator.writeEnd();
-        }
-    }
-
-    private void writeFrameworkProperties(final JsonGenerator generator, final Map<String,String> props) {
-        // framework properties
-        if ( !props.isEmpty() ) {
-            generator.writeStartObject(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES);
-            for(final Map.Entry<String, String> entry : props.entrySet()) {
-                generator.write(entry.getKey(), entry.getValue());
-            }
-            generator.writeEnd();
-        }
-    }
-
-    private void writeExtensions(final JsonGenerator generator,
-            final List<Extension> extensions,
-            final Configurations allConfigs) throws IOException {
-        for(final Extension ext : extensions) {
-            final String state;
-            switch (ext.getState()) {
-            case OPTIONAL:
-                state = "false";
-                break;
-            case REQUIRED:
-                state = "true";
-                break;
-            default:
-                state = ext.getState().name();
-            }
-            final String key = ext.getName().concat(":").concat(ext.getType().name()).concat("|").concat(state);
-            if ( ext.getType() == ExtensionType.JSON ) {
-                generator.write(key, ext.getJSONStructure());
-            } else if ( ext.getType() == ExtensionType.TEXT ) {
-                generator.writeStartArray(key);
-                for(String line : ext.getText().split("\n")) {
-                    generator.write(line);
-                }
-                generator.writeEnd();
-            } else {
-                generator.writeStartArray(key);
-                for(final Artifact artifact : ext.getArtifacts()) {
-                    final Configurations artifactCfgs = new Configurations();
-                    for(final Configuration cfg : allConfigs) {
-                        final String artifactProp = (String)cfg.getProperties().get(Configuration.PROP_ARTIFACT_ID);
-                        if (  artifact.getId().toMvnId().equals(artifactProp) ) {
-                            artifactCfgs.add(cfg);
-                        }
-                    }
-                    if ( artifact.getMetadata().isEmpty() && artifactCfgs.isEmpty() ) {
-                        generator.write(artifact.getId().toMvnId());
-                    } else {
-                        generator.writeStartObject();
-                        generator.write(JSONConstants.ARTIFACT_ID, artifact.getId().toMvnId());
-
-                        for(final Map.Entry<String, String> me : artifact.getMetadata().entrySet()) {
-                            generator.write(me.getKey(), me.getValue());
-                        }
-
-                        writeConfigurations(generator, artifactCfgs);
-
-                        generator.writeEnd();
-                    }
-                }
-                generator.writeEnd();
-            }
-        }
-    }
-
-    private void writeProperty(final JsonGenerator generator, final String key, final String value) {
-        if ( value != null ) {
-            generator.write(key, value);
-        }
-    }
-
-    private <T> void writeList(final JsonGenerator generator, final String name, final Collection<T> values) {
-        if (!values.isEmpty()) {
-            generator.writeStartArray(name);
-            for (T value : values) {
-                generator.write(value.toString());
-            }
-            generator.writeEnd();
-        }
-    }
-
-    private void writePrototype(final JsonGenerator generator, final Prototype inc) {
-        if (inc == null) {
-            return;
-        }
-
-        if ( inc.getArtifactExtensionRemovals().isEmpty()
-             && inc.getBundleRemovals().isEmpty()
-             && inc.getConfigurationRemovals().isEmpty()
-             && inc.getFrameworkPropertiesRemovals().isEmpty()
-             && inc.getRequirementRemovals().isEmpty()
-             && inc.getCapabilityRemovals().isEmpty() ) {
-
-            generator.write(JSONConstants.FEATURE_PROTOTYPE, inc.getId().toMvnId());
-        } else {
-            generator.writeStartObject(JSONConstants.FEATURE_PROTOTYPE);
-            writeProperty(generator, JSONConstants.ARTIFACT_ID, inc.getId().toMvnId());
-
-            generator.writeStartObject(JSONConstants.PROTOTYPE_REMOVALS);
-
-            if ( !inc.getArtifactExtensionRemovals().isEmpty()
-                 || inc.getExtensionRemovals().isEmpty() ) {
-                generator.writeStartArray(JSONConstants.PROTOTYPE_EXTENSION_REMOVALS);
-
-                for(final String id : inc.getExtensionRemovals()) {
-                    generator.write(id);
-                }
-                for(final Map.Entry<String, List<ArtifactId>> entry : inc.getArtifactExtensionRemovals().entrySet()) {
-                    generator.writeStartObject();
-
-                    writeList(generator, entry.getKey(), entry.getValue());
-
-                    generator.writeEnd();
-                }
-
-                generator.writeEnd();
-            }
-            writeList(generator, JSONConstants.FEATURE_CONFIGURATIONS, inc.getConfigurationRemovals());
-            writeList(generator, JSONConstants.FEATURE_BUNDLES, inc.getBundleRemovals());
-            writeList(generator, JSONConstants.FEATURE_FRAMEWORK_PROPERTIES, inc.getFrameworkPropertiesRemovals());
-
-            writeRequirements(generator, inc.getRequirementRemovals());
-            writeCapabilities(generator, inc.getCapabilityRemovals());
-
-            generator.writeEnd().writeEnd();
-        }
-    }
-
-    private void writeRequirements(final JsonGenerator generator, final List<MatchingRequirement> requirements) {
-        if (requirements.isEmpty()) {
-            return;
-        }
-
-        generator.writeStartArray(JSONConstants.FEATURE_REQUIREMENTS);
-
-        for(final Requirement req : requirements) {
-            generator.writeStartObject();
-            writeProperty(generator, JSONConstants.REQCAP_NAMESPACE, req.getNamespace());
-            if ( !req.getAttributes().isEmpty() ) {
-                generator.writeStartObject(JSONConstants.REQCAP_ATTRIBUTES);
-                req.getAttributes().forEach((key, value) -> ManifestUtils.marshalAttribute(key, value, generator::write));
-                generator.writeEnd();
-            }
-            if ( !req.getDirectives().isEmpty() ) {
-                generator.writeStartObject(JSONConstants.REQCAP_DIRECTIVES);
-                req.getDirectives().forEach((key, value) -> ManifestUtils.marshalDirective(key, value, generator::write));
-                generator.writeEnd();
-            }
-            generator.writeEnd();
-        }
-
-        generator.writeEnd();
-    }
-
-    private void writeCapabilities(final JsonGenerator generator, final List<Capability> capabilities) {
-        if (capabilities.isEmpty()) {
-            return;
-        }
-
-        generator.writeStartArray(JSONConstants.FEATURE_CAPABILITIES);
-
-        for(final Capability cap : capabilities) {
-            generator.writeStartObject();
-            writeProperty(generator, JSONConstants.REQCAP_NAMESPACE, cap.getNamespace());
-            if ( !cap.getAttributes().isEmpty() ) {
-                generator.writeStartObject(JSONConstants.REQCAP_ATTRIBUTES);
-                cap.getAttributes().forEach((key, value) -> ManifestUtils.marshalAttribute(key, value, generator::write));
-                generator.writeEnd();
-            }
-            if ( !cap.getDirectives().isEmpty() ) {
-                generator.writeStartObject(JSONConstants.REQCAP_DIRECTIVES);
-                cap.getDirectives().forEach((key, value) -> ManifestUtils.marshalDirective(key, value, generator::write));
-                generator.writeEnd();
-            }
-            generator.writeEnd();
-        }
-
-        generator.writeEnd();
-    }
-
-    /**
-     * Writes the feature to the writer.
-     * The writer is not closed.
-     * @param writer Writer
-     * @param feature Feature
-     * @throws IOException If writing fails
-     */
-    private void writeFeature(final Writer writer, final Feature feature)
-    throws IOException {
-        JsonGenerator generator = newGenerator(writer);
-        generator.writeStartObject();
-
-        writeFeatureId(generator, feature);
-
-        if (feature.isFinal()) {
-            generator.write(JSONConstants.FEATURE_FINAL, true);
-        }
-
-        if (feature.isComplete()) {
-            generator.write(JSONConstants.FEATURE_COMPLETE, true);
-        }
-
-        // title, description, vendor, license
-        writeProperty(generator, JSONConstants.FEATURE_TITLE, feature.getTitle());
-        writeProperty(generator, JSONConstants.FEATURE_DESCRIPTION, feature.getDescription());
-        writeProperty(generator, JSONConstants.FEATURE_VENDOR, feature.getVendor());
-        writeProperty(generator, JSONConstants.FEATURE_LICENSE, feature.getLicense());
-
-        // variables
-        writeVariables(generator, feature.getVariables());
-
-        // prototype
-        writePrototype(generator, feature.getPrototype());
-
-        // requirements
-        writeRequirements(generator, feature.getRequirements());
-
-        // capabilities
-        writeCapabilities(generator, feature.getCapabilities());
-
-        // bundles
-        writeBundles(generator, feature.getBundles(), feature.getConfigurations());
-
-        // configurations
-        final Configurations cfgs = new Configurations();
-        for(final Configuration cfg : feature.getConfigurations()) {
-            final String artifactProp = (String)cfg.getProperties().get(Configuration.PROP_ARTIFACT_ID);
-            if (  artifactProp == null ) {
-                cfgs.add(cfg);
-            }
-        }
-        writeConfigurations(generator, cfgs);
-
-        // framework properties
-        writeFrameworkProperties(generator, feature.getFrameworkProperties());
-
-        // extensions
-        writeExtensions(generator, feature.getExtensions(), feature.getConfigurations());
-
-        generator.writeEnd().close();
-    }
-
-    private void writeFeatureId(final JsonGenerator generator,
-    		final Feature feature) {
-        writeProperty(generator, JSONConstants.FEATURE_ID, feature.getId().toMvnId());
-    }
-
-}
diff --git a/src/main/java/org/apache/sling/feature/io/json/JSONConstants.java b/src/main/java/org/apache/sling/feature/io/json/JSONConstants.java
deleted file mode 100644
index 9d665fb..0000000
--- a/src/main/java/org/apache/sling/feature/io/json/JSONConstants.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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 java.util.Arrays;
-import java.util.List;
-
-import org.apache.sling.feature.Configuration;
-
-public abstract class JSONConstants {
-
-    static final String FEATURE_ID = "id";
-
-    static final String FEATURE_VARIABLES = "variables";
-
-    static final String FEATURE_BUNDLES = "bundles";
-
-    static final String FEATURE_FRAMEWORK_PROPERTIES = "framework-properties";
-
-    static final String FEATURE_CONFIGURATIONS = "configurations";
-
-    static final String FEATURE_PROTOTYPE = "prototype";
-
-    static final String FEATURE_REQUIREMENTS = "requirements";
-
-    static final String FEATURE_CAPABILITIES = "capabilities";
-
-    static final String FEATURE_TITLE = "title";
-
-    static final String FEATURE_DESCRIPTION = "description";
-
-    static final String FEATURE_VENDOR = "vendor";
-
-    static final String FEATURE_LICENSE = "license";
-
-    static final String FEATURE_FINAL = "final";
-
-    static final String FEATURE_COMPLETE = "complete";
-
-    static final String FEATURE_MODEL_VERSION = "model-version";
-
-    static final List<String> FEATURE_KNOWN_PROPERTIES = Arrays.asList(FEATURE_ID,
-            FEATURE_MODEL_VERSION,
-            FEATURE_VARIABLES,
-            FEATURE_BUNDLES,
-            FEATURE_FRAMEWORK_PROPERTIES,
-            FEATURE_CONFIGURATIONS,
-            FEATURE_PROTOTYPE,
-            FEATURE_REQUIREMENTS,
-            FEATURE_CAPABILITIES,
-            FEATURE_TITLE,
-            FEATURE_DESCRIPTION,
-            FEATURE_VENDOR,
-            FEATURE_FINAL,
-            FEATURE_COMPLETE,
-            FEATURE_LICENSE);
-
-    static final String ARTIFACT_ID = "id";
-
-    static final List<String> ARTIFACT_KNOWN_PROPERTIES = Arrays.asList(ARTIFACT_ID,
-            Configuration.PROP_ARTIFACT_ID,
-            FEATURE_CONFIGURATIONS);
-
-    static final String PROTOTYPE_REMOVALS = "removals";
-
-    static final String PROTOTYPE_EXTENSION_REMOVALS = "extensions";
-
-    static final String REQCAP_NAMESPACE = "namespace";
-    static final String REQCAP_ATTRIBUTES = "attributes";
-    static final String REQCAP_DIRECTIVES = "directives";
-}
diff --git a/src/main/java/org/apache/sling/feature/io/json/ManifestUtils.java b/src/main/java/org/apache/sling/feature/io/json/ManifestUtils.java
deleted file mode 100644
index dae75c8..0000000
--- a/src/main/java/org/apache/sling/feature/io/json/ManifestUtils.java
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * 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 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;
-
-import javax.json.JsonValue;
-
-import org.apache.felix.cm.json.Configurations;
-import org.apache.felix.utils.resource.CapabilityImpl;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.Version;
-import org.osgi.resource.Capability;
-
-// 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, JsonValue value, BiConsumer<String, Object> sink) throws IOException {
-        unmarshal(key.concat("=").concat(Configurations.convertToObject(value).toString()), Capability::getAttributes, sink);
-    }
-
-    public static void unmarshalDirective(String key, JsonValue value, BiConsumer<String, String> sink) throws IOException {
-        unmarshal(key.concat(":=").concat(Configurations.convertToObject(value).toString()), 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;".concat(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/src/main/java/org/apache/sling/feature/io/json/package-info.java b/src/main/java/org/apache/sling/feature/io/json/package-info.java
deleted file mode 100644
index 54ecf2e..0000000
--- a/src/main/java/org/apache/sling/feature/io/json/package-info.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.
- */
-
-@org.osgi.annotation.versioning.Version("1.1.0")
-package org.apache.sling.feature.io.json;
-
-
diff --git a/src/main/java/org/apache/sling/feature/io/package-info.java b/src/main/java/org/apache/sling/feature/io/package-info.java
deleted file mode 100644
index 81fdbf9..0000000
--- a/src/main/java/org/apache/sling/feature/io/package-info.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.
- */
-
-@org.osgi.annotation.versioning.Version("1.1.0")
-package org.apache.sling.feature.io;
-
-
diff --git a/src/main/resources/META-INF/feature/Feature-1.0.0.schema.json b/src/main/resources/META-INF/feature/Feature-1.0.0.schema.json
deleted file mode 100644
index 9021482..0000000
--- a/src/main/resources/META-INF/feature/Feature-1.0.0.schema.json
+++ /dev/null
@@ -1,226 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-07/schema#",
-  "$id": "http://sling.apache.org/Feature/1.0.0",
-  "type": "object",
-  "properties": {
-    "model-version": {
-      "type": "string"
-    },
-    "id": {
-      "type": "string",
-      "pattern": "^(([^: ]+):([^: ]+)(:([^: ]*)(:([^: ]+))?)?:([^: ]+)|([^/ ]+)/([^/ ]+)(/([^/ ]+))?(/([^/ ]*)(/([^/ ]+))?)?)$"
-    },
-    "final": {
-      "type": "boolean"
-    },
-    "complete": {
-      "type": "boolean"
-    },
-    "title": {
-      "type": "string"
-    },
-    "description": {
-      "type": "string"
-    },
-    "vendor": {
-      "type": "string"
-    },
-    "license": {
-      "type": "string"
-    },
-    "variables": {
-      "type": "object",
-      "patternProperties": {
-        "^(.+)$": {
-          "type": ["string","null"]
-        }
-      }
-    },
-    "bundles": {
-      "type": "array",
-      "items": {
-        "$ref": "#/definitions/Bundle"
-      }
-    },
-    "framework-properties": {
-      "type": "object",
-      "patternProperties": {
-        "^(.+)$": {
-          "type": [ "string", "number", "boolean" ]
-        }
-      }
-    },
-    "configurations": {
-      "type": "object",
-      "patternProperties": {
-        "^(.+)$": {
-          "$ref": "#/definitions/Configuration"
-        }
-      }
-    },
-    "prototype": {
-      "$ref": "#/definitions/Prototype"
-    },
-    "requirements": {
-    " type": "array",
-      "items": {
-        "$ref": "#/definitions/Requirement"
-      }
-    },
-    "capabilities": {
-    " type": "array",
-      "items": {
-        "$ref": "#/definitions/Capability"
-      }
-    }
-  },
-  "patternProperties": {
-    "^[^:]+:ARTIFACTS\\|(true|false)$": {
-      "type": "array",
-      "items": {
-        "$ref": "#/definitions/Bundle"
-      }
-    },
-    "^[^:]+:TEXT\\|(true|false)$": {
-      "type": [ "string", "array" ],
-      "items": {
-        "type": "string"
-      }
-    },
-    "^[^:]+:JSON\\|(true|false)$": {
-      "type": [
-        "object",
-        "array"
-      ]
-    }
-  },
-  "definitions": {
-    "Bundle": {
-      "$id": "#Bundle",
-      "type": [
-        "string",
-        "object"
-      ],
-      "properties": {
-        "id": {
-          "type": "string",
-          "pattern": "^(([^: ]+):([^: ]+)(:([^: ]*)(:([^: ]+))?)?:([^: ]+)|(mvn:)?([^/ ]+)/([^/ ]+)(/([^/ ]+))?(/([^/ ]*)(/([^/ ]+))?)?)$"
-        },
-        "start-level": {
-          "type": [ "string", "number" ],
-          "pattern": "^\\d+$"
-        },
-        "run-modes": {
-          "type": [
-            "string",
-            "array"
-          ],
-          "items": {
-            "type": "string"
-          }
-        },
-        "configurations": {
-          "type": "object",
-          "patternProperties": {
-            "^(.+)$": {
-              "$ref": "#/definitions/Configuration"
-            }
-          }
-        }
-      }
-    },
-    "Configuration": {
-      "$id": "#Configuration",
-      "patternProperties": {
-        "^(.+)$": {
-          "type": [
-            "string",
-            "number",
-            "boolean",
-            "array",
-            "object"
-          ]
-        }
-      }
-    },
-    "Prototype": {
-      "$id": "#Prototype",
-      "type": "object",
-      "properties": {
-        "id": {
-          "type": "string",
-          "pattern": "^(([^: ]+):([^: ]+)(:([^: ]*)(:([^: ]+))?)?:([^: ]+)|(mvn:)?([^/ ]+)/([^/ ]+)(/([^/ ]+))?(/([^/ ]*)(/([^/ ]+))?)?)$"
-        },
-        "removals": {
-           "$ref": "#/definitions/Removals"
-        }
-      }
-    },
-    "Removals": {
-      "$id": "#Removals",
-      "type": "object",
-      "properties": {
-        "configurations": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          }
-        },
-        "bundles": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          }
-        },
-        "framework-properties": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          }
-        }
-      }
-    },
-    "Requirement": {
-      "$id": "#Requirement",
-      "type": "object",
-      "properties": {
-        "namespace": {
-          "type": "string"
-        },
-        "directives": {
-          "type": "object",
-          "patternProperties": {
-            "^(.+)$": {
-              "type": "string"
-            }
-          }
-        }
-      }
-    },
-    "Capability": {
-      "$id": "#Capability",
-      "type": "object",
-      "properties": {
-        "namespace": {
-          "type": "string"
-        },
-        "directives": {
-          "type": "object",
-          "patternProperties": {
-            "^(.+)$": {
-              "type": "string"
-            }
-          }
-        },
-        "attributes": {
-          "type": "object",
-          "patternProperties": {
-            "^(.+)$": {
-              "type": [ "string", "number", "boolean" ]
-            }
-          }
-        }
-      }
-    }
-  }
-}
diff --git a/src/test/java/org/apache/sling/feature/io/ConfiguratorUtilTest.java b/src/test/java/org/apache/sling/feature/io/ConfiguratorUtilTest.java
deleted file mode 100644
index 4ad71eb..0000000
--- a/src/test/java/org/apache/sling/feature/io/ConfiguratorUtilTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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;
-
-import java.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.felix.cm.json.Configurations;
-import org.hamcrest.Description;
-import org.hamcrest.TypeSafeDiagnosingMatcher;
-import org.hamcrest.core.Every;
-import org.junit.Assert;
-import org.junit.Test;
-import org.osgi.util.converter.Converter;
-import org.osgi.util.converter.Converters;
-import org.osgi.util.converter.TypeReference;
-
-public class ConfiguratorUtilTest {
-
-    @Test
-    public void testConfigurationWriteReadRoundtrip() throws IOException {
-        Dictionary<String, Object> props = new Hashtable<>();
-        props.put("Integer-simple", 1);
-        props.put("Integer-array", new Integer[]{1,2});
-        props.put("Integer-list", Arrays.asList(1, 2));
-        props.put("int-array", new int[]{1,2});
-        props.put("Long-simple", 1);
-        props.put("Long-array", new Long[]{1L,2L});
-        props.put("Long-list", Arrays.asList(1l, 2l));
-        props.put("long-array", new long[]{1,2});
-        props.put("Boolean-simple", Boolean.TRUE);
-        props.put("Boolean-array", new Boolean[] {Boolean.TRUE, Boolean.FALSE});
-        props.put("Boolean-list", Arrays.asList(Boolean.TRUE, Boolean.FALSE));
-        props.put("bool-array", new boolean[] {true, false});
-        props.put("Float-simple", 1.0d);
-        props.put("Float-array", new Float[]{1.0f, 2.0f});
-        props.put("Float-list", Arrays.asList(1.0f, 2.0f));
-        props.put("float-array", new float[]{1.0f,2.0f});
-        props.put("Double-simple", 1.0d);
-        props.put("Double-array", new Double[]{1.0d,2.0d});
-        props.put("Double-list", Arrays.asList(1.0d, 2.0d));
-        props.put("double-array", new double[]{1.0d,2.0d});
-        props.put("Byte-simple", new Byte((byte)1));
-        props.put("Byte-array", new Byte[]{1,2});
-        props.put("Byte-list", Arrays.asList((byte)1, (byte)2));
-        props.put("byte-array", new byte[]{1,2});
-        props.put("Short-simple", new Short((short) 1));
-        props.put("Short-array", new Short[]{1,2});
-        props.put("Short-list", Arrays.asList((short)1, (short)2));
-        props.put("Short-array", new short[]{1,2});
-        props.put("Character-simple", 1);
-        props.put("Character-array", new Character[]{'a','b'});
-        props.put("Character-list", Arrays.asList('a', 'b'));
-        props.put("char-array", new char[]{'a','b'});
-        props.put("String-simple", "test");
-        props.put("String-array", new String[]{"test1", "test2"});
-        props.put("String-list", Arrays.asList("test1", "test2"));
-        StringWriter writer = new StringWriter();
-        ConfiguratorUtil.writeConfiguration(writer, props);
-        writer.close();
-        assertConfigurationJson(writer.toString(), props);
-    }
-
-    protected void assertConfigurationJson(String json, Dictionary<String, Object> expectedProps) throws IOException {
-        final Hashtable<String, Object> readProps = Configurations.buildReader().verifyAsBundleResource(true).build(new StringReader(json)).readConfiguration();
-        // convert to maps for easier comparison
-        Converter converter = Converters.standardConverter();
-        Map<String, Object> expectedPropsMap = converter.convert(expectedProps).to(new TypeReference<Map<String,Object>>(){});
-        Map<String, Object> actualPropsMap = converter.convert(readProps).to(new TypeReference<Map<String,Object>>(){});
-        Assert.assertThat(actualPropsMap.entrySet(), Every.everyItem(new MapEntryMatcher<>(expectedPropsMap)));
-    }
-
-    public static class MapEntryMatcher<K, V> extends TypeSafeDiagnosingMatcher<Map.Entry<K, V>> {
-
-        private final Map<K,V> expectedMap;
-
-        public MapEntryMatcher(Map<K, V> expectedMap) {
-            this.expectedMap = expectedMap;
-        }
-
-        @Override
-        public void describeTo(Description description) {
-            description.appendText("contained in the expected map");
-        }
-
-        @Override
-        protected boolean matchesSafely(Map.Entry<K, V> item, Description description) {
-            if (expectedMap.get(item.getKey()) == null){
-                description.appendText("key '" + item.getKey() + "' is not present");
-                return false;
-            } else {
-                boolean isEqual;
-                if (item.getValue().getClass().isArray()) {
-                    isEqual = Objects.deepEquals(expectedMap.get(item.getKey()), item.getValue());
-
-                } else {
-                    isEqual = expectedMap.get(item.getKey()).equals(item.getValue());
-                }
-                if (!isEqual) {
-                    description.appendText("has the wrong value for key '" + item.getKey() + "': Expected=" + expectedMap.get(item.getKey()) + " (" + expectedMap.get(item.getKey()).getClass() + ")" + ", Actual=" + item.getValue() + " (" + item.getValue().getClass() + ")");
-                }
-                return isEqual;
-            }
-        }
-    }
-}
diff --git a/src/test/java/org/apache/sling/feature/io/IOUtilsTest.java b/src/test/java/org/apache/sling/feature/io/IOUtilsTest.java
deleted file mode 100644
index 08920a4..0000000
--- a/src/test/java/org/apache/sling/feature/io/IOUtilsTest.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * 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;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.jar.JarOutputStream;
-
-import org.junit.Test;
-
-public class IOUtilsTest {
-
-    @Test public void testFileSort() {
-        final String[] files = new String[] {
-            "/different/path/app.json",
-            "/path/to/base.json",
-            "/path/to/feature.json",
-            "/path/to/amode/feature.json",
-            "/path/to/later/feature.json",
-            "http://sling.apache.org/features/one.json",
-            "http://sling.apache.org/features/two.json",
-            "http://sling.apache.org/features/amode/feature.json"
-        };
-
-        final List<String> l = new ArrayList<>(Arrays.asList(files));
-        Collections.sort(l, IOUtils.FEATURE_PATH_COMP);
-        for(int i=0; i<files.length; i++) {
-            assertEquals(files[i], l.get(i));
-        }
-    }
-
-    @Test public void testGetFileFromURL() throws IOException {
-        File file = File.createTempFile("IOUtilsTest", ".test");
-
-        try {
-            try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"))) {
-                writer.println("Hello");
-            }
-
-            assertEquals(file, IOUtils.getFileFromURL(file.toURI().toURL(), false, null));
-
-            URL url = new URL(null,"bla:" + file.toURI().toURL(), new URLStreamHandler() {
-                @Override
-                protected URLConnection openConnection(URL u){
-                    return new URLConnection(u) {
-                        @Override
-                        public void connect()
-                        {
-
-                        }
-
-                        @Override
-                        public InputStream getInputStream() throws IOException
-                        {
-                            return new FileInputStream(file);
-                        }
-                    };
-                }
-            });
-
-            assertNull(IOUtils.getFileFromURL(url, false, null));
-            File tmp = IOUtils.getFileFromURL(url, true, null);
-
-            assertNotEquals(file, tmp);
-            try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(tmp), "UTF-8"))) {
-                assertEquals("Hello", reader.readLine());
-            }
-        } finally {
-            file.delete();
-        }
-        File jarFile = File.createTempFile("IOUtilsTes", ".jar");
-        try {
-
-            try (JarOutputStream output = new JarOutputStream(new FileOutputStream(jarFile))) {
-                output.putNextEntry(new JarEntry("test"));
-                output.write("Hello".getBytes());
-                output.closeEntry();
-            }
-
-            assertEquals(jarFile, IOUtils.getFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/"), false, null));
-            assertNull(IOUtils.getFileFromURL(new URL("jar:file:" + jarFile.getPath() + "!/test"), false, null));
-            File tmpJar = IOUtils.getFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test"), true, null);
-            assertNotNull(tmpJar);
-            assertNotEquals(jarFile, tmpJar);
-        } finally {
-            jarFile.delete();
-        }
-    }
-
-    @Test public void testGetJarFileFromURL() throws IOException {
-        File jarFile = File.createTempFile("IOUtilsTest", ".jar");
-
-        try {
-            try (JarOutputStream output = new JarOutputStream(new FileOutputStream(jarFile))) {
-                output.putNextEntry(new JarEntry("test"));
-                output.write("Hello".getBytes());
-                output.closeEntry();
-                output.putNextEntry(new JarEntry("test.jar"));
-                try (JarOutputStream inner = new JarOutputStream(output)) {
-                    inner.putNextEntry(new JarEntry("inner"));
-                    inner.write("Hello".getBytes());
-                    inner.closeEntry();
-                }
-            }
-
-            JarFile jar = IOUtils.getJarFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/"), true, null);
-            assertNotNull(jar);
-            jar = IOUtils.getJarFileFromURL(jarFile.toURI().toURL(), true, null);
-            assertNotNull(jar);
-
-            assertNull(IOUtils.getFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test"), false, null));
-
-            JarFile tmpJar = IOUtils.getJarFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test.jar"), true, null);
-            assertNotNull(tmpJar);
-            assertNotNull(tmpJar.getEntry("inner"));
-
-            try {
-                IOUtils.getJarFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test"), true, null);
-                fail();
-            } catch (IOException ex) {
-                // Expected
-            }
-
-            try {
-                IOUtils.getJarFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test.jar"), false, null);
-                fail();
-            } catch (IOException ex) {
-                // Expected
-            }
-        } finally {
-            jarFile.delete();
-        }
-    }
-}
diff --git a/src/test/java/org/apache/sling/feature/io/archive/ArchiveWriterTest.java b/src/test/java/org/apache/sling/feature/io/archive/ArchiveWriterTest.java
deleted file mode 100644
index ee3fa71..0000000
--- a/src/test/java/org/apache/sling/feature/io/archive/ArchiveWriterTest.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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.archive;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Reader;
-import java.io.StringReader;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.io.json.FeatureJSONReader;
-import org.junit.Test;
-
-public class ArchiveWriterTest {
-
-    public static final String ARTIFACT = "/features/final.json";
-
-    @Test
-    public void testArchiveWrite() throws IOException {
-        final Feature f = new Feature(ArtifactId.parse("g:f:1"));
-        f.getBundles().add(new Artifact(ArtifactId.parse("g:a:2")));
-
-        byte[] archive = null;
-        final Set<ArtifactId> ids = new HashSet<>();
-        try (final ByteArrayOutputStream out = new ByteArrayOutputStream()) {
-            ArchiveWriter.write(out, null, id -> {
-                ids.add(id);
-                return ArchiveWriterTest.class.getResource(ARTIFACT);
-            }, f).finish();
-            out.flush();
-            archive = out.toByteArray();
-        }
-        assertEquals(1, ids.size());
-        assertTrue(ids.contains(ArtifactId.parse("g:a:2")));
-
-        // read "artifact"
-        final byte[] artifactBytes;
-        try (final InputStream is = ArchiveWriterTest.class.getResourceAsStream(ARTIFACT)) {
-            artifactBytes = readFromStream(is);
-        }
-
-        final Set<ArtifactId> readIds = new HashSet<>();
-        final Set<ArtifactId> readFeatureIds = new HashSet<>();
-
-        final List<Feature> features = new ArrayList<>();
-        try (final InputStream in = new ByteArrayInputStream(archive)) {
-            features.addAll(ArchiveReader.read(in, (id, is) -> {
-
-                // read contents
-                byte[] read = readFromStream(is);
-
-                // is feature?
-                if ( id.equals(f.getId()) ) {
-                    try ( final Reader reader = new StringReader(new String(read, StandardCharsets.UTF_8))) {
-                        final Feature readFeature = FeatureJSONReader.read(reader, id.toString());
-                        assertEquals(f.getId(), readFeature.getId());
-                        readFeatureIds.add(f.getId());
-                    }
-                } else {
-                    readIds.add(id);
-                    assertEquals(artifactBytes.length, read.length);
-                    assertArrayEquals(artifactBytes, read);
-                }
-            }));
-        }
-
-        assertEquals(1, readFeatureIds.size());
-        assertTrue(readFeatureIds.contains(f.getId()));
-
-        assertEquals(1, readIds.size());
-        assertTrue(readIds.contains(ArtifactId.parse("g:a:2")));
-
-        assertEquals(1, features.size());
-        final Feature g = features.get(0);
-        assertEquals(f.getId(), g.getId());
-    }
-
-    private byte[] readFromStream(final InputStream is) throws IOException {
-        byte[] read;
-        try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
-            byte[] buf = new byte[1024];
-            int l;
-            while ((l = is.read(buf)) > 0) {
-                baos.write(buf, 0, l);
-            }
-            baos.flush();
-            read = baos.toByteArray();
-        }
-        return read;
-    }
-}
diff --git a/src/test/java/org/apache/sling/feature/io/artifacts/ArtifactManagerTest.java b/src/test/java/org/apache/sling/feature/io/artifacts/ArtifactManagerTest.java
deleted file mode 100644
index fd106d3..0000000
--- a/src/test/java/org/apache/sling/feature/io/artifacts/ArtifactManagerTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.artifacts;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.sling.feature.io.artifacts.spi.ArtifactProvider;
-import org.junit.Test;
-
-public class ArtifactManagerTest {
-
-    private static final String METADATA = "<metadata modelVersion=\"1.1.0\">\n" +
-            "<groupId>org.apache.sling.samples</groupId>\n" +
-            "<artifactId>slingshot</artifactId>\n" +
-            "<version>0-DEFAULT-SNAPSHOT</version>\n" +
-            "<versioning>\n" +
-                "<snapshot>\n" +
-                    "<timestamp>20160321.103951</timestamp>\n" +
-                    "<buildNumber>1</buildNumber>\n" +
-                "</snapshot>\n" +
-                "<lastUpdated>20160321103951</lastUpdated>\n" +
-                "<snapshotVersions>\n" +
-                    "<snapshotVersion>\n" +
-                        "<extension>txt</extension>\n" +
-                        "<value>0-DEFAULT-20160321.103951-1</value>\n" +
-                        "<updated>20160321103951</updated>\n" +
-                    "</snapshotVersion>\n" +
-                    "<snapshotVersion>\n" +
-                        "<extension>pom</extension>\n" +
-                        "<value>0-DEFAULT-20160321.103951-1</value>\n" +
-                        "<updated>20160321103951</updated>\n" +
-                    "</snapshotVersion>\n" +
-                "</snapshotVersions>\n" +
-            "</versioning></metadata>";
-
-    @Test public void testMetadataParsing() {
-        final String version = ArtifactManager.getLatestSnapshot(METADATA);
-        assertEquals("20160321.103951-1", version);
-    }
-
-    @Test public void testSnapshotHandling() throws IOException {
-        final String REPO = "http://org.apache.sling";
-        final ArtifactManagerConfig config = mock(ArtifactManagerConfig.class);
-        when(config.getRepositoryUrls()).thenReturn(new String[] {REPO});
-
-        final URL metadataFile = new URL("file:/maven-metadata.xml");
-
-        final URL artifactFile = new URL("file:/artifact");
-
-        final ArtifactProvider provider = mock(ArtifactProvider.class);
-        when(provider.getArtifact(REPO + "/group/artifact/1.0.0-SNAPSHOT/artifact-1.0.0-SNAPSHOT.txt", "group/artifact/1.0.0-SNAPSHOT/artifact-1.0.0-SNAPSHOT.txt")).thenReturn(null);
-        when(provider.getArtifact(REPO + "/group/artifact/1.0.0-SNAPSHOT/maven-metadata.xml", "org.apache.sling/group/artifact/1.0.0-SNAPSHOT/maven-metadata.xml")).thenReturn(metadataFile);
-        when(provider.getArtifact(REPO + "/group/artifact/1.0.0-SNAPSHOT/artifact-1.0.0-20160321.103951-1.txt", "group/artifact/1.0.0-SNAPSHOT/artifact-1.0.0-SNAPSHOT.txt")).thenReturn(artifactFile);
-
-        final Map<String, ArtifactProvider> providers = new HashMap<>();
-        providers.put("*", provider);
-
-        final ArtifactManager mgr = new ArtifactManager(config, providers) {
-
-            @Override
-            protected String getFileContents(final ArtifactHandler handler) throws IOException {
-                final String path = handler.getLocalURL().getPath();
-                if ( "/maven-metadata.xml".equals(path) ) {
-                    return METADATA;
-                }
-                return super.getFileContents(handler);
-            }
-        };
-
-        final ArtifactHandler handler = mgr.getArtifactHandler("mvn:group/artifact/1.0.0-SNAPSHOT/txt");
-        assertNotNull(handler);
-        assertEquals(artifactFile, handler.getLocalURL());
-    }
-}
diff --git a/src/test/java/org/apache/sling/feature/io/json/ArtifactsExtensions.java b/src/test/java/org/apache/sling/feature/io/json/ArtifactsExtensions.java
deleted file mode 100644
index e2ecc54..0000000
--- a/src/test/java/org/apache/sling/feature/io/json/ArtifactsExtensions.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package org.apache.sling.feature.io.json;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.ExtensionState;
-import org.apache.sling.feature.Extensions;
-import org.apache.sling.feature.Feature;
-
-/*
- * 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.
- */
-public class ArtifactsExtensions {
-
-    public static void testReadArtifactsExtensions(Feature feature) {
-        Extensions extensions = feature.getExtensions();
-
-        assertEquals(2, extensions.size());
-
-        Extension extension1 = extensions.getByName("my-extension1");
-        assertNotNull(extension1);
-        assertEquals(extension1.getName(), "my-extension1");
-        assertEquals(extension1.getState(), ExtensionState.OPTIONAL);
-        assertEquals(1, extension1.getArtifacts().size());
-
-        ArtifactId artifactId1 = extension1.getArtifacts().get(0).getId();
-        assertEquals(artifactId1.getGroupId(), "org.apache.sling");
-        assertEquals(artifactId1.getArtifactId(), "my-extension1");
-        assertEquals(artifactId1.getVersion(), "1.2.3");
-
-        Extension extension2 = extensions.getByName("my-extension2");
-        assertNotNull(extension2);
-        assertEquals(extension2.getName(), "my-extension2");
-        assertEquals(extension2.getState(), ExtensionState.REQUIRED);
-        assertEquals(1, extension2.getArtifacts().size());
-
-        ArtifactId artifactId2 = extension2.getArtifacts().get(0).getId();
-        assertEquals(artifactId2.getGroupId(), "org.apache.sling");
-        assertEquals(artifactId2.getArtifactId(), "my-extension2");
-        assertEquals(artifactId2.getVersion(), "1.2.3");
-    }
-}
diff --git a/src/test/java/org/apache/sling/feature/io/json/FeatureJSONReaderTest.java b/src/test/java/org/apache/sling/feature/io/json/FeatureJSONReaderTest.java
deleted file mode 100644
index d2f6872..0000000
--- a/src/test/java/org/apache/sling/feature/io/json/FeatureJSONReaderTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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 static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.util.Arrays;
-
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Bundles;
-import org.apache.sling.feature.Configuration;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.Extensions;
-import org.apache.sling.feature.Feature;
-import org.junit.Test;
-import org.osgi.resource.Capability;
-
-public class FeatureJSONReaderTest {
-
-    @Test public void testRead() throws Exception {
-        final Feature feature = U.readFeature("test");
-        assertNotNull(feature);
-        assertNotNull(feature.getId());
-        assertEquals("org.apache.sling", feature.getId().getGroupId());
-        assertEquals("test-feature", feature.getId().getArtifactId());
-        assertEquals("1.1", feature.getId().getVersion());
-        assertEquals("jar", feature.getId().getType());
-        assertNull(feature.getId().getClassifier());
-
-        assertEquals(2, feature.getConfigurations().size());
-        final Configuration cfg1 = U.findConfiguration(feature.getConfigurations(), "my.pid");
-        assertEquals(7, cfg1.getProperties().get("number"));
-        final Configuration cfg2 = U.findConfiguration(feature.getConfigurations(), "my.factory.pid~name");
-        assertEquals("yeah", cfg2.getProperties().get("a.value"));
-
-        assertEquals(3, feature.getCapabilities().size());
-        Capability capability = U.findCapability(feature.getCapabilities(),"osgi.service");
-        assertNotNull(capability.getAttributes().get("objectClass"));
-
-        assertEquals(Arrays.asList("org.osgi.service.http.runtime.HttpServiceRuntime"), capability.getAttributes().get("objectClass"));
-
-    }
-
-    @Test public void testReadRepoInitExtension() throws Exception {
-        Feature feature = U.readFeature("repoinit");
-        Extensions extensions = feature.getExtensions();
-        assertEquals(1, extensions.size());
-        Extension ext = extensions.iterator().next();
-        assertEquals("some repo init\ntext", ext.getText());
-    }
-
-    @Test public void testReadRepoInitExtensionArray() throws Exception {
-        Feature feature = U.readFeature("repoinit2");
-        Extensions extensions = feature.getExtensions();
-        assertEquals(1, extensions.size());
-        Extension ext = extensions.iterator().next();
-        assertEquals("some repo init\ntext\n", ext.getText());
-    }
-
-    @Test public void testReadArtifactsExtensions() throws Exception {
-        final Feature feature = U.readFeature("artifacts-extension");
-        ArtifactsExtensions.testReadArtifactsExtensions(feature);
-    }
-
-    @Test
-    public void testFinalFlag() throws Exception {
-        final Feature featureA = U.readFeature("test");
-        assertFalse(featureA.isFinal());
-
-        final Feature featureB = U.readFeature("final");
-        assertTrue(featureB.isFinal());
-    }
-
-    @Test
-    public void testCompleteFlag() throws Exception {
-        final Feature featureA = U.readFeature("test");
-        assertFalse(featureA.isComplete());
-
-        final Feature featureB = U.readFeature("complete");
-        assertTrue(featureB.isComplete());
-    }
-
-    @Test
-    public void testReadMultiBSNVer() throws Exception {
-        final Feature f = U.readFeature("test3");
-        Bundles fb = f.getBundles();
-        assertEquals(2, fb.size());
-        assertTrue(fb.containsExact(ArtifactId.fromMvnId("org.apache.sling:foo:1.2.3")));
-        assertTrue(fb.containsExact(ArtifactId.fromMvnId("org.apache.sling:foo:4.5.6")));
-        assertFalse(fb.containsExact(ArtifactId.fromMvnId("org.apache.sling:foo:7.8.9")));
-    }
-
-    @Test
-    public void readComments() throws Exception {
-        // we only test whether the feature can be read without problems
-        U.readFeature("feature-model");
-    }
-}
diff --git a/src/test/java/org/apache/sling/feature/io/json/FeatureJSONWriterTest.java b/src/test/java/org/apache/sling/feature/io/json/FeatureJSONWriterTest.java
deleted file mode 100644
index 4d3feca..0000000
--- a/src/test/java/org/apache/sling/feature/io/json/FeatureJSONWriterTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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 static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import java.io.InputStreamReader;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.util.Arrays;
-
-import javax.json.Json;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.json.JsonValue;
-import javax.json.JsonValue.ValueType;
-
-import org.apache.sling.feature.Feature;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class FeatureJSONWriterTest {
-
-    @Test public void testWrite() throws Exception {
-        final Feature f = U.readFeature("test");
-        final Feature rf;
-        try ( final StringWriter writer = new StringWriter() ) {
-            FeatureJSONWriter.write(writer, f);
-            try ( final StringReader reader = new StringReader(writer.toString()) ) {
-                rf = FeatureJSONReader.read(reader, null);
-            }
-        }
-        assertEquals(f.getId(), rf.getId());
-        assertEquals("org.apache.sling:test-feature:1.1", rf.getId().toMvnId());
-        assertEquals("The feature description", rf.getDescription());
-
-        assertEquals(Arrays.asList("org.osgi.service.http.runtime.HttpServiceRuntime"),
-                U.findCapability(rf.getCapabilities(), "osgi.service").getAttributes().get("objectClass"));
-    }
-
-    @Test public void testWrite2() throws Exception {
-        final Feature f = U.readFeature("test2");
-
-        final Feature rf;
-        try ( final StringWriter writer = new StringWriter() ) {
-            FeatureJSONWriter.write(writer, f);
-            try ( final StringReader reader = new StringReader(writer.toString()) ) {
-                rf = FeatureJSONReader.read(reader, null);
-            }
-        }
-
-        assertEquals(f.getVariables(), rf.getVariables());
-    }
-
-    @Test public void testExtensionsWriteRead() throws Exception {
-        final Feature f = U.readFeature("artifacts-extension");
-        final Feature rf;
-        try ( final StringWriter writer = new StringWriter() ) {
-            FeatureJSONWriter.write(writer, f);
-            try ( final StringReader reader = new StringReader(writer.toString()) ) {
-                rf = FeatureJSONReader.read(reader, null);
-            }
-        }
-
-        ArtifactsExtensions.testReadArtifactsExtensions(rf);
-    }
-
-    @Test public void testPrototypeWriteRead() throws Exception {
-        final Feature f = U.readFeature("test");
-        assertNotNull(f.getPrototype());
-
-        final Feature rf;
-        try ( final StringWriter writer = new StringWriter() ) {
-            FeatureJSONWriter.write(writer, f);
-            try ( final StringReader reader = new StringReader(writer.toString()) ) {
-                rf = FeatureJSONReader.read(reader, null);
-            }
-        }
-        assertEquals(f.getPrototype().getId(), rf.getPrototype().getId());
-    }
-
-    @Test public void testRepoInitWrite() throws Exception {
-        final Feature f = U.readFeature("repoinit2");
-        try ( final StringWriter writer = new StringWriter() ) {
-            FeatureJSONWriter.write(writer, f);
-            final JsonObject refJson = Json.createReader(
-                    new InputStreamReader(U.class.getResourceAsStream("/features/repoinit2.json"))
-                ).readObject();
-            final JsonObject resultJson = Json.createReader(new StringReader(writer.toString())).readObject();
-
-            JsonArray refJsonArray = refJson.getJsonArray("repoinit:TEXT|false");
-            JsonArray resultJsonArray = resultJson.getJsonArray("repoinit:TEXT|false");
-            Assert.assertEquals(refJsonArray, resultJsonArray);
-        }
-    }
-
-    @Test
-    public void testFinalFlag() throws Exception {
-        // no final flag set in test feature
-        final Feature featureA = U.readFeature("test");
-        try (final StringWriter writer = new StringWriter()) {
-            FeatureJSONWriter.write(writer, featureA);
-            final JsonObject resultJson = Json.createReader(new StringReader(writer.toString())).readObject();
-
-            assertNull(resultJson.get(JSONConstants.FEATURE_FINAL));
-        }
-
-        // final flag set in final feature
-        final Feature featureB = U.readFeature("final");
-        try (final StringWriter writer = new StringWriter()) {
-            FeatureJSONWriter.write(writer, featureB);
-            final JsonObject resultJson = Json.createReader(new StringReader(writer.toString())).readObject();
-
-            final JsonValue val = resultJson.get(JSONConstants.FEATURE_FINAL);
-            assertNotNull(val);
-            assertEquals(ValueType.TRUE, val.getValueType());
-        }
-    }
-}
diff --git a/src/test/java/org/apache/sling/feature/io/json/U.java b/src/test/java/org/apache/sling/feature/io/json/U.java
deleted file mode 100644
index ee642c1..0000000
--- a/src/test/java/org/apache/sling/feature/io/json/U.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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 static org.junit.Assert.fail;
-
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.List;
-
-import org.apache.sling.feature.Configuration;
-import org.apache.sling.feature.Feature;
-import org.osgi.resource.Capability;
-import org.osgi.resource.Requirement;
-
-/** Test utilities */
-public class U {
-
-    /** Read the feature from the provided resource
-     */
-    public static Feature readFeature(final String name) throws Exception {
-        try ( final Reader reader = new InputStreamReader(U.class.getResourceAsStream("/features/" + name + ".json"),
-                "UTF-8") ) {
-            return FeatureJSONReader.read(reader, name);
-        }
-    }
-
-    public static Configuration findConfiguration(final List<Configuration> cfgs, final String pid) {
-        for(final Configuration c : cfgs) {
-            if (pid.equals(c.getPid())) {
-                return c;
-            }
-        }
-        fail("Configuration not found " + pid);
-        return null;
-    }
-
-    public static Capability findCapability(List<Capability> capabilities, final String namespace) {
-        for (Capability capability : capabilities) {
-            if (capability.getNamespace().equals(namespace)) {
-                return capability;
-            }
-        }
-
-        fail(String.format("No Capability with namespace '%s' found", namespace));
-        return null;
-    }
-
-    public static Requirement findRequirement(List<Requirement> requirements, final String namespace) {
-        for (Requirement requirement : requirements) {
-            if (requirement.getNamespace().equals(namespace)) {
-                return requirement;
-            }
-        }
-
-        fail(String.format("No Requirement with namespace '%s' found", namespace));
-        return null;
-    }
-}
diff --git a/src/test/resources/features/artifacts-extension.json b/src/test/resources/features/artifacts-extension.json
deleted file mode 100644
index 3bcd47a..0000000
--- a/src/test/resources/features/artifacts-extension.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "id": "test/artifacts-extension/1.0.0",
-  "my-extension1:ARTIFACTS|false":[
-    "org.apache.sling/my-extension1/1.2.3"
-  ],
-  "my-extension2:ARTIFACTS|true":[
-    "org.apache.sling/my-extension2/1.2.3"
-  ]
-}
\ No newline at end of file
diff --git a/src/test/resources/features/complete.json b/src/test/resources/features/complete.json
deleted file mode 100644
index dc4b141..0000000
--- a/src/test/resources/features/complete.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-    "id" : "org.apache.sling/test-feature-complete/1.0.0",
-    "complete": true
-}
\ No newline at end of file
diff --git a/src/test/resources/features/feature-model.json b/src/test/resources/features/feature-model.json
deleted file mode 100644
index aff0e66..0000000
--- a/src/test/resources/features/feature-model.json
+++ /dev/null
@@ -1,123 +0,0 @@
-{
-    // this is a comment
-    "id": "org.apache.sling:my.app:slingosgifeature:my-classifier:1.0",
-
-    "title": "A title for the feature. (optional)",
-    "description": "A description for the feature. (optional)",
-    "vendor": "The feature vendor, for example 'Apache Software Foundation'. (optional)",
-    "license": "The license of this feature file, for example 'ASL-2'. (optional)",
-
-    // A complete feature has no external dependencies
-    "complete": true,
-
-    // A final feature cannot be used as a prototype for another feature
-    "final": false,
-
-    // variables used in configuration and framework properties are substituted at launch time.
-    "variables": {
-        "cfgvar": "somedefault",
-        "org.abc.xyz": "1.2.3",
-
-        // When converting to provisioning model, if you need a special name
-        "provisioning.model.name": ":boot"
-    },
-
-    // A prototype is another feature that is used as a prototype for this one
-    // Bundles, configurations and framework properties can be removed from the
-    //prototype. Bundles with the same artifact ID defined in the feature override
-    // bundles with this artifact ID in the Prototype
-    "prototype": 
-        {
-            "id": "org.apache.sling:some-other-feature:1.2.3",
-            "removals": {
-                "configurations": [],
-                "bundles": [],
-                "framework-properties": []
-            }
-        },
-
-    // Requirements over and above the requirements in the bundles referenced by
-    // feature.
-    "requirements": [
-        {
-            "namespace": "osgi.contract",
-            "directives": {
-                "filter": "(&(osgi.contract=JavaServlet)(version=3.1))"
-            }
-        }
-    ],
-
-    // Capabilities over and above the capabilities provided by the bundles referenced
-    // by the feature.
-    "capabilities": [
-        {
-            "namespace": "osgi.implementation",
-            "attributes": {
-                "osgi.implementation": "osgi.http",
-                "version:Version": "1.1"
-            },
-            "directives": {
-                "uses": "javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard"
-            }
-        },
-        {
-            "namespace": "osgi.service",
-            "attributes": {
-                "objectClass:List<String>": "org.osgi.service.http.runtime.HttpServiceRuntime"
-            },
-            "directives": {
-                "uses": "org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto"
-            }
-        }
-    ],
-
-    // Framework properties to be provided to the running OSGi Framework
-    "framework-properties": {
-        "foo": 1,
-        "org.osgi.framework.storage": "${tempdir}",
-        "org.apache.felix.scr.directory": "launchpad/scr"
-    },
-
-    // The bundles that are part of the feature. Bundles are referenced using Maven
-    // coordinates and can have additional metadata associated with them. Bundles can
-    // specified as either a simple string (the Maven coordinates of the bundle) or
-    // as an object with 'id' and additional metadata.
-    "bundles": [
-        {
-            "id": "org.apache.sling:security-server:2.2.0",
-            "hash": "4632463464363646436",
-
-            // This is the relative start order inside the feature
-            "start-order": 5
-        },
-        {
-            "id": "org.apache.sling:application-bundle:2.0.0",
-            "start-order": 10
-        },
-        "org.apache.sling:foo-xyz:1.2.3"
-    ],
-
-    // The configurations are specified following the format defined by the OSGi Configurator
-    // specification: https://osgi.org/specification/osgi.cmpn/7.0.0/service.configurator.html
-    // Variables declared in the variables section can be used for late binding of variables
-    // they can be specified with the Launcher, or the default from the variables section is used.
-    // Factory configurations can be specified using the named factory syntax, which separates
-    // The factory PID and the name with a tilde '~'
-    "configurations": {
-        "my.pid": {
-            "foo": 5,
-            "something-enabled": false,
-            "bar": "${cfgvar}",
-
-            // The tempdir variable is not specified at the variables section.
-            // It needs to be provided at launch, otherwise the launch will stop.
-            "tempdir": "${tempdir}",
-
-
-            "number:Integer": 7
-        },
-        "my.factory.pid~name": {
-           "a.value":"yeah"
-        }
-    }
-}
diff --git a/src/test/resources/features/final.json b/src/test/resources/features/final.json
deleted file mode 100644
index ff1d8a0..0000000
--- a/src/test/resources/features/final.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-    "id" : "org.apache.sling/final-feature/1.1",
-    "description": "The feature description",
-    "final" : true
-}
\ No newline at end of file
diff --git a/src/test/resources/features/repoinit.json b/src/test/resources/features/repoinit.json
deleted file mode 100644
index 2177702..0000000
--- a/src/test/resources/features/repoinit.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-    "id": "test/repoinit/1.0.0",
-    "repoinit:TEXT|false": "some repo init\ntext"
-}
diff --git a/src/test/resources/features/repoinit2.json b/src/test/resources/features/repoinit2.json
deleted file mode 100644
index beef986..0000000
--- a/src/test/resources/features/repoinit2.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-    "id": "test/repoinit2/1.0.0",
-    "repoinit:TEXT|false": [
-        "some repo init",
-        "text"
-    ]
-}
diff --git a/src/test/resources/features/test.json b/src/test/resources/features/test.json
deleted file mode 100644
index 42d4c4d..0000000
--- a/src/test/resources/features/test.json
+++ /dev/null
@@ -1,92 +0,0 @@
-{
-    "id" : "org.apache.sling/test-feature/1.1",
-    "description": "The feature description",
-
-    "prototype" :
-         {
-             "id" : "org.apache.sling/sling/9",
-             "removals" : {
-                 "configurations" : [
-                 ],
-                 "bundles" : [
-                 ],
-                 "framework-properties" : [
-                 ]
-             }
-         }
-    ,
-    "requirements" : [
-          {
-              "namespace" : "osgi.contract",
-              "directives" : {
-                  "filter" : "(&(osgi.contract=JavaServlet)(&(version>=3.0)(!(version>=4.0))))"
-              }
-          }
-    ],
-    "capabilities" : [
-        {
-             "namespace" : "osgi.implementation",
-             "attributes" : {
-                   "osgi.implementation" : "osgi.http",
-                   "version:Version" : "1.1"
-             },
-             "directives" : {
-                  "uses" : "javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard"
-             }
-        },
-        {
-             "namespace" : "osgi.service",
-             "attributes" : {
-                  "objectClass:List<String>" : "org.osgi.service.http.runtime.HttpServiceRuntime"
-             },
-             "directives" : {
-                  "uses" : "org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto"
-             }
-        },
-        {
-          "namespace" : "osgi.contract",
-          "attributes" : {
-            "osgi.contract" : "JavaServlet",
-            "osgi.implementation" : "osgi.http",
-            "version:Version" : "3.1"
-          },
-          "directives" : {
-            "uses" : "org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto"
-          }
-        }
-    ],
-    "framework-properties" : {
-        "foo" : 1,
-        "brave" : "something",
-        "org.apache.felix.scr.directory" : "launchpad/scr"
-    },
-    "bundles" :[
-            {
-              "id" : "org.apache.sling/oak-server/1.0.0",
-              "hash" : "4632463464363646436",
-              "start-order" : 1
-            },
-            {
-              "id" : "org.apache.sling/application-bundle/2.0.0",
-              "start-order" : 1
-            },
-            {
-              "id" : "org.apache.sling/another-bundle/2.1.0",
-              "start-order" : 1
-            },
-            {
-              "id" : "org.apache.sling/foo-xyz/1.2.3",
-              "start-order" : 2
-            }
-    ],
-    "configurations" : {
-        "my.pid" : {
-           "foo" : 5,
-           "bar" : "test",
-           "number:Integer" : 7
-        },
-        "my.factory.pid~name" : {
-           "a.value" : "yeah"
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/test/resources/features/test2.json b/src/test/resources/features/test2.json
deleted file mode 100644
index d89a051..0000000
--- a/src/test/resources/features/test2.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-    "id" : "org.apache.sling/test-feature2/1.0.0.0",
-    "variables": {
-        "foo": "bar",
-        "zar": null
-    }
-}
\ No newline at end of file
diff --git a/src/test/resources/features/test3.json b/src/test/resources/features/test3.json
deleted file mode 100644
index de143cc..0000000
--- a/src/test/resources/features/test3.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-    "id" : "org.apache.sling/test-feature3/2",
-    "bundles": [
-        "org.apache.sling/foo/1.2.3",
-        "org.apache.sling/foo/4.5.6"
-    ]
-}
\ No newline at end of file