You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by si...@apache.org on 2019/03/27 22:57:04 UTC

[sling-org-apache-sling-feature-cpconverter] branch master created (now f039042)

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

simonetripodi pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-cpconverter.git.


      at f039042  initial checkin, moved from whiteboard

This branch includes the following new commits:

     new f039042  initial checkin, moved from whiteboard

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[sling-org-apache-sling-feature-cpconverter] 01/01: initial checkin, moved from whiteboard

Posted by si...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f039042ac8bdfd5016474abf823a03a7312b51a5
Author: Simo Tripodi <st...@adobe.com>
AuthorDate: Wed Mar 27 23:56:54 2019 +0100

    initial checkin, moved from whiteboard
---
 CODE_OF_CONDUCT.md                                 |  22 ++
 CONTRIBUTING.md                                    |  24 ++
 LICENSE                                            | 201 ++++++++++
 README.md                                          | 398 +++++++++++++++++++
 pom.xml                                            | 322 +++++++++++++++
 src/main/assembly/README.md                        | 149 +++++++
 src/main/assembly/bin.xml                          |  71 ++++
 .../ContentPackage2FeatureModelConverter.java      | 431 +++++++++++++++++++++
 .../cpconverter/DefaultBundlesDeployer.java        |  99 +++++
 .../cpconverter/RegexBasedResourceFilter.java      |  54 +++
 ...ntentPackage2FeatureModelConverterLauncher.java | 186 +++++++++
 .../feature/cpconverter/cli/ShutDownHook.java      |  81 ++++
 .../feature/cpconverter/cli/package-info.java      |  21 +
 .../AbstractConfigurationEntryHandler.java         |  68 ++++
 .../handlers/AbstractRegexEntryHandler.java        |  44 +++
 .../cpconverter/handlers/BundleEntryHandler.java   | 136 +++++++
 .../handlers/ConfigurationEntryHandler.java        |  36 ++
 .../handlers/ContentPackageEntryHandler.java       |  49 +++
 .../handlers/JsonConfigurationEntryHandler.java    |  75 ++++
 .../PropertiesConfigurationEntryHandler.java       |  60 +++
 .../handlers/XmlConfigurationEntryHandler.java     |  92 +++++
 .../feature/cpconverter/handlers/package-info.java |  23 ++
 .../sling/feature/cpconverter/package-info.java    |  21 +
 .../feature/cpconverter/spi/ArtifactWriter.java    |  26 ++
 .../feature/cpconverter/spi/BundlesDeployer.java   |  33 ++
 .../feature/cpconverter/spi/EntryHandler.java      |  29 ++
 .../feature/cpconverter/spi/package-info.java      |  21 +
 .../cpconverter/vltpkg/VaultPackageAssembler.java  | 172 ++++++++
 .../feature/cpconverter/vltpkg/package-info.java   |  21 +
 .../cpconverter/writers/FileArtifactWriter.java    |  42 ++
 .../writers/InputStreamArtifactWriter.java         |  39 ++
 .../writers/MavenPomSupplierWriter.java            |  54 +++
 .../feature/cpconverter/writers/package-info.java  |  21 +
 src/main/legal/LICENSE-with-deps                   | 233 +++++++++++
 src/main/legal/NOTICE-with-deps                    |   5 +
 src/main/legal/license-header                      |  14 +
 ...ache.sling.feature.cpconverter.spi.EntryHandler |   6 +
 .../sling/feature/cpconverter/vltpkg/config.xml    |  95 +++++
 .../cpconverter/vltpkg/definition/.content.xml     |  19 +
 .../sling/feature/cpconverter/vltpkg/settings.xml  |  22 ++
 .../ContentPackage2FeatureModelConverterTest.java  | 235 +++++++++++
 .../cpconverter/DefaultBundlesDeployerTest.java    |  80 ++++
 .../cpconverter/RegexBasedResourceFilterTest.java  |  69 ++++
 .../handlers/BundleEntryHandlerTest.java           | 118 ++++++
 .../handlers/ConfigurationEntryHandlerTest.java    | 127 ++++++
 .../handlers/ContentPackageEntryHandlerTest.java   |  51 +++
 .../JsonConfigurationEntryHandlerTest.java         |  46 +++
 .../vltpkg/VaultPackageAssemblerTest.java          |  89 +++++
 .../cpconverter/handlers/jcr_root/.content.xml     |  22 ++
 ...ceusermapping.impl.ServiceUserMapperImpl.config |  17 +
 ...ceusermapping.impl.ServiceUserMapperImpl.config |  17 +
 ...ing.impl.ServiceUserMapperImpl.INVALID.cfg.json |   1 +
 ...rviceusermapping.impl.ServiceUserMapperImpl.cfg |  17 +
 ...usermapping.impl.ServiceUserMapperImpl.cfg.json |   7 +
 ...ceusermapping.impl.ServiceUserMapperImpl.config |  17 +
 ...sermapping.impl.ServiceUserMapperImpl.empty.cfg |  14 +
 ...pping.impl.ServiceUserMapperImpl.empty.cfg.json |   3 +
 ...mapping.impl.ServiceUserMapperImpl.empty.config |  14 +
 ...sermapping.impl.ServiceUserMapperImpl.empty.xml |  19 +
 ...apping.impl.ServiceUserMapperImpl.empty.xml.cfg |  19 +
 ...rviceusermapping.impl.ServiceUserMapperImpl.xml |  21 +
 ...eusermapping.impl.ServiceUserMapperImpl.xml.cfg |  22 ++
 .../apps/asd/install.author/test-framework.jar     | Bin 0 -> 13288 bytes
 .../apps/asd/install.publish/test-framework.jar    | Bin 0 -> 13288 bytes
 .../apps/asd/install/test-framework-no-pom.jar     | Bin 0 -> 10769 bytes
 .../jcr_root/apps/asd/install/test-framework.jar   | Bin 0 -> 13288 bytes
 .../cpconverter/handlers/jcr_root/asd/.content.xml |  20 +
 .../handlers/jcr_root/asd/public/.content.xml      |  19 +
 .../handlers/jcr_root/asd/public/_rep_policy.xml   |  24 ++
 .../handlers/jcr_root/asd/public/license.txt       |  14 +
 .../test-content-package-unacceptable.zip          | Bin 0 -> 15017 bytes
 .../feature/cpconverter/test-content-package.zip   | Bin 0 -> 46393 bytes
 72 files changed, 4617 insertions(+)

diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..0fa18e5
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,22 @@
+<!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
+Apache Software Foundation Code of Conduct
+====
+
+Being an Apache project, Apache Sling adheres to the Apache Software Foundation's [Code of Conduct](https://www.apache.org/foundation/policies/conduct.html).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..ac82a1a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,24 @@
+<!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
+Contributing
+====
+
+Thanks for choosing to contribute!
+
+You will find all the necessary details about how you can do this at https://sling.apache.org/contributing.html.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3072730
--- /dev/null
+++ b/README.md
@@ -0,0 +1,398 @@
+[<img src="http://sling.apache.org/res/logos/sling.png"/>](http://sling.apache.org)
+
+# Apache Sling Content-Package to Feature Model converter
+
+This tool aims to provide to Apache Sling users an easy-to-use conversion tool which is able to convert `content-package` archives to the new _Sling Feature Model_.
+
+## Introduction
+
+`content-package`s are zipped archives containing OSGi bundles, OSGi configurations and resources (and nested `content-package`s as well), aside metadata, that can be used to install content into a _JCR_ repository using the [Apache Jackrabbit FileVault](http://jackrabbit.apache.org/filevault/) packaging runtime.
+
+OTOH, [Apache Sling Feature](https://github.com/apache/sling-org-apache-sling-feature) allows users to describe an entire OSGi-based application based on reusable components and includes everything related to this application, including bundles, configuration, framework properties, capabilities, requirements and custom artifacts.
+
+The _Apache Sling Content Package to Feature Model converter_ (referred as _cp2fm_) is a tool able to extract OSGI bundles, OSGi configurations, resources and iteratively scan nested `content-package`s from an input `content-package` and create one (or more) _Apache Sling Feature_ model files and deploy the extracted OSGi bundles in a directory which structure is compliant the _Apache Maven_ repository conventions.
+
+## Understanding the Input
+
+As exposed above, `content-package`s are archives, compressed with the ZIP algorithm, which contain:
+
+ * OSGi bundles, conventionally found under the `jcr_root/apps/<application>/install(.runMode)/<bundle>.jar` path; typically, OSGi bundles are also valid _Apache Maven_ artifacts, that means that they contain _Apache Maven_ metadata files such as `META-INF/maven/<groupId>/<artifactId>/pom.(xml|properties)`;
+ * OSGi configurations, conventionally found under the `jcr_root/apps/<application>/config(.runMode)/<configuration>.<extension>` path;
+ * nested `content-package`s, conventionally found under the `jcr_root/etc/packages/<package-name>.zip` path;
+ * Metadata files, under the `META-INF/` directory;
+ * any other kind of resource.
+
+### a content-package sample
+
+We can have a look at what's inside a `test-content-package.zip` test `content-package` included in the `cp2fm` test resources:
+
+```
+$ unzip -l ./content-package-2-feature-model/src/test/resources/org/apache/sling/cp2fm/test-content-package.zip 
+Archive:  content-package-2-feature-model/src/test/resources/org/apache/sling/cp2fm/test-content-package.zip
+  Length      Date    Time    Name
+---------  ---------- -----   ----
+        0  03-12-2019 17:31   META-INF/
+       69  03-12-2019 17:31   META-INF/MANIFEST.MF
+        0  03-12-2019 17:06   jcr_root/
+        0  03-12-2019 17:06   jcr_root/etc/
+        0  03-12-2019 17:06   jcr_root/etc/packages/
+        0  03-12-2019 17:30   jcr_root/etc/packages/asd/
+    34493  03-12-2019 17:30   jcr_root/etc/packages/asd/test-bundles.zip
+     8333  03-12-2019 17:09   jcr_root/etc/packages/asd/test-content.zip
+     7235  03-12-2019 17:08   jcr_root/etc/packages/asd/test-configurations.zip
+        0  03-12-2019 15:28   META-INF/maven/
+        0  03-12-2019 15:29   META-INF/maven/org.apache.sling/
+        0  02-28-2019 14:27   META-INF/maven/org.apache.sling/org.apache.sling.feature.cpconverter.all/
+     1231  03-12-2019 15:30   META-INF/maven/org.apache.sling/org.apache.sling.feature.cpconverter.all/pom.xml
+      127  03-12-2019 15:30   META-INF/maven/org.apache.sling/org.apache.sling.feature.cpconverter.all/pom.properties
+        0  03-12-2019 17:06   META-INF/vault/
+      892  03-12-2019 15:32   META-INF/vault/settings.xml
+      840  03-12-2019 15:47   META-INF/vault/properties.xml
+     3579  03-12-2019 15:33   META-INF/vault/config.xml
+      267  03-12-2019 15:50   META-INF/vault/filter.xml
+---------                     -------
+    63214                     20 files
+```
+
+Where the `test-bundles.zip` is a nested `content-package` wrapping OSGi bundles:
+
+```
+$ unzip -l test-bundles.zip 
+Archive:  test-bundles.zip
+  Length      Date    Time    Name
+---------  ---------- -----   ----
+        0  03-12-2019 17:30   META-INF/
+       69  03-12-2019 17:30   META-INF/MANIFEST.MF
+        0  03-11-2019 23:39   jcr_root/
+        0  03-11-2019 23:31   jcr_root/apps/
+        0  03-12-2019 17:26   jcr_root/apps/asd/
+        0  03-11-2019 23:32   jcr_root/apps/asd/install/
+    13288  12-06-2018 12:30   jcr_root/apps/asd/install/test-framework.jar
+        0  03-12-2019 17:16   jcr_root/apps/asd/install.publish/
+     7210  03-12-2019 17:15   jcr_root/apps/asd/install.publish/test-api.jar
+        0  03-12-2019 17:18   jcr_root/apps/asd/install.author/
+     7735  03-12-2019 17:17   jcr_root/apps/asd/install.author/test-api.jar
+        0  03-11-2019 23:42   META-INF/maven/
+        0  03-11-2019 23:43   META-INF/maven/org.apache.sling/
+        0  02-28-2019 14:26   META-INF/maven/org.apache.sling/org.apache.sling.feature.cpconverter.bundles/
+     1229  03-12-2019 10:22   META-INF/maven/org.apache.sling/org.apache.sling.feature.cpconverter.bundles/pom.xml
+      131  03-12-2019 00:26   META-INF/maven/org.apache.sling/org.apache.sling.feature.cpconverter.bundles/pom.properties
+        0  03-12-2019 12:41   META-INF/vault/
+      888  03-12-2019 00:28   META-INF/vault/settings.xml
+      954  03-12-2019 15:33   META-INF/vault/properties.xml
+     3571  03-12-2019 00:27   META-INF/vault/config.xml
+      891  03-12-2019 00:28   META-INF/vault/filter.xml
+      842  03-12-2019 00:27   META-INF/vault/filter-plugin-generated.xml
+---------                     -------
+    79844                     29 files
+```
+
+the `test-configurations.zip` contains OSGi configurations:
+
+```
+$ unzip -l test-configurations.zip 
+Archive:  test-configurations.zip
+  Length      Date    Time    Name
+---------  ---------- -----   ----
+        0  03-12-2019 17:08   META-INF/
+       69  03-12-2019 17:08   META-INF/MANIFEST.MF
+        0  03-12-2019 10:21   META-INF/maven/
+        0  03-12-2019 10:21   META-INF/maven/org.apache.sling/
+        0  02-28-2019 14:25   META-INF/maven/org.apache.sling/org.apache.sling.feature.cpconverter.config/
+     1228  03-12-2019 10:24   META-INF/maven/org.apache.sling/org.apache.sling.feature.cpconverter.config/pom.xml
+      129  03-12-2019 10:22   META-INF/maven/org.apache.sling/org.apache.sling.feature.cpconverter.config/pom.properties
+        0  03-12-2019 13:23   META-INF/vault/
+       94  02-28-2019 14:25   META-INF/vault/settings.xml
+      664  03-12-2019 15:13   META-INF/vault/properties.xml
+     3579  02-28-2019 14:25   META-INF/vault/config.xml
+      175  03-12-2019 10:37   META-INF/vault/filter.xml
+        0  02-28-2019 14:25   jcr_root/
+        0  03-12-2019 10:17   jcr_root/apps/
+        0  02-28-2019 14:25   jcr_root/apps/asd/
+        0  03-12-2019 10:17   jcr_root/apps/asd/config/
+      438  02-28-2019 14:25   jcr_root/apps/asd/config/org.apache.sling.commons.log.LogManager.factory.config-asd-retail.xml
+        0  03-12-2019 10:18   jcr_root/apps/asd/config.publish/
+      377  02-28-2019 14:25   jcr_root/apps/asd/config.publish/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-asd-retail.xml
+      244  02-28-2019 14:25   jcr_root/apps/.content.xml
+---------                     -------
+    25441                     23 files
+```
+
+and the `test-content.zip` package includes resources of various nature:
+
+```
+$ unzip -l test-content.zip 
+Archive:  test-content.zip
+  Length      Date    Time    Name
+---------  ---------- -----   ----
+        0  03-12-2019 17:09   META-INF/
+       69  03-12-2019 17:09   META-INF/MANIFEST.MF
+        0  03-12-2019 11:31   META-INF/maven/
+        0  03-12-2019 11:31   META-INF/maven/org.apache.sling/
+        0  02-28-2019 14:26   META-INF/maven/org.apache.sling/org.apache.sling.feature.cpconverter.content/
+     1229  03-12-2019 11:32   META-INF/maven/org.apache.sling/org.apache.sling.feature.cpconverter.content/pom.xml
+      131  03-12-2019 11:32   META-INF/maven/org.apache.sling/org.apache.sling.feature.cpconverter.content/pom.properties
+        0  03-12-2019 12:40   META-INF/vault/
+      118  02-28-2019 14:26   META-INF/vault/settings.xml
+      859  03-12-2019 15:12   META-INF/vault/properties.xml
+     3571  03-12-2019 12:42   META-INF/vault/config.xml
+      895  03-12-2019 12:57   META-INF/vault/filter.xml
+       72  02-28-2019 14:26   META-INF/vault/filter-plugin-generated.xml
+        0  03-12-2019 12:30   jcr_root/
+        0  03-12-2019 12:31   jcr_root/content/
+        0  03-12-2019 12:31   jcr_root/content/asd/
+     1021  02-28-2019 14:26   jcr_root/content/asd/.content.xml
+     6924  02-28-2019 14:26   jcr_root/content/asd/resources.xml
+---------                     -------
+    39481                     22 files
+```
+
+## Mapping and the Output
+
+All metadata are mainly collected inside one or more, depending by declared run modes in the installation and configuration paths, _Feature_ model files:
+
+```json
+$ cat asd.retail.all.json 
+{
+  "id":"org.apache.sling:asd.retail.all:slingosgifeature:cp2fm-converted-feature:0.0.1",
+  "description":"Combined package for asd.Retail",
+  "bundles":[
+    {
+      "id":"org.apache.felix:org.apache.felix.framework:6.0.1",
+      "start-order":"5"
+    }
+  ],
+  "configurations":{
+    "org.apache.sling.commons.log.LogManager.factory.config-asd-retail":{
+      "org.apache.sling.commons.log.pattern":"{0,date,yyyy-MM-dd HH:mm:ss.SSS} {4} [{3}] {5}",
+      "org.apache.sling.commons.log.names":[
+        "we.retail"
+      ],
+      "org.apache.sling.commons.log.level":"info",
+      "org.apache.sling.commons.log.file":"logs/project-we-retail.log"
+    }
+  },
+  "content-packages:ARTIFACTS|true":[
+    "org.apache.sling:asd.retail.all:zip:cp2fm-converted-feature:0.0.1"
+  ]
+}
+```
+
+the `publish` run mode leads the tool to generate a separated _Apache Sling Feature_ model file:
+
+```json
+$ cat asd.retail.all-publish.json 
+{
+  "id":"org.apache.sling:asd.retail.all:slingosgifeature:cp2fm-converted-feature-publish:0.0.1",
+  "bundles":[
+    {
+      "id":"org.apache.sling:org.apache.sling.models.api:1.3.8",
+      "start-order":"5"
+    }
+  ],
+  "configurations":{
+    "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-asd-retail":{
+      "user.mapping":[
+        "com.asd.sample.we.retail.core:orders=[commerce-orders-service]",
+        "com.asd.sample.we.retail.core:frontend=[content-reader-service]"
+      ]
+    }
+  }
+}
+```
+
+bundles are collected in an _Apache Maven repository_ compliant directory, all other resources are collected in a new `content-package` created while scanning the packages:
+
+```
+$ tree bundles/
+bundles/
+└── org
+    └── apache
+        ├── felix
+        │   └── org.apache.felix.framework
+        │       └── 6.0.1
+        │           ├── org.apache.felix.framework-6.0.1.jar
+        │           └── org.apache.felix.framework-6.0.1.pom
+        └── sling
+            ├── asd.retail.all
+            │   └── 0.0.1
+            │       ├── asd.retail.all-0.0.1-cp2fm-converted-feature.zip
+            │       └── asd.retail.all-0.0.1.pom
+            ├── org.apache.sling.api
+            │   └── 2.20.0
+            │       ├── org.apache.sling.api-2.20.0.jar
+            │       └── org.apache.sling.api-2.20.0.pom
+            └── org.apache.sling.models.api
+                └── 1.3.8
+                    ├── org.apache.sling.models.api-1.3.8.jar
+                    └── org.apache.sling.models.api-1.3.8.pom
+
+12 directories, 8 files
+```
+
+_Apache Maven GAVs_ are extracted from nested bundles metadata and are renamed according to the _Apache Maven_ conventions.
+
+### Supported configurations
+
+All OSGi configuration formats are supported:
+
+ * _Property_ files, which extensions are `.properties` or `.cfg`, see the related [documentation](https://sling.apache.org/documentation/bundles/configuration-installer-factory.html#property-files-cfg);
+ * Configuration Files, which extension is `.config`, see the related [documentation](https://sling.apache.org/documentation/bundles/configuration-installer-factory.html#configuration-files-config);
+ * JSON format, which extension is `.cfg.json`, see the related [documentation](https://blog.osgi.org/2018/06/osgi-r7-highlights-configuration-admin.html)
+ * `sling:OsgiConfig` content nodes, typically `.xml` files.
+
+During the conversion process, all these formats will be parsed and then added in the `configuration` section of the _Sling Feature Model_ file.
+
+### Run Modes
+
+As shown above, run modes in the path lead the tool to create a dedicated _Apache Sling Feature_ model file containing all interested OSGi configurations/bundles.
+
+### Known limitations
+
+Multiple Run Modes are not supported yet.
+
+## Sample APIs
+
+```java
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+
+...
+
+new ContentPackage2FeatureModelConverter()
+            // content-package validation, when opening them
+            .setStrictValidation(strictValidation)
+            // (don't) allow different OSGi configurations file have the same PID
+            .setMergeConfigurations(mergeConfigurations)
+            // users can decide which is the bundles start order, declared in the generated Apache Sling Feature(s)
+            .setBundlesStartOrder(bundlesStartOrder)
+            // a valid directory where the outputs will be generated (it will created, if not existing already)
+            .setOutputDirectory(outputDirectory)
+            // an existing and valid content-package file
+            .convert(contentPackage);
+```
+
+### Handler Services
+
+In order to make the tool extensible, the [org.apache.sling.feature.cpconverter.spi.EntryHandler](./src/main/java/org/apache/sling/feature/cpconverter/spi/EntryHandler.java) interface is declared to handle different kind of resources, have a look at the [org.apache.sling.feature.cpconverter.handlers](src/main/java/org/apache/sling/feature/cpconverter/handlers) package to see the default implementations.
+
+If users want to handle special resource type, all they have to do is providing their `org.apache.sling.feature.cpconverter.spi.EntryHandler` service implementation and declaring them in the `META-INF/services/org.apache.sling.feature.cpconverter.spi.EntryHandler` classpath resource file, on order to let the `ServiceLoader` including it in the `content-package` scan.
+
+### Bundles deployer
+
+The [org.apache.sling.feature.cpconverter.spi.BundlesDeployer](./src/main/java/org/apache/sling/cp2fm/spi/BundlesDeployer) service is designed to let the conversion tool be integrated in external services, i.e. _Apache Maven_.
+
+The [default implementation](src/main/java/org/apache/sling/cp2fm/DefaultBundlesDeployer.java) just copies bundles in the target output directory, according to the _Apache Maven_ repository layout.
+
+## The CLI Tool
+
+The tool is distributed with a commodity package containing all is needed in order to launch the `ContentPackage2FeatureModelConverter` form the shell:
+
+```
+$ unzip -l org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT.zip 
+Archive:  org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT.zip
+  Length      Date    Time    Name
+---------  ---------- -----   ----
+        0  03-13-2019 15:58   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/
+        0  03-13-2019 15:58   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/bin/
+        0  03-13-2019 15:58   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/
+     4605  02-27-2019 16:30   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/README.md
+   801904  02-28-2019 14:55   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/jackrabbit-spi-commons-2.19.1.jar
+    14744  02-11-2019 15:44   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/osgi.annotation-6.0.1.jar
+    35919  02-11-2019 15:44   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/org.osgi.service.component.annotations-1.3.0.jar
+    23575  02-11-2019 15:44   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/org.osgi.service.metatype.annotations-1.3.0.jar
+    34518  02-27-2019 15:28   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/org.apache.felix.scr.annotations-1.11.0.jar
+    45199  03-13-2019 15:58   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT.jar
+    17489  03-13-2019 15:58   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/LICENSE
+   588337  02-11-2019 12:49   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/commons-collections-3.2.2.jar
+   108555  02-11-2019 15:45   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/xz-1.8.jar
+    52873  03-05-2019 17:31   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/plexus-classworlds-2.6.0.jar
+   165965  03-05-2019 18:02   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/maven-model-3.6.0.jar
+      178  02-27-2019 15:56   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/NOTICE
+   745712  02-28-2019 10:02   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/org.apache.jackrabbit.vault-3.2.6.jar
+  2374421  02-27-2019 15:28   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/biz.aQute.bndlib-3.2.0.jar
+     3263  03-13-2019 15:58   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/bin/cp2sf.bat
+    69246  02-11-2019 12:49   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/jcr-2.0.jar
+   113508  02-11-2019 12:36   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/org.apache.felix.converter-1.0.0.jar
+    12548  02-11-2019 12:36   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/org.osgi.util.function-1.0.0.jar
+   176142  02-11-2019 12:35   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/org.apache.felix.utils-1.11.0.jar
+   155618  03-04-2019 00:12   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/org.apache.felix.configadmin-1.9.12.jar
+    75443  03-05-2019 14:58   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/plexus-io-3.1.1.jar
+    57954  02-11-2019 12:39   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/snappy-0.4.jar
+   148098  02-11-2019 12:39   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/xbean-reflect-3.7.jar
+     3808  03-13-2019 15:58   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/bin/cp2sf
+   214788  02-11-2019 15:44   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/commons-io-2.6.jar
+    26081  02-11-2019 12:36   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/geronimo-json_1.0_spec-1.0-alpha-1.jar
+    90358  02-11-2019 12:35   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/johnzon-core-1.0.0.jar
+    14769  02-11-2019 12:35   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/org.osgi.annotation.versioning-1.0.0.jar
+   475256  02-11-2019 12:35   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/osgi.core-6.0.0.jar
+    28688  02-11-2019 12:48   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/slf4j-api-1.7.6.jar
+    28561  02-28-2019 14:55   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/jackrabbit-spi-2.19.1.jar
+   403186  02-28-2019 14:55   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/jackrabbit-jcr-commons-2.19.1.jar
+    49017  03-04-2019 15:12   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/jackrabbit-api-2.19.1.jar
+   260371  03-05-2019 14:58   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/plexus-utils-3.1.1.jar
+   639592  02-11-2019 12:39   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/google-collections-1.0.jar
+    10684  02-11-2019 12:48   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/slf4j-simple-1.7.6.jar
+   164159  02-11-2019 12:48   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/org.apache.sling.feature.io-1.0.0.jar
+   289040  02-11-2019 12:36   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/org.apache.felix.configurator-1.0.4.jar
+   591748  02-11-2019 15:45   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/commons-compress-1.18.jar
+   242435  02-27-2019 15:58   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/picocli-3.6.0.jar
+   115238  02-11-2019 12:48   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/org.apache.sling.feature-1.0.0.jar
+    18587  02-11-2019 15:46   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/annotations-16.0.3.jar
+   191914  03-05-2019 14:58   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/plexus-archiver-4.1.0.jar
+   229982  03-05-2019 17:31   org.apache.sling.feature.cpconverter-0.0.1-SNAPSHOT/lib/plexus-container-default-2.0.0.jar
+---------                     -------
+  9914076                     48 files
+```
+
+once the package is decompressed, open the shell and type:
+
+```
+$ ./bin/cp2sf -h
+Usage: cp2fm [-hmqsvX] [-b=<bundlesStartOrder>] -c=<contentPackage>
+             -o=<outputDirectory> [-f=<filteringPatterns>]...
+Apache Sling Content Package to Sling Feature converter
+  -b, --bundles-start-order=<bundlesStartOrder>
+                            The order to start detected bundles.
+  -c, --content-package=<contentPackage>
+                            The content-package input file.
+  -f, --filtering-patterns=<filteringPatterns>
+                            Regex based pattern(s) to reject content-package archive
+                              entries.
+  -h, --help                Display the usage message.
+  -m, --merge-configurations
+                            Flag to mark OSGi configurations with same PID will be
+                              merged, the tool will fail otherwise.
+  -o, --output-directory=<outputDirectory>
+                            The output directory where the Feature File and the
+                              bundles will be deployed.
+  -q, --quiet               Log errors only.
+  -s, --strict-validation   Flag to mark the content-package input file being strict
+                              validated.
+  -v, --version             Display version information.
+  -X, --verbose             Produce execution debug output.
+Copyright(c) 2019 The Apache Software Foundation.
+```
+
+to see all the available options; a sample execution could look like:
+
+```
+$ ./bin/cp2sf -v -b 20 -c /content-package-2-feature-model/src/test/resources/org/apache/sling/cp2fm/test-content-package.zip -o /tmp
+```
+
+Argument Files for Long Command Lines:
+
+```
+# argfile
+# comments are supported
+
+-v
+-b 20
+-c /content-package-2-feature-model/src/test/resources/org/apache/sling/cp2fm/test-content-package.zip
+-o /tmp
+```
+
+then execute the command
+
+```
+$ ./bin/cp2sf @arfile
+````
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..b7f697c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,322 @@
+<?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/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.sling</groupId>
+    <artifactId>sling</artifactId>
+    <version>34</version>
+    <relativePath />
+  </parent>
+
+  <artifactId>org.apache.sling.feature.cpconverter</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+
+  <name>Apache Sling Content Package to Feature Model converter</name>
+  <description>Content Package to Feature Model converter tools for Apache Sling</description>
+
+  <properties>
+    <sling.java.version>8</sling.java.version>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <dependencies>
+    <!--
+     | Logging
+    -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <!--
+     | CLI
+    -->
+    <dependency>
+      <groupId>info.picocli</groupId>
+      <artifactId>picocli</artifactId>
+      <version>3.6.0</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <!--
+     | Content-Package
+    -->
+    <dependency>
+      <groupId>org.apache.jackrabbit.vault</groupId>
+      <artifactId>org.apache.jackrabbit.vault</artifactId>
+      <version>3.2.6</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jackrabbit</groupId>
+      <artifactId>jackrabbit-spi-commons</artifactId>
+      <version>2.19.1</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.jcr</groupId>
+      <artifactId>jcr</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <version>2.6</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <!--
+     | Sling Feature Model libraries
+    -->
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.feature.io</artifactId>
+      <version>1.0.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.converter</artifactId>
+      <version>1.0.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.configurator</artifactId>
+      <version>1.0.4</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.utils</artifactId>
+      <version>1.11.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-json_1.0_spec</artifactId>
+      <version>1.0-alpha-1</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.johnzon</groupId>
+      <artifactId>johnzon-core</artifactId>
+      <version>1.0.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.feature</artifactId>
+      <version>1.0.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.annotation.versioning</artifactId>
+      <version>1.0.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <!--
+     | Handle .config files
+    -->
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.configadmin</artifactId>
+      <version>1.9.12</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jackrabbit</groupId>
+      <artifactId>jackrabbit-api</artifactId>
+      <version>2.19.1</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <!--
+     | repackage
+    -->
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-archiver</artifactId>
+      <version>4.1.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+      <version>3.1.1</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-io</artifactId>
+      <version>3.1.1</version>
+      <scope>provided</scope>
+    </dependency>
+    <!--
+     | avoid classloading issue
+    -->
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-container-default</artifactId>
+      <version>2.0.0</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <!--
+     | POM model
+    -->
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-model</artifactId>
+      <version>3.6.0</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <!--
+     | Test only dependencies
+    -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <version>2.25.0</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.rat</groupId>
+        <artifactId>apache-rat-plugin</artifactId>
+          <configuration>
+          <excludes>
+            <exclude>src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.spi.EntryHandler</exclude>
+            <exclude>src/main/legal/NOTICE-with-deps</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <systemPropertyVariables>
+            <testDirectory>${project.build.directory}/unit-tests</testDirectory>
+          </systemPropertyVariables>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>license-maven-plugin</artifactId>
+        <version>1.16</version>
+        <executions>
+          <execution>
+            <id>add-third-party</id>
+            <phase>generate-resources</phase>
+            <goals>
+              <goal>add-third-party</goal>
+            </goals>
+            <configuration>
+              <fileTemplate>${basedir}/src/main/legal/LICENSE-with-deps</fileTemplate>
+              <thirdPartyFilename>LICENSE-with-deps</thirdPartyFilename>
+              <sortArtifactByName>true</sortArtifactByName>
+              <excludedScopes>test</excludedScopes>
+              <licenseMerges>
+                <licenseMerge>The Apache Software License, Version 2.0|Apache License, Version 2.0|Apache Public License 2.0</licenseMerge>
+              </licenseMerges>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>appassembler-maven-plugin</artifactId>
+        <version>2.0.0</version>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>assemble</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <repositoryLayout>flat</repositoryLayout>
+          <repositoryName>lib</repositoryName>
+          <useWildcardClassPath>true</useWildcardClassPath>
+          <extraJvmArguments>-Dproject.artifactId=${project.artifactId} -Dproject.version=${project.version} -Dbuild.timestamp=${maven.build.timestamp} -Duser.timezone=UTC -Dfile.encoding=UTF-8</extraJvmArguments>
+          <defaultJvmSettings>-Xms500m -Xmx500m -XX:PermSize=128m -XX:-UseGCOverheadLimit</defaultJvmSettings>
+          <licenseHeaderFile>${basedir}/src/main/legal/license-header</licenseHeaderFile>
+          <programs>
+            <program>
+              <mainClass>org.apache.sling.feature.cpconverter.cli.ContentPackage2FeatureModelConverterLauncher</mainClass>
+              <name>cp2sf</name>
+            </program>
+          </programs>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <version>3.1.0</version>
+        <executions>
+          <execution>
+            <id>assembly</id>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <finalName>${project.build.finalName}</finalName>
+          <tarLongFileMode>gnu</tarLongFileMode>
+          <appendAssemblyId>false</appendAssemblyId>
+          <descriptors>
+            <descriptor>${basedir}/src/main/assembly/bin.xml</descriptor>
+          </descriptors>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/src/main/assembly/README.md b/src/main/assembly/README.md
new file mode 100644
index 0000000..4f743d5
--- /dev/null
+++ b/src/main/assembly/README.md
@@ -0,0 +1,149 @@
+```
+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.name} - ${project.inceptionYear}
+
+## What is it?
+
+  ${project.description}
+
+## Licensing
+
+  Please see the files called LICENSE and NOTICE
+
+## Documentation
+
+  The most up-to-date documentation can be found at ${project.url}.
+
+## Useful URLs
+
+  Home Page:          ${project.url}/
+  Source Code:        ${project.scm.url}
+  Issue Tracking:     ${project.issueManagement.url}
+
+## System Requirements
+
+### JDK
+
+    ${sling.java.version} or above. (see http://www.oracle.com/technetwork/java/)
+
+### Memory
+
+    No minimum requirement.
+
+### Disk
+
+    No minimum requirement.
+
+###  Operating System
+
+    No minimum requirement. On Windows, Windows NT and above or Cygwin is required for
+    the startup scripts. Tested on Windows XP, Fedora Core and Mac OS X.
+
+---
+
+## Installation
+
+### Windows 2000/XP
+
+  1) Unzip the distribution archive, i.e. `${project.build.finalName}.zip` to the directory you wish to install `${project.name} ${project.version}`.
+These instructions assume you chose `C:\Program Files`.
+The subdirectory `${project.build.finalName}` will be created from the archive.
+
+  2) Add the `SFA_HOME` environment variable by opening up the system properties (WinKey + Pause), selecting the "Advanced" tab, and the "Environment Variables" button, then adding the `SFA_HOME` variable in the user variables with the value `C:\Program Files\${project.build.finalName}`.
+
+  3) In the same dialog, add the SFA environment variable in the user variables with the value `%SFA_HOME%\bin`.
+
+  4) In the same dialog, update/create the _Path_ environment variable in the user variables and prepend the value `%SFA%` to add `${project.name}` available in the command line.
+
+  5) In the same dialog, make sure that `JAVA_HOME` exists in your user variables or in the system variables and it is set to the location of your JDK, e.g. `C:\Program Files\Java\1.8.0_152` and that `%JAVA_HOME%\bin` is in your _Path_ environment variable.
+
+  6) Open a new command prompt (Winkey + R then type cmd) and run `sfa --version` to verify that it is correctly installed.
+
+## Unix-based Operating Systems (Linux, Solaris and Mac OS X)
+
+  1) Extract the distribution archive, i.e. `${project.build.finalName}.tar.gz` to the directory you wish to install `${project.name} ${project.version}`.
+These instructions assume you chose `/usr/local`.
+The subdirectory `${project.build.finalName}` will be created from the archive.
+
+  2) In a command terminal, add the `SFA_HOME` environment variable, e.g.
+        `export SFA_HOME=/usr/local/${project.build.finalName}`.
+
+  3) Add the `SFA` environment variable, e.g. `export SFA=$SFA_HOME/bin`.
+
+  4) Add `SFA` environment variable to your path, e.g. `export PATH=$SFA:$PATH`.
+
+  5) Make sure that `JAVA_HOME` is set to the location of your JDK, e.g. `export JAVA_HOME=/usr/java/1.8.0_152` and that `$JAVA_HOME/bin` is in your `PATH` environment variable.
+
+  6) Run `sfa --version` to verify that it is correctly installed.
+
+---
+
+## Execution
+
+  Open the shell and type `cp2sf -h` to see the available commands:
+
+```
+$ ./cp2sf -h
+Usage: cp2fm [-hmqsvX] [-b=<bundlesStartOrder>] -c=<contentPackage>
+             -o=<outputDirectory> [-f=<filteringPatterns>]...
+Apache Sling Content Package to Sling Feature converter
+  -b, --bundles-start-order=<bundlesStartOrder>
+                            The order to start detected bundles.
+  -c, --content-package=<contentPackage>
+                            The content-package input file.
+  -f, --filtering-patterns=<filteringPatterns>
+                            Regex based pattern(s) to reject content-package archive
+                              entries.
+  -h, --help                Display the usage message.
+  -m, --merge-configurations
+                            Flag to mark OSGi configurations with same PID will be
+                              merged, the tool will fail otherwise.
+  -o, --output-directory=<outputDirectory>
+                            The output directory where the Feature File and the
+                              bundles will be deployed.
+  -q, --quiet               Log errors only.
+  -s, --strict-validation   Flag to mark the content-package input file being strict
+                              validated.
+  -v, --version             Display version information.
+  -X, --verbose             Produce execution debug output.
+Copyright(c) 2019 The Apache Software Foundation.
+```
+
+a sample execution could look like:
+
+```
+$ ./bin/cp2sf -v -b 20 -c /content-package-2-feature-model/src/test/resources/org/apache/sling/cp2fm/test-content-package.zip -o /tmp
+```
+
+### Argument Files for Long Command Lines
+
+```
+# argfile
+# comments are supported
+
+-v
+-b 20
+-c /content-package-2-feature-model/src/test/resources/org/apache/sling/cp2fm/test-content-package.zip
+-o /tmp
+```
+
+then execute the command
+
+```
+$ ./bin/cp2sf @arfile
+````
diff --git a/src/main/assembly/bin.xml b/src/main/assembly/bin.xml
new file mode 100644
index 0000000..482f4cd
--- /dev/null
+++ b/src/main/assembly/bin.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with this
+ work for additional information regarding copyright ownership. The ASF
+ licenses this file to You under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations under
+ the License.
+-->
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1 http://maven.apache.org/xsd/assembly-1.1.1.xsd">
+
+  <id>${project.build.finalName}</id>
+  <formats>
+    <format>tar.gz</format>
+    <format>zip</format>
+  </formats>
+  <includeBaseDirectory>true</includeBaseDirectory>
+  <baseDirectory>${project.build.finalName}</baseDirectory>
+
+  <files>
+    <file>
+      <source>${basedir}/src/main/assembly/README.md</source>
+      <filtered>true</filtered>
+      <outputDirectory>/</outputDirectory>
+      <fileMode>666</fileMode>
+    </file>
+
+    <!--
+     | Use the N&L files which apply to the included dependencies
+    -->
+    <file>
+      <source>${project.build.directory}/generated-sources/license/LICENSE-with-deps</source>
+      <destName>LICENSE</destName>
+    </file>
+    <file>
+      <source>${basedir}/src/main/legal/NOTICE-with-deps</source>
+      <destName>NOTICE</destName>
+    </file>
+  </files>
+
+  <fileSets>
+    <!--
+     | shell scripts
+    -->
+    <fileSet>
+      <directory>${project.build.directory}/appassembler/bin/</directory>
+      <outputDirectory>/bin</outputDirectory>
+      <fileMode>755</fileMode>
+      <directoryMode>755</directoryMode>
+    </fileSet>
+  </fileSets>
+
+  <dependencySets>
+    <dependencySet>
+      <outputDirectory>/lib</outputDirectory>
+      <scope>provided</scope>
+      <useTransitiveDependencies>true</useTransitiveDependencies>
+      <useProjectArtifact>true</useProjectArtifact>
+    </dependencySet>
+  </dependencySets>
+
+</assembly>
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java b/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
new file mode 100644
index 0000000..32c0197
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
@@ -0,0 +1,431 @@
+/*
+ * 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.cpconverter;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.ServiceLoader;
+import java.util.Set;
+
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.jackrabbit.vault.packaging.Dependency;
+import org.apache.jackrabbit.vault.packaging.PackageManager;
+import org.apache.jackrabbit.vault.packaging.PackageProperties;
+import org.apache.jackrabbit.vault.packaging.VaultPackage;
+import org.apache.jackrabbit.vault.packaging.impl.PackageManagerImpl;
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Configuration;
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionType;
+import org.apache.sling.feature.Extensions;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.cpconverter.spi.BundlesDeployer;
+import org.apache.sling.feature.cpconverter.spi.EntryHandler;
+import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
+import org.apache.sling.feature.cpconverter.writers.FileArtifactWriter;
+import org.apache.sling.feature.cpconverter.writers.MavenPomSupplierWriter;
+import org.apache.sling.feature.io.json.FeatureJSONWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ContentPackage2FeatureModelConverter {
+
+    private static final String CONTENT_PACKAGES = "content-packages";
+
+    public static final String POM_TYPE = "pom";
+
+    public static final String ZIP_TYPE = "zip";
+
+    public static final String NAME_GROUP_ID = "groupId";
+
+    public static final String NAME_ARTIFACT_ID = "artifactId";
+
+    public static final String FEATURE_CLASSIFIER = "cp2fm-converted-feature";
+
+    private static final String SLING_OSGI_FEATURE_TILE_TYPE = "slingosgifeature";
+
+    private static final String JSON_FILE_EXTENSION = ".json";
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private final PackageManager packageManager = new PackageManagerImpl();
+
+    private final ServiceLoader<EntryHandler> entryHandlers = ServiceLoader.load(EntryHandler.class);
+
+    private final Map<String, Feature> runModes = new HashMap<>();
+
+    private final Set<String> dependencies = new HashSet<>();
+
+    private final RegexBasedResourceFilter filter = new RegexBasedResourceFilter();
+
+    private BundlesDeployer artifactDeployer;
+
+    private boolean strictValidation = false;
+
+    private boolean mergeConfigurations = false;
+
+    private int bundlesStartOrder = 0;
+
+    private File outputDirectory;
+
+    private Feature targetFeature = null;
+
+    private VaultPackageAssembler mainPackageAssembler = null;
+
+    public ContentPackage2FeatureModelConverter setStrictValidation(boolean strictValidation) {
+        this.strictValidation = strictValidation;
+        return this;
+    }
+
+    public boolean isStrictValidation() {
+        return strictValidation;
+    }
+
+    public boolean isMergeConfigurations() {
+        return mergeConfigurations;
+    }
+
+    public ContentPackage2FeatureModelConverter setMergeConfigurations(boolean mergeConfigurations) {
+        this.mergeConfigurations = mergeConfigurations;
+        return this;
+    }
+
+    public ContentPackage2FeatureModelConverter setBundlesStartOrder(int bundlesStartOrder) {
+        this.bundlesStartOrder = bundlesStartOrder;
+        return this;
+    }
+
+    public ContentPackage2FeatureModelConverter setOutputDirectory(File outputDirectory) {
+        this.outputDirectory = outputDirectory;
+        return this;
+    }
+
+    public File getOutputDirectory() {
+        return outputDirectory;
+    }
+
+    public Feature getTargetFeature() {
+        return targetFeature;
+    }
+
+    public void addFilteringPattern(String filteringPattern) {
+        Objects.requireNonNull(filteringPattern, "Null pattern to filter resources out is not a valid filtering pattern");
+        if (filteringPattern.isEmpty()) {
+            throw new IllegalArgumentException("Empty pattern to filter resources out is not a valid filtering pattern");
+        }
+
+        filter.addFilteringPattern(filteringPattern);
+    }
+
+    public Feature getRunMode(String runMode) {
+        if (getTargetFeature() == null) {
+            throw new IllegalStateException("Target Feature not initialized yet, please make sure convert() method was invoked first.");
+        }
+
+        if (runMode == null) {
+            return getTargetFeature();
+        }
+
+        ArtifactId id = getTargetFeature().getId();
+
+        return runModes.computeIfAbsent(runMode, k -> new Feature(new ArtifactId(id.getGroupId(),
+                                                                                 id.getArtifactId(),
+                                                                                 id.getVersion(),
+                                                                                 id.getClassifier() + '-' + runMode,
+                                                                                 id.getType())));
+    }
+
+    public BundlesDeployer getArtifactDeployer() {
+        return artifactDeployer;
+    }
+
+    public void convert(File contentPackage) throws Exception {
+        Objects.requireNonNull(contentPackage , "Null content-package can not be converted.");
+
+        if (!contentPackage.exists() || !contentPackage.isFile()) {
+            throw new IllegalArgumentException("Content-package "
+                                            + contentPackage
+                                            + " does not exist or it is not a valid file.");
+        }
+
+        if (outputDirectory == null) {
+            throw new IllegalStateException("Null output directory not supported, it must be set before invoking the convert(File) method.");
+        }
+
+        Iterator<BundlesDeployer> artifactDeployerLoader = ServiceLoader.load(BundlesDeployer.class).iterator();
+        if (!artifactDeployerLoader.hasNext()) {
+            artifactDeployer = new DefaultBundlesDeployer(outputDirectory);
+        } else {
+            artifactDeployer = artifactDeployerLoader.next();
+        }
+
+        if (!outputDirectory.exists() && !outputDirectory.mkdirs()) {
+            throw new IllegalStateException("output directory "
+                                            + outputDirectory
+                                            + " does not exist and can not be created, please make sure current user '"
+                                            + System.getProperty("user.name")
+                                            + " has enough rights to write on the File System.");
+        }
+
+        logger.info("Reading content-package '{}'...", contentPackage);
+
+        try (VaultPackage vaultPackage = packageManager.open(contentPackage, strictValidation)) {
+            logger.info("content-package '{}' successfully read!", contentPackage);
+
+            mainPackageAssembler = VaultPackageAssembler.create(vaultPackage);
+
+            PackageProperties packageProperties = vaultPackage.getProperties();
+            String groupId = packageProperties.getProperty(NAME_GROUP_ID);
+            String artifactId = packageProperties.getProperty(NAME_ARTIFACT_ID);
+            String version = packageProperties.getProperty(PackageProperties.NAME_VERSION);
+
+            targetFeature = new Feature(new ArtifactId(groupId,
+                                                       artifactId,
+                                                       version,
+                                                       FEATURE_CLASSIFIER,
+                                                       SLING_OSGI_FEATURE_TILE_TYPE));
+
+            targetFeature.setDescription(packageProperties.getDescription());
+
+            logger.info("Converting content-package '{}' to Feature File '{}'...", vaultPackage.getId(), targetFeature.getId());
+
+            process(vaultPackage);
+
+            // attach all unmatched resources as new content-package
+
+            File contentPackageArchive = mainPackageAssembler.createPackage(outputDirectory);
+
+            // deploy the new zip content-package to the local mvn bundles dir
+
+            artifactDeployer.deploy(new FileArtifactWriter(contentPackageArchive),
+                                                           targetFeature.getId().getGroupId(),
+                                                           targetFeature.getId().getArtifactId(),
+                                                           targetFeature.getId().getVersion(),
+                                                           FEATURE_CLASSIFIER,
+                                                           ZIP_TYPE);
+
+            artifactDeployer.deploy(new MavenPomSupplierWriter(targetFeature.getId().getGroupId(),
+                                                               targetFeature.getId().getArtifactId(),
+                                                               targetFeature.getId().getVersion(),
+                                                               ZIP_TYPE),
+                                    targetFeature.getId().getGroupId(),
+                                    targetFeature.getId().getArtifactId(),
+                                    targetFeature.getId().getVersion(),
+                                    null,
+                                    POM_TYPE);
+
+            attach(null,
+                   targetFeature.getId().getGroupId(),
+                   targetFeature.getId().getArtifactId(),
+                   targetFeature.getId().getVersion(),
+                   FEATURE_CLASSIFIER,
+                   ZIP_TYPE);
+
+            // finally serialize the Feature Model(s) file(s)
+
+            seralize(targetFeature);
+
+            if (!runModes.isEmpty()) {
+                for (Feature runMode : runModes.values()) {
+                    seralize(runMode);
+                }
+            }
+        }
+    }
+
+    public void addConfiguration(String runMode, String pid, Dictionary<String, Object> configurationProperties) {
+        if (!mergeConfigurations) {
+            checkConfigurationExist(getTargetFeature(), pid);
+
+            for (Feature runModeFeature : runModes.values()) {
+                checkConfigurationExist(runModeFeature, pid);
+            }
+        }
+
+        Feature feature = getRunMode(runMode);
+        Configuration configuration = feature.getConfigurations().getConfiguration(pid);
+
+        if (configuration == null) {
+            configuration = new Configuration(pid);
+            feature.getConfigurations().add(configuration);
+        }
+
+        Enumeration<String> keys = configurationProperties.keys();
+        while (keys.hasMoreElements()) {
+            String key = keys.nextElement();
+            Object value = configurationProperties.get(key);
+            configuration.getProperties().put(key, value);
+        }
+    }
+
+    private static void checkConfigurationExist(Feature feature, String pid) {
+        if (feature != null) {
+            if (feature.getConfigurations().getConfiguration(pid) != null) {
+                throw new IllegalStateException("Cinfiguration '"
+                                                + pid
+                                                + "' already defined in Feature Model '"
+                                                + feature.getId().toMvnId()
+                                                + "', can not be added");
+            }
+        }
+    }
+
+    private void seralize(Feature feature) throws Exception {
+        StringBuilder fileName = new StringBuilder().append(feature.getId().getArtifactId());
+
+        if (!FEATURE_CLASSIFIER.equals(feature.getId().getClassifier())) {
+            fileName.append(feature.getId().getClassifier().substring(FEATURE_CLASSIFIER.length()));
+        }
+
+        fileName.append(JSON_FILE_EXTENSION);
+
+        File targetFile = new File(outputDirectory, fileName.toString());
+
+        logger.info("Conversion complete!", targetFile);
+        logger.info("Writing resulting Feature File to '{}'...", targetFile);
+
+        try (FileWriter targetWriter = new FileWriter(targetFile)) {
+            FeatureJSONWriter.write(targetWriter, feature);
+
+            logger.info("'{}' Feature File successfully written!", targetFile);
+        }
+    }
+
+    public void processSubPackage(String path, File contentPackage) throws Exception {
+        Objects.requireNonNull(path, "Impossible to process a null vault package");
+        Objects.requireNonNull(contentPackage, "Impossible to process a null vault package");
+
+        try (VaultPackage vaultPackage = packageManager.open(contentPackage, strictValidation)) {
+            process(vaultPackage);
+
+            File clonedPackage = VaultPackageAssembler.create(vaultPackage).createPackage();
+            mainPackageAssembler.addEntry(path, clonedPackage);
+        }
+    }
+
+    private void process(VaultPackage vaultPackage) throws Exception {
+        Objects.requireNonNull(vaultPackage, "Impossible to process a null vault package");
+
+        if (getTargetFeature() == null) {
+            throw new IllegalStateException("Target Feature not initialized yet, please make sure convert() method was invoked first.");
+        }
+
+        dependencies.remove(vaultPackage.getId().toString());
+
+        for (Dependency dependency : vaultPackage.getDependencies()) {
+            dependencies.add(dependency.toString());
+        }
+
+        Archive archive = vaultPackage.getArchive();
+        try {
+            archive.open(strictValidation);
+
+            Entry jcrRoot = archive.getJcrRoot();
+            traverse(null, archive, jcrRoot);
+        } finally {
+            archive.close();
+        }
+    }
+
+    private void traverse(String path, Archive archive, Entry entry) throws Exception {
+        String entryPath = newPath(path, entry.getName());
+
+        if (entry.isDirectory()) {
+            for (Entry child : entry.getChildren()) {
+                traverse(entryPath, archive, child);
+            }
+
+            return;
+        }
+
+        logger.info("Processing entry {}...", entryPath);
+
+        if (filter.isFilteredOut(entryPath)) {
+            throw new IllegalArgumentException("Path '"
+                                               + entryPath
+                                               + "' in archive "
+                                               + archive.getMetaInf().getProperties()
+                                               + " not allowed by user configuration, please check configured filtering patterns");
+        }
+
+        getEntryHandlerByEntryPath(entryPath).handle(entryPath, archive, entry, this);
+
+        logger.info("Entry {} successfully processed.", entryPath);
+    }
+
+    private static String newPath(String path, String entryName) {
+        if (path == null) {
+            return entryName;
+        }
+
+        return path + '/' + entryName;
+    }
+
+    private EntryHandler getEntryHandlerByEntryPath(String path) {
+        Iterator<EntryHandler> entryHandlersIterator = entryHandlers.iterator();
+        while (entryHandlersIterator.hasNext()) {
+            EntryHandler entryHandler = entryHandlersIterator.next();
+
+            if (entryHandler.matches(path)) {
+                return entryHandler;
+            }
+        }
+
+        return mainPackageAssembler;
+    }
+
+    public void attach(String runMode,
+                       String groupId,
+                       String artifactId,
+                       String version,
+                       String classifier,
+                       String type) {
+        Objects.requireNonNull(groupId, "Artifact can not be attached to a feature without specifying a valid 'groupId'.");
+        Objects.requireNonNull(artifactId, "Artifact can not be attached to a feature without specifying a valid 'artifactId'.");
+        Objects.requireNonNull(version, "Artifact can not be attached to a feature without specifying a valid 'version'.");
+        Objects.requireNonNull(type, "Artifact can not be attached to a feature without specifying a valid 'type'.");
+
+        Artifact artifact = new Artifact(new ArtifactId(groupId, artifactId, version, classifier, type));
+
+        Feature targetFeature = getRunMode(runMode);
+
+        if (ZIP_TYPE.equals(type) ) {
+            Extensions extensions = targetFeature.getExtensions();
+            Extension extension = extensions.getByName(CONTENT_PACKAGES);
+
+            if (extension == null) {
+                extension = new Extension(ExtensionType.ARTIFACTS, CONTENT_PACKAGES, true);
+                extensions.add(extension);
+            }
+
+            extension.getArtifacts().add(artifact);
+        } else {
+            artifact.setStartOrder(bundlesStartOrder);
+            targetFeature.getBundles().add(artifact);
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/DefaultBundlesDeployer.java b/src/main/java/org/apache/sling/feature/cpconverter/DefaultBundlesDeployer.java
new file mode 100644
index 0000000..780aed8
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/DefaultBundlesDeployer.java
@@ -0,0 +1,99 @@
+/*
+ * 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.cpconverter;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+import java.util.StringTokenizer;
+
+import org.apache.sling.feature.cpconverter.spi.ArtifactWriter;
+import org.apache.sling.feature.cpconverter.spi.BundlesDeployer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class DefaultBundlesDeployer implements BundlesDeployer {
+
+    private static final String BUNDLES_RIRECTORY_NAME = "bundles";
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private final File artifactsDirectory;
+
+    public DefaultBundlesDeployer(File outputDirectory) {
+        artifactsDirectory = new File(outputDirectory, BUNDLES_RIRECTORY_NAME);
+        if (!artifactsDirectory.exists()) {
+            artifactsDirectory.mkdirs();
+        }
+    }
+
+    @Override
+    public File getBundlesDirectory() {
+        return artifactsDirectory;
+    }
+
+    @Override
+    public void deploy(ArtifactWriter artifactWriter,
+                              String groupId,
+                              String artifactId,
+                              String version,
+                              String classifier,
+                              String type) throws IOException {
+        Objects.requireNonNull(artifactWriter, "Null ArtifactWriter can not install an artifact to a Maven repository.");
+        Objects.requireNonNull(groupId, "Bundle can not be installed to a Maven repository without specifying a valid 'groupId'.");
+        Objects.requireNonNull(artifactId, "Bundle can not be installed to a Maven repository without specifying a valid 'artifactId'.");
+        Objects.requireNonNull(version, "Bundle can not be installed to a Maven repository without specifying a valid 'version'.");
+        Objects.requireNonNull(type, "Bundle can not be installed to a Maven repository without specifying a valid 'type'.");
+
+        File targetDir = artifactsDirectory;
+
+        StringTokenizer tokenizer = new StringTokenizer(groupId, ".");
+        while (tokenizer.hasMoreTokens()) {
+            String current = tokenizer.nextToken();
+            targetDir = new File(targetDir, current);
+        }
+
+        targetDir = new File(targetDir, artifactId);
+
+        targetDir = new File(targetDir, version);
+
+        targetDir.mkdirs();
+
+        StringBuilder nameBuilder = new StringBuilder()
+                                    .append(artifactId)
+                                    .append('-')
+                                    .append(version);
+
+        if (classifier != null) {
+            nameBuilder.append('-').append(classifier);
+        }
+
+        nameBuilder.append('.').append(type);
+
+        File targetFile = new File(targetDir, nameBuilder.toString());
+
+        logger.info("Writing data to {}...", targetFile);
+
+        try (FileOutputStream targetStream = new FileOutputStream(targetFile)) {
+            artifactWriter.write(targetStream);
+        }
+
+        logger.info("Data successfully written to {}.", targetFile);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/RegexBasedResourceFilter.java b/src/main/java/org/apache/sling/feature/cpconverter/RegexBasedResourceFilter.java
new file mode 100644
index 0000000..9535acf
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/RegexBasedResourceFilter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.cpconverter;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class RegexBasedResourceFilter {
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private final List<Pattern> patterns = new LinkedList<>();
+
+    public void addFilteringPattern(String filteringPattern) {
+        patterns.add(Pattern.compile(filteringPattern));
+    }
+
+    public boolean isFilteredOut(String path) {
+        for (Pattern pattern : patterns) {
+            logger.debug("Checking if path '{}' matches against '{}' pattern...", path, pattern);
+
+            if (pattern.matcher(path).matches()) {
+                logger.debug("Path '{}' matches against '{}' pattern.", path, pattern);
+
+                return true;
+            } else {
+                logger.debug("Path '{}' does not matches against '{}' pattern.", path, pattern);
+            }
+        }
+
+        logger.debug("Path '{}' does not match against any configured pattern.", path);
+
+        return false;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java b/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java
new file mode 100644
index 0000000..7aa7c50
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java
@@ -0,0 +1,186 @@
+/*
+ * 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.cpconverter.cli;
+
+import java.io.File;
+import java.util.TimeZone;
+
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+@Command(
+    name = "cp2fm",
+    description = "Apache Sling Content Package to Sling Feature converter",
+    footer = "Copyright(c) 2019 The Apache Software Foundation."
+)
+public final class ContentPackage2FeatureModelConverterLauncher implements Runnable {
+
+    @Option(names = { "-h", "--help" }, usageHelp = true, description = "Display the usage message.")
+    private boolean helpRequested;
+
+    @Option(names = { "-X", "--verbose" }, description = "Produce execution debug output.")
+    private boolean debug;
+
+    @Option(names = { "-q", "--quiet" }, description = "Log errors only.")
+    private boolean quiet;
+
+    @Option(names = { "-v", "--version" }, description = "Display version information.")
+    private boolean printVersion;
+
+    @Option(names = { "-c", "--content-package" }, description = "The content-package input file.", required = true)
+    private File contentPackage;
+
+    @Option(names = { "-s", "--strict-validation" }, description = "Flag to mark the content-package input file being strict validated.", required = false, defaultValue = "false")
+    private boolean strictValidation = false;
+
+    @Option(names = { "-m", "--merge-configurations" }, description = "Flag to mark OSGi configurations with same PID will be merged, the tool will fail otherwise.", required = false, defaultValue = "false")
+    private boolean mergeConfigurations = false;
+
+    @Option(names = { "-b", "--bundles-start-order" }, description = "The order to start detected bundles.", required = false)
+    private int bundlesStartOrder = 0;
+
+    @Option(names = { "-f", "--filtering-patterns" }, description = "Regex based pattern(s) to reject content-package archive entries.", required = false)
+    private String[] filteringPatterns;
+
+    @Option(names = { "-o", "--output-directory" }, description = "The output directory where the Feature File and the bundles will be deployed.", required = true)
+    private File outputDirectory;
+
+    @Override
+    public void run() {
+        if (quiet) {
+            System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "error");
+        } else if (debug) {
+            System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug");
+        } else {
+            System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "info");
+        }
+        System.setProperty("org.slf4j.simpleLogger.showThreadName", "false");
+        System.setProperty("org.slf4j.simpleLogger.levelInBrackets", "true");
+        System.setProperty("org.slf4j.simpleLogger.showLogName", "false");
+
+        String appName = getClass().getAnnotation(Command.class).description()[0];
+        final Logger logger = LoggerFactory.getLogger(appName);
+
+        // Add the Shutdown Hook to the Java virtual machine
+        // in order to destroy all the allocated resources
+        Runtime.getRuntime().addShutdownHook(new ShutDownHook(logger));
+
+        if (printVersion) {
+            printVersion(logger);
+        }
+
+        logger.info(appName);
+        logger.info("");
+
+        try {
+            ContentPackage2FeatureModelConverter converter = new ContentPackage2FeatureModelConverter()
+                                                             .setStrictValidation(strictValidation)
+                                                             .setMergeConfigurations(mergeConfigurations)
+                                                             .setBundlesStartOrder(bundlesStartOrder)
+                                                             .setOutputDirectory(outputDirectory);
+
+            if (filteringPatterns != null && filteringPatterns.length > 0) {
+                for (String filteringPattern : filteringPatterns) {
+                    converter.addFilteringPattern(filteringPattern);
+                }
+            }
+
+            converter.convert(contentPackage);
+
+            logger.info( "+-----------------------------------------------------+" );
+            logger.info("{} SUCCESS", appName);
+        } catch (Throwable t) {
+            logger.info( "+-----------------------------------------------------+" );
+            logger.info("{} FAILURE", appName);
+            logger.info( "+-----------------------------------------------------+" );
+
+            if (debug) {
+                logger.error("Unable to convert content-package {}:", contentPackage, t);
+            } else {
+                logger.error("Unable to convert content-package {}: {}", contentPackage, t.getMessage());
+            }
+
+            logger.info( "" );
+
+            System.exit(1);
+        }
+
+        logger.info( "+-----------------------------------------------------+" );
+    }
+
+    private static void printVersion(final Logger logger) {
+        logger.info("{} v{} (built on {})",
+                System.getProperty("project.artifactId"),
+                System.getProperty("project.version"),
+                System.getProperty("build.timestamp"));
+        logger.info("Java version: {}, vendor: {}",
+                System.getProperty("java.version"),
+                System.getProperty("java.vendor"));
+        logger.info("Java home: {}", System.getProperty("java.home"));
+        logger.info("Default locale: {}_{}, platform encoding: {}",
+                System.getProperty("user.language"),
+                System.getProperty("user.country"),
+                System.getProperty("sun.jnu.encoding"));
+        logger.info("Default Time Zone: {}", TimeZone.getDefault().getDisplayName());
+        logger.info("OS name: \"{}\", version: \"{}\", arch: \"{}\", family: \"{}\"",
+                System.getProperty("os.name"),
+                System.getProperty("os.version"),
+                System.getProperty("os.arch"),
+                getOsFamily());
+        logger.info("+-----------------------------------------------------+");
+    }
+
+    private static final String getOsFamily() {
+        String osName = System.getProperty("os.name").toLowerCase();
+        String pathSep = System.getProperty("path.separator");
+
+        if (osName.indexOf("windows") != -1) {
+            return "windows";
+        } else if (osName.indexOf("os/2") != -1) {
+            return "os/2";
+        } else if (osName.indexOf("z/os") != -1 || osName.indexOf("os/390") != -1) {
+            return "z/os";
+        } else if (osName.indexOf("os/400") != -1) {
+            return "os/400";
+        } else if (pathSep.equals(";")) {
+            return "dos";
+        } else if (osName.indexOf("mac") != -1) {
+            if (osName.endsWith("x")) {
+                return "mac"; // MACOSX
+            }
+            return "unix";
+        } else if (osName.indexOf("nonstop_kernel") != -1) {
+            return "tandem";
+        } else if (osName.indexOf("openvms") != -1) {
+            return "openvms";
+        } else if (pathSep.equals(":")) {
+            return "unix";
+        }
+
+        return "undefined";
+    }
+
+    public static void main(String[] args) {
+        CommandLine.run(new ContentPackage2FeatureModelConverterLauncher(), args);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/cli/ShutDownHook.java b/src/main/java/org/apache/sling/feature/cpconverter/cli/ShutDownHook.java
new file mode 100644
index 0000000..0c5d2fc
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/cli/ShutDownHook.java
@@ -0,0 +1,81 @@
+/*
+ * 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.cpconverter.cli;
+
+import java.util.Date;
+import java.util.Formatter;
+
+import org.slf4j.Logger;
+
+final class ShutDownHook extends Thread {
+
+    private final long start = System.currentTimeMillis();
+
+    private final Logger logger;
+
+    public ShutDownHook(Logger logger) {
+        this.logger = logger;
+    }
+
+    @Override
+    public void run() {
+        logger.info("+-----------------------------------------------------+");
+        logger.info("");
+
+        // format the uptime string
+        Formatter uptimeFormatter = new Formatter();
+        uptimeFormatter.format("Total time:");
+
+        long uptime = System.currentTimeMillis() - start;
+        if (uptime < 1000) {
+            uptimeFormatter.format(" %s millisecond%s", uptime, (uptime > 1 ? "s" : ""));
+        } else {
+            long uptimeInSeconds = (uptime) / 1000;
+            final long hours = uptimeInSeconds / 3600;
+
+            if (hours > 0) {
+                uptimeFormatter.format(" %s hour%s", hours, (hours > 1 ? "s" : ""));
+            }
+
+            uptimeInSeconds = uptimeInSeconds - (hours * 3600);
+            final long minutes = uptimeInSeconds / 60;
+
+            if (minutes > 0) {
+                uptimeFormatter.format(" %s minute%s", minutes, (minutes > 1 ? "s" : ""));
+            }
+
+            uptimeInSeconds = uptimeInSeconds - (minutes * 60);
+
+            if (uptimeInSeconds > 0) {
+                uptimeFormatter.format(" %s second%s", uptimeInSeconds, (uptimeInSeconds > 1 ? "s" : ""));
+            }
+        }
+        logger.info(uptimeFormatter.toString());
+
+        uptimeFormatter.close();
+
+        logger.info("Finished at: {}", new Date());
+
+        final Runtime runtime = Runtime.getRuntime();
+        final int megaUnit = 1024 * 1024;
+
+        logger.info("Final Memory: {}M/{}M",
+                    (runtime.totalMemory() - runtime.freeMemory()) / megaUnit,
+                    runtime.totalMemory() / megaUnit);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/cli/package-info.java b/src/main/java/org/apache/sling/feature/cpconverter/cli/package-info.java
new file mode 100644
index 0000000..63be681
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/cli/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * CLI implementation for the converter.
+ */
+package org.apache.sling.feature.cpconverter.cli;
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractConfigurationEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractConfigurationEntryHandler.java
new file mode 100644
index 0000000..99ea954
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractConfigurationEntryHandler.java
@@ -0,0 +1,68 @@
+/*
+ * 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.cpconverter.handlers;
+
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.regex.Matcher;
+
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+
+abstract class AbstractConfigurationEntryHandler extends AbstractRegexEntryHandler {
+
+    public AbstractConfigurationEntryHandler(String extension) {
+        super("(jcr_root)?/apps/[^/]+/config(\\.([^/]+))?/.+\\." + extension);
+    }
+
+    @Override
+    public final void handle(String path, Archive archive, Entry entry, ContentPackage2FeatureModelConverter converter) throws Exception {
+        String pid = entry.getName().substring(0, entry.getName().lastIndexOf('.'));
+
+        logger.info("Processing configuration '{}'.", pid);
+
+        Dictionary<String, Object> configurationProperties;
+        try (InputStream input = archive.openInputStream(entry)) {
+            configurationProperties = parseConfiguration(pid, input);
+        }
+
+        if (configurationProperties.isEmpty()) {
+            logger.info("No configuration properties found for configuration {}", path);
+            return;
+        }
+
+        Matcher matcher = getPattern().matcher(path);
+        String runMode = null;
+        // we are pretty sure it matches, here
+        if (matcher.matches()) {
+            // there is a specified RunMode
+            runMode = matcher.group(3);
+        } else {
+            throw new IllegalStateException("Something went terribly wrong: pattern '"
+                                            + getPattern().pattern()
+                                            + "' should have matched already with path '"
+                                            + path
+                                            + "' but it does not, currently");
+        }
+
+        converter.addConfiguration(runMode, pid, configurationProperties);
+    }
+
+    protected abstract Dictionary<String, Object> parseConfiguration(String name, InputStream input) throws Exception;
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractRegexEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractRegexEntryHandler.java
new file mode 100644
index 0000000..f34d866
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractRegexEntryHandler.java
@@ -0,0 +1,44 @@
+/*
+ * 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.cpconverter.handlers;
+
+import java.util.regex.Pattern;
+
+import org.apache.sling.feature.cpconverter.spi.EntryHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractRegexEntryHandler implements EntryHandler {
+
+    protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private final Pattern pattern;
+
+    public AbstractRegexEntryHandler(String regex) {
+        pattern = Pattern.compile(regex);
+    }
+
+    @Override
+    public final boolean matches(String path) {
+        return pattern.matcher(path).matches();
+    }
+
+    protected final Pattern getPattern() {
+        return pattern;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandler.java
new file mode 100644
index 0000000..884bf06
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandler.java
@@ -0,0 +1,136 @@
+/*
+ * 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.cpconverter.handlers;
+
+import static org.apache.jackrabbit.vault.packaging.PackageProperties.NAME_VERSION;
+import static org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.NAME_ARTIFACT_ID;
+import static org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.NAME_GROUP_ID;
+import static org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.POM_TYPE;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.apache.sling.feature.cpconverter.spi.ArtifactWriter;
+import org.apache.sling.feature.cpconverter.writers.InputStreamArtifactWriter;
+import org.apache.sling.feature.cpconverter.writers.MavenPomSupplierWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class BundleEntryHandler extends AbstractRegexEntryHandler {
+
+    private static final String JAR_TYPE = "jar";
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private final Pattern pomPropertiesPattern = Pattern.compile("META-INF/maven/[^/]+/[^/]+/pom.properties");
+
+    private final Pattern pomXmlPattern = Pattern.compile("META-INF/maven/[^/]+/[^/]+/pom.xml");
+
+    public BundleEntryHandler() {
+        super("(jcr_root)?/apps/[^/]+/install(\\.([^/]+))?/.+\\.jar");
+    }
+
+    @Override
+    public void handle(String path, Archive archive, Entry entry, ContentPackage2FeatureModelConverter converter) throws Exception {
+        logger.info("Processing bundle {}...", entry.getName());
+
+        Properties properties = new Properties();
+        byte[] pomXml = null;
+
+        try (JarInputStream jarInput = new JarInputStream(archive.openInputStream(entry));
+                ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            JarEntry jarEntry;
+            while ((jarEntry = jarInput.getNextJarEntry()) != null) {
+                String entryName = jarEntry.getName();
+
+                if (pomPropertiesPattern.matcher(entryName).matches()) {
+                    logger.info("Reading '{}' bundle GAV from {}...", entry.getName(), entryName);
+
+                    properties.load(jarInput);
+                } else if (pomXmlPattern.matcher(entryName).matches()) {
+                    logger.info("Reading '{}' POM file from {}...", entry.getName(), entryName);
+
+                    IOUtils.copy(jarInput, baos);
+                    pomXml = baos.toByteArray();
+                }
+            }
+        }
+
+        String groupId = getCheckedProperty(properties, NAME_GROUP_ID);
+        String artifactId = getCheckedProperty(properties, NAME_ARTIFACT_ID);
+        String version = getCheckedProperty(properties, NAME_VERSION);
+
+        Matcher matcher = getPattern().matcher(path);
+        String runMode = null;
+        // we are pretty sure it matches, here
+        if (matcher.matches()) {
+            // there is a specified RunMode
+            runMode = matcher.group(3);
+        } else {
+            throw new IllegalStateException("Something went terribly wrong: pattern '"
+                                            + getPattern().pattern()
+                                            + "' should have matched already with path '"
+                                            + path
+                                            + "' but it does not, currently");
+        }
+
+        try (InputStream input = archive.openInputStream(entry)) {
+            converter.getArtifactDeployer().deploy(new InputStreamArtifactWriter(input),
+                                                   groupId,
+                                                   artifactId,
+                                                   version,
+                                                   null,
+                                                   JAR_TYPE);
+
+            converter.attach(runMode,
+                             groupId,
+                             artifactId,
+                             version,
+                             null,
+                             JAR_TYPE);
+        }
+
+        ArtifactWriter pomWriter;
+        if (pomXml == null) {
+            pomWriter = new MavenPomSupplierWriter(groupId, artifactId, version, JAR_TYPE);
+        } else {
+            pomWriter = new InputStreamArtifactWriter(new ByteArrayInputStream(pomXml));
+        }
+
+        converter.getArtifactDeployer().deploy(pomWriter, groupId, artifactId, version, null, POM_TYPE);
+    }
+
+    private static String getCheckedProperty(Properties properties, String name) {
+        String property = properties.getProperty(name).trim();
+        Objects.requireNonNull(property, "Bundle can not be defined as a valid Maven artifact without specifying a valid '"
+                                         + name
+                                         + "' property.");
+        return property;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/ConfigurationEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/ConfigurationEntryHandler.java
new file mode 100644
index 0000000..64c8647
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/ConfigurationEntryHandler.java
@@ -0,0 +1,36 @@
+/*
+ * 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.cpconverter.handlers;
+
+import java.io.InputStream;
+import java.util.Dictionary;
+
+import org.apache.felix.cm.file.ConfigurationHandler;
+
+public final class ConfigurationEntryHandler extends AbstractConfigurationEntryHandler {
+
+    public ConfigurationEntryHandler() {
+        super("config");
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected Dictionary<String, Object> parseConfiguration(String name, InputStream input) throws Exception {
+        return ConfigurationHandler.read(input);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandler.java
new file mode 100644
index 0000000..c385e05
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandler.java
@@ -0,0 +1,49 @@
+/*
+ * 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.cpconverter.handlers;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+
+public final class ContentPackageEntryHandler extends AbstractRegexEntryHandler {
+
+    public ContentPackageEntryHandler() {
+        super("(?:jcr_root)?/etc/packages/.+\\.zip");
+    }
+
+    @Override
+    public void handle(String path, Archive archive, Entry entry, ContentPackage2FeatureModelConverter converter) throws Exception {
+        logger.info("Processing sub-content package '{}'...", entry.getName());
+
+        File temporaryContentPackage = File.createTempFile("content-package", entry.getName());
+
+        try (InputStream input = archive.openInputStream(entry);
+                OutputStream output = new FileOutputStream(temporaryContentPackage)) {
+            IOUtils.copy(input, output);
+        }
+
+        converter.processSubPackage(path, temporaryContentPackage);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/JsonConfigurationEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/JsonConfigurationEntryHandler.java
new file mode 100644
index 0000000..7ddaf4f
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/JsonConfigurationEntryHandler.java
@@ -0,0 +1,75 @@
+/*
+ * 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.cpconverter.handlers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.net.URL;
+import java.util.Dictionary;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.felix.configurator.impl.json.JSONUtil;
+import org.apache.felix.configurator.impl.json.TypeConverter;
+import org.apache.felix.configurator.impl.model.ConfigurationFile;
+
+public final class JsonConfigurationEntryHandler extends AbstractConfigurationEntryHandler {
+
+    public JsonConfigurationEntryHandler() {
+        super("cfg\\.json");
+    }
+
+    @Override
+    protected Dictionary<String, Object> parseConfiguration(String name, InputStream input) throws Exception {
+        StringBuilder content = new StringBuilder()
+                                .append("{ \"")
+                                .append(name)
+                                .append("\" : ");
+        try (Reader reader = new InputStreamReader(input); StringWriter writer = new StringWriter()) {
+            IOUtils.copy(reader, writer);
+            content.append(writer.toString());
+        }
+        content.append("}");
+
+        JSONUtil.Report report = new JSONUtil.Report();
+        ConfigurationFile configuration = JSONUtil.readJSON(new TypeConverter(null),
+                                                            name,
+                                                            new URL("file://content-package/" + name),
+                                                            0,
+                                                            content.toString(),
+                                                            report);
+
+        if (!report.errors.isEmpty() || !report.warnings.isEmpty()) {
+            final StringBuilder builder = new StringBuilder();
+            builder.append("Errors in configuration:");
+            for (final String w : report.warnings) {
+                builder.append("\n");
+                builder.append(w);
+            }
+            for (final String e : report.errors) {
+                builder.append("\n");
+                builder.append(e);
+            }
+            throw new IOException(builder.toString());
+        }
+
+        return configuration.getConfigurations().get(0).getProperties();
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/PropertiesConfigurationEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/PropertiesConfigurationEntryHandler.java
new file mode 100644
index 0000000..faa0dac
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/PropertiesConfigurationEntryHandler.java
@@ -0,0 +1,60 @@
+/*
+ * 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.cpconverter.handlers;
+
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+
+public final class PropertiesConfigurationEntryHandler extends AbstractConfigurationEntryHandler {
+
+    public PropertiesConfigurationEntryHandler() {
+        super("(cfg|properties)");
+    }
+
+    @Override
+    protected Dictionary<String, Object> parseConfiguration(String name, InputStream input) throws Exception {
+        final Properties properties = new Properties();
+
+        try (final BufferedInputStream in = new BufferedInputStream(input)) {
+            in.mark(1);
+
+            boolean isXml = '<' == in.read();
+
+            in.reset();
+
+            if (isXml) {
+                properties.loadFromXML(in);
+            } else {
+                properties.load(in);
+            }
+        }
+
+        Dictionary<String, Object> configuration = new Hashtable<>();
+        final Enumeration<Object> i = properties.keys();
+        while (i.hasMoreElements()) {
+            final Object key = i.nextElement();
+            configuration.put(key.toString(), properties.get(key));
+        }
+
+        return configuration;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/XmlConfigurationEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/XmlConfigurationEntryHandler.java
new file mode 100644
index 0000000..ed6eabe
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/XmlConfigurationEntryHandler.java
@@ -0,0 +1,92 @@
+/*
+ * 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.cpconverter.handlers;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.jackrabbit.vault.util.DocViewProperty;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public final class XmlConfigurationEntryHandler extends AbstractConfigurationEntryHandler {
+
+    private static final String JCR_ROOT = "jcr:root";
+
+    private static final String SLING_OSGICONFIG = "sling:OsgiConfig";
+
+    private final SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+
+    public XmlConfigurationEntryHandler() {
+        super("xml");
+    }
+
+    @Override
+    protected Dictionary<String, Object> parseConfiguration(String name, InputStream input) throws Exception {
+        SAXParser saxParser = saxParserFactory.newSAXParser();
+        JcrConfigurationHandler configurationHandler = new JcrConfigurationHandler();
+        saxParser.parse(input, configurationHandler);
+        return configurationHandler.getConfiguration();
+    }
+
+    private static final class JcrConfigurationHandler extends DefaultHandler {
+
+        private final Dictionary<String, Object> configuration = new Hashtable<>();
+
+        public Dictionary<String, Object> getConfiguration() {
+            return configuration;
+        }
+
+        @Override
+        public void startElement(String uri, String localName, String qName, Attributes attributes)
+                throws SAXException {
+            String primaryType = attributes.getValue(JCR_PRIMARYTYPE);
+
+            if (JCR_ROOT.equals(qName) && SLING_OSGICONFIG.equals(primaryType)) {
+                for (int i = 0; i < attributes.getLength(); i++) {
+                    String attributeQName = attributes.getQName(i);
+
+                    // ignore jcr: and similar properties
+                    if (attributeQName.indexOf(':') == -1) {
+                        String attributeValue = attributes.getValue(i);
+
+                        if (attributeValue != null && !attributeValue.isEmpty()) {
+                            DocViewProperty property = DocViewProperty.parse(attributeQName, attributeValue);
+
+                            if (property.values.length > 0) {
+                                if (property.isMulti) {
+                                    configuration.put(attributeQName, property.values);
+                                } else {
+                                    configuration.put(attributeQName, property.values[0]);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/package-info.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/package-info.java
new file mode 100644
index 0000000..85df1d5
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Default implementations of the <i>org.apache.sling.feature.cpconverter.spi.EntryHandler</i> service
+ * to handle resources while scanning Apache Jackrabbit Vault packages,
+ * such as OSGi bundles &amp; configurations and nested content packages.
+ */
+package org.apache.sling.feature.cpconverter.handlers;
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/package-info.java b/src/main/java/org/apache/sling/feature/cpconverter/package-info.java
new file mode 100644
index 0000000..1af70af
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Content Package to Feature Model converter tool for Apache Sling.
+ */
+package org.apache.sling.feature.cpconverter;
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/spi/ArtifactWriter.java b/src/main/java/org/apache/sling/feature/cpconverter/spi/ArtifactWriter.java
new file mode 100644
index 0000000..6df9c20
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/spi/ArtifactWriter.java
@@ -0,0 +1,26 @@
+/*
+ * 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.cpconverter.spi;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public interface ArtifactWriter {
+
+    void write(OutputStream output) throws IOException;
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/spi/BundlesDeployer.java b/src/main/java/org/apache/sling/feature/cpconverter/spi/BundlesDeployer.java
new file mode 100644
index 0000000..ca64299
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/spi/BundlesDeployer.java
@@ -0,0 +1,33 @@
+/*
+ * 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.cpconverter.spi;
+
+import java.io.File;
+import java.io.IOException;
+
+public interface BundlesDeployer {
+
+    File getBundlesDirectory();
+
+    void deploy(ArtifactWriter artifactWriter,
+                String groupId,
+                String artifactId,
+                String version,
+                String classifier,
+                String type) throws IOException;
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/spi/EntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/spi/EntryHandler.java
new file mode 100644
index 0000000..51e3835
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/spi/EntryHandler.java
@@ -0,0 +1,29 @@
+/*
+ * 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.cpconverter.spi;
+
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+
+public interface EntryHandler {
+
+    boolean matches(String path);
+
+    void handle(String path, Archive archive, Entry entry, ContentPackage2FeatureModelConverter converter) throws Exception;
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/spi/package-info.java b/src/main/java/org/apache/sling/feature/cpconverter/spi/package-info.java
new file mode 100644
index 0000000..4d66b84
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/spi/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Definition of services that can be plugged at runtime to extend the converter tool.
+ */
+package org.apache.sling.feature.cpconverter.spi;
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssembler.java b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssembler.java
new file mode 100644
index 0000000..d5ec573
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssembler.java
@@ -0,0 +1,172 @@
+/*
+ * 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.cpconverter.vltpkg;
+
+import static org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.FEATURE_CLASSIFIER;
+import static org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.NAME_ARTIFACT_ID;
+import static org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.NAME_GROUP_ID;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Properties;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.jackrabbit.vault.packaging.PackageProperties;
+import org.apache.jackrabbit.vault.packaging.VaultPackage;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.apache.sling.feature.cpconverter.spi.EntryHandler;
+import org.codehaus.plexus.archiver.Archiver;
+import org.codehaus.plexus.archiver.jar.JarArchiver;
+import org.codehaus.plexus.archiver.util.DefaultFileSet;
+
+public final class VaultPackageAssembler implements EntryHandler {
+
+    private static final String META_INF_VAULT_DIRECTORY = "META-INF/vault/";
+
+    private static final String VAULT_PROPERTIES_FILE = META_INF_VAULT_DIRECTORY + "properties.xml";
+
+    private static final String NAME_PATH = "path";
+
+    private static final String[] INCLUDE_RESOURCES = { "definition/.content.xml", "config.xml", "settings.xml" };
+
+    private static final File TMP_DIR = new File(System.getProperty("java.io.tmpdir"));
+
+    public static VaultPackageAssembler create(VaultPackage vaultPackage) {
+        File storingDirectory = new File(TMP_DIR, vaultPackage.getFile().getName() + "-deflated");
+        storingDirectory.mkdirs();
+
+        PackageProperties packageProperties = vaultPackage.getProperties();
+
+        Properties properties = new Properties();
+        properties.setProperty(PackageProperties.NAME_VERSION,
+                               packageProperties.getProperty(PackageProperties.NAME_VERSION)
+                                                             + '-'
+                                                             + FEATURE_CLASSIFIER);
+
+        for (String key : new String[] {
+                PackageProperties.NAME_GROUP,
+                PackageProperties.NAME_NAME,
+                PackageProperties.NAME_DEPENDENCIES,
+                PackageProperties.NAME_CREATED_BY,
+                PackageProperties.NAME_CREATED,
+                PackageProperties.NAME_REQUIRES_ROOT,
+                PackageProperties.NAME_PACKAGE_TYPE,
+                PackageProperties.NAME_AC_HANDLING,
+                NAME_GROUP_ID,
+                NAME_ARTIFACT_ID,
+                NAME_PATH
+        }) {
+            String value = packageProperties.getProperty(key);
+            if (value != null && !value.isEmpty()) {
+                properties.setProperty(key, value);
+            }
+        }
+
+        return new VaultPackageAssembler(storingDirectory, properties);
+    }
+
+    private final File storingDirectory;
+
+    private final Properties properties;
+
+    @Override
+    public boolean matches(String path) {
+        return true;
+    }
+
+    @Override
+    public void handle(String path, Archive archive, Entry entry, ContentPackage2FeatureModelConverter converter)
+            throws Exception {
+        addEntry(path, archive, entry);
+    }
+
+    /**
+     * This class can not be instantiated from outside
+     *
+     * @param properties
+     */
+    private VaultPackageAssembler(File storingDirectory, Properties properties) {
+        this.storingDirectory = storingDirectory;
+        this.properties = properties;
+    }
+
+    public void addEntry(String path, Archive archive, Entry entry) throws IOException {
+        try (InputStream input = archive.openInputStream(entry)) {
+            addEntry(path, input);
+        }
+    }
+
+    public void addEntry(String path, File file) throws IOException {
+        try (InputStream input = new FileInputStream(file)) {
+            addEntry(path, input);
+        }
+    }
+
+    public void addEntry(String path, InputStream input) throws IOException {
+        File target = new File(storingDirectory, path);
+
+        target.getParentFile().mkdirs();
+
+        try (OutputStream output = new FileOutputStream(target)) {
+            IOUtils.copy(input, output);
+        }
+    }
+
+    public File createPackage() throws IOException {
+        return createPackage(TMP_DIR);
+    }
+
+    public File createPackage(File outputDirectory) throws IOException {
+        // generate the Vault properties XML file
+
+        File xmlProperties = new File(storingDirectory, VAULT_PROPERTIES_FILE);
+        xmlProperties.getParentFile().mkdirs();
+
+        try (FileOutputStream fos = new FileOutputStream(xmlProperties)) {
+            properties.storeToXML(fos, null);
+        }
+
+        // copy the required resources
+
+        for (String resource : INCLUDE_RESOURCES) {
+            try (InputStream input = getClass().getResourceAsStream(resource)) {
+                addEntry(META_INF_VAULT_DIRECTORY + resource, input);
+            }
+        }
+
+        // create the target archiver
+
+        Archiver archiver = new JarArchiver();
+        archiver.setIncludeEmptyDirs(true);
+
+        String destFileName = storingDirectory.getName().substring(0, storingDirectory.getName().lastIndexOf('-'));
+        File destFile = new File(TMP_DIR, destFileName);
+
+        archiver.setDestFile(destFile);
+        archiver.addFileSet(new DefaultFileSet(storingDirectory));
+        archiver.createArchive();
+
+        return destFile;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/package-info.java b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/package-info.java
new file mode 100644
index 0000000..eab6ead
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Easy to use Apache Jackrabbit Vault packager.
+ */
+package org.apache.sling.feature.cpconverter.vltpkg;
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/writers/FileArtifactWriter.java b/src/main/java/org/apache/sling/feature/cpconverter/writers/FileArtifactWriter.java
new file mode 100644
index 0000000..c70bc8e
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/writers/FileArtifactWriter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.cpconverter.writers;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.sling.feature.cpconverter.spi.ArtifactWriter;
+
+public final class FileArtifactWriter implements ArtifactWriter {
+
+    private final File fileArtifact;
+
+    public FileArtifactWriter(File fileArtifact) {
+        this.fileArtifact = fileArtifact;
+    }
+
+    @Override
+    public void write(OutputStream output) throws IOException {
+        try (InputStream input = new FileInputStream(fileArtifact)) {
+            new InputStreamArtifactWriter(input).write(output);
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/writers/InputStreamArtifactWriter.java b/src/main/java/org/apache/sling/feature/cpconverter/writers/InputStreamArtifactWriter.java
new file mode 100644
index 0000000..3092c0f
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/writers/InputStreamArtifactWriter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.cpconverter.writers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.sling.feature.cpconverter.spi.ArtifactWriter;
+
+public class InputStreamArtifactWriter implements ArtifactWriter {
+
+    private final InputStream input;
+
+    public InputStreamArtifactWriter(InputStream input) {
+        this.input = input;
+    }
+
+    @Override
+    public void write(OutputStream output) throws IOException {
+        IOUtils.copy(input, output);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/writers/MavenPomSupplierWriter.java b/src/main/java/org/apache/sling/feature/cpconverter/writers/MavenPomSupplierWriter.java
new file mode 100644
index 0000000..69f8f9b
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/writers/MavenPomSupplierWriter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.cpconverter.writers;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.maven.model.Model;
+import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
+import org.apache.sling.feature.cpconverter.spi.ArtifactWriter;
+
+public final class MavenPomSupplierWriter implements ArtifactWriter {
+
+    private final String groupId;
+
+    private final String artifactId;
+
+    private final String version;
+
+    private final String type;
+
+    public MavenPomSupplierWriter(String groupId, String artifactId, String version, String type) {
+        this.groupId = groupId;
+        this.artifactId = artifactId;
+        this.version = version;
+        this.type = type;
+    }
+
+    @Override
+    public void write(OutputStream outputStream) throws IOException {
+        Model model = new Model();
+        model.setGroupId(groupId);
+        model.setArtifactId(artifactId);
+        model.setVersion(version);
+        model.setPackaging(type);
+
+        new MavenXpp3Writer().write(outputStream, model);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/writers/package-info.java b/src/main/java/org/apache/sling/feature/cpconverter/writers/package-info.java
new file mode 100644
index 0000000..103359d
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/cpconverter/writers/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Default implementations of the <i>org.apache.sling.feature.cpconverter.spi.ArtifactWriter</i>.
+ */
+package org.apache.sling.feature.cpconverter.writers;
diff --git a/src/main/legal/LICENSE-with-deps b/src/main/legal/LICENSE-with-deps
new file mode 100644
index 0000000..75545a9
--- /dev/null
+++ b/src/main/legal/LICENSE-with-deps
@@ -0,0 +1,233 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
+
+<#function licenseFormat licenses>
+    <#assign result = ""/>
+    <#list licenses as license>
+        <#if result?length &gt; 0>
+            <#assign result = result + ", " />
+        </#if>
+        <#assign result = result + license />
+    </#list>
+    <#return result>
+</#function>
+
+<#function artifactFormat p>
+    <#if p.name?index_of('Unnamed') &gt; -1>
+        <#return p.groupId + ":" + p.artifactId + ":" + p.version + " (" + (p.url!"no url defined") + ")">
+    <#else>
+        <#return p.name + " v" + p.version + " (" + (p.url!"no url defined") + ")">
+    </#if>
+</#function>
+
+<#if dependencyMap?size == 0>
+The project has no dependencies.
+<#else>
+    <#list dependencyMap as e>
+        <#assign project = e.getKey()/>
+        <#assign licenses = e.getValue()/>
+For the ${artifactFormat(project)} component
+This is licensed under ${licenseFormat(licenses)}
+
+    </#list>
+</#if>
diff --git a/src/main/legal/NOTICE-with-deps b/src/main/legal/NOTICE-with-deps
new file mode 100644
index 0000000..e7f7780
--- /dev/null
+++ b/src/main/legal/NOTICE-with-deps
@@ -0,0 +1,5 @@
+Apache Sling Feature Analyser
+Copyright 2018 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/src/main/legal/license-header b/src/main/legal/license-header
new file mode 100644
index 0000000..d323a71
--- /dev/null
+++ b/src/main/legal/license-header
@@ -0,0 +1,14 @@
+  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. 
diff --git a/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.spi.EntryHandler b/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.spi.EntryHandler
new file mode 100644
index 0000000..dfa616c
--- /dev/null
+++ b/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.spi.EntryHandler
@@ -0,0 +1,6 @@
+org.apache.sling.feature.cpconverter.handlers.BundleEntryHandler
+org.apache.sling.feature.cpconverter.handlers.ConfigurationEntryHandler
+org.apache.sling.feature.cpconverter.handlers.ContentPackageEntryHandler
+org.apache.sling.feature.cpconverter.handlers.JsonConfigurationEntryHandler
+org.apache.sling.feature.cpconverter.handlers.PropertiesConfigurationEntryHandler
+org.apache.sling.feature.cpconverter.handlers.XmlConfigurationEntryHandler
diff --git a/src/main/resources/org/apache/sling/feature/cpconverter/vltpkg/config.xml b/src/main/resources/org/apache/sling/feature/cpconverter/vltpkg/config.xml
new file mode 100644
index 0000000..54a0a38
--- /dev/null
+++ b/src/main/resources/org/apache/sling/feature/cpconverter/vltpkg/config.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with this
+ work for additional information regarding copyright ownership. The ASF
+ licenses this file to You under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations under
+ the License.
+-->
+<vaultfs version="1.1">
+  <!--
+    Defines the content aggregation. The order of the defined aggregates
+    is important for finding the correct aggregator.
+  -->
+  <aggregates>
+    <!--
+      Defines an aggregate that handles nt:file and nt:resource nodes.
+    -->
+    <aggregate type="file" title="File Aggregate"/>
+
+    <!--
+      Defines an aggregate that handles file/folder like nodes. It matches
+      all nt:hierarchyNode nodes that have or define a jcr:content
+      child node and excludes child nodes that are nt:hierarchyNodes.
+    -->
+    <aggregate type="filefolder" title="File/Folder Aggregate"/>
+
+    <!--
+      Defines an aggregate that handles nt:nodeType nodes and serializes
+      them into .cnd notation.
+    -->
+    <aggregate type="nodetype" title="Node Type Aggregate" />
+
+    <!--
+      Defines an aggregate that defines full coverage for certain node
+      types that cannot be covered by the default aggregator.
+    -->
+    <aggregate type="full" title="Full Coverage Aggregate">
+      <matches>
+        <include nodeType="rep:AccessControl" respectSupertype="true" />
+        <include nodeType="cq:Widget" respectSupertype="true" />
+        <include nodeType="cq:WidgetCollection" respectSupertype="true" />
+        <include nodeType="cq:EditConfig" respectSupertype="true" />
+        <include nodeType="cq:WorkflowModel" respectSupertype="true" />
+        <include nodeType="vlt:FullCoverage" respectSupertype="true" />
+        <include nodeType="mix:language" respectSupertype="true" />
+        <include nodeType="sling:OsgiConfig" respectSupertype="true" />
+      </matches>
+    </aggregate>
+
+    <!--
+      Defines an aggregate that handles nt:folder like nodes.
+    -->
+    <aggregate type="generic" title="Folder Aggregate">
+      <matches>
+        <include nodeType="nt:folder" respectSupertype="true" />
+      </matches>
+      <contains>
+        <exclude isNode="true" />
+      </contains>
+    </aggregate>
+
+    <!--
+      Defines the default aggregate
+    -->
+    <aggregate type="generic" title="Default Aggregator" isDefault="true">
+      <contains>
+        <exclude nodeType="nt:hierarchyNode" respectSupertype="true" />
+      </contains>
+      <matches>
+        <!-- all -->
+      </matches>
+    </aggregate>
+
+  </aggregates>
+
+  <!--
+    defines the input handlers
+  -->
+  <handlers>
+    <handler type="folder"/>
+    <handler type="file"/>
+    <handler type="nodetype"/>
+    <handler type="generic"/>
+  </handlers>
+
+</vaultfs>
diff --git a/src/main/resources/org/apache/sling/feature/cpconverter/vltpkg/definition/.content.xml b/src/main/resources/org/apache/sling/feature/cpconverter/vltpkg/definition/.content.xml
new file mode 100644
index 0000000..abf67f1
--- /dev/null
+++ b/src/main/resources/org/apache/sling/feature/cpconverter/vltpkg/definition/.content.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with this
+ work for additional information regarding copyright ownership. The ASF
+ licenses this file to You under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations under
+ the License.
+-->
+<jcr:root xmlns:vlt="http://www.day.com/jcr/vault/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
+  jcr:primaryType="vlt:PackageDefinition" />
diff --git a/src/main/resources/org/apache/sling/feature/cpconverter/vltpkg/settings.xml b/src/main/resources/org/apache/sling/feature/cpconverter/vltpkg/settings.xml
new file mode 100644
index 0000000..679e6b1
--- /dev/null
+++ b/src/main/resources/org/apache/sling/feature/cpconverter/vltpkg/settings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<vault version="1.0">
+  <ignore name=".git" />
+  <ignore name=".svn" />
+  <ignore name=".DS_Store" />
+</vault>
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java b/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
new file mode 100644
index 0000000..0514504
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
@@ -0,0 +1,235 @@
+/*
+ * 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.cpconverter;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.Reader;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.zip.ZipFile;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.io.json.FeatureJSONReader;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ContentPackage2FeatureModelConverterTest {
+
+    private ContentPackage2FeatureModelConverter converter;
+
+    @Before
+    public void setUp() {
+        converter = new ContentPackage2FeatureModelConverter();
+    }
+
+    @After
+    public void tearDowd() {
+        converter = null;
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void convertRequiresNonNullPackage() throws Exception {
+        converter.convert(null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void convertRequiresExistingFile() throws Exception {
+        converter.convert(new File("this/file/does/not/exist.zip"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void convertRequiresNotDirectoryFile() throws Exception {
+        File testDirectory = new File(System.getProperty("user.dir"));
+        converter.convert(testDirectory);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void getRunModeRequiresConvertInvoked() {
+        converter.getRunMode(null);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void addConfigurationRequiresConvertInvoked() {
+        converter.setMergeConfigurations(true).getRunMode(null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void processRequiresNotNullPackage() throws Exception {
+        converter.processSubPackage("", null);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void processRequiresConvertInvoked() throws Exception {
+        converter.processSubPackage("", mock(File.class));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void deployLocallyAndAttachRequiresNonNullInput() throws Exception {
+        converter.attach(null, null, null, null, null, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void deployLocallyAndAttachRequiresNonNullGroupId() throws Exception {
+        converter.attach(null, null, null, null, null, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void deployLocallyAndAttachRequiresNonNullArtifactId() throws Exception {
+        converter.attach(null, "org.apache.sling", null, null, null, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void deployLocallyAndAttachRequiresNonNullVersion() throws Exception {
+        converter.attach(null, "org.apache.sling", "org.apache.sling.cm2fm", null, null, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void deployLocallyAndAttachRequiresNonNullType() throws Exception {
+        converter.attach(null, "org.apache.sling", "org.apache.sling.cm2fm", "0.0.1", null, null);
+    }
+
+    @Test
+    public void convertContentPackage() throws Exception {
+        URL packageUrl = getClass().getResource("test-content-package.zip");
+        File packageFile = FileUtils.toFile(packageUrl);
+
+        File outputDirectory = new File(System.getProperty("testDirectory"), getClass().getName() + '_' + System.currentTimeMillis());
+
+        converter.setBundlesStartOrder(5).setOutputDirectory(outputDirectory).convert(packageFile);
+
+        verifyFeatureFile(outputDirectory,
+                          "asd.retail.all.json",
+                          "org.apache.sling:asd.retail.all:slingosgifeature:cp2fm-converted-feature:0.0.1",
+                          Arrays.asList("org.apache.felix:org.apache.felix.framework:6.0.1"),
+                          Arrays.asList("org.apache.sling.commons.log.LogManager.factory.config-asd-retail"),
+                          Arrays.asList("org.apache.sling:asd.retail.all:zip:cp2fm-converted-feature:0.0.1"));
+        verifyFeatureFile(outputDirectory,
+                          "asd.retail.all-author.json",
+                          "org.apache.sling:asd.retail.all:slingosgifeature:cp2fm-converted-feature-author:0.0.1",
+                          Arrays.asList("org.apache.sling:org.apache.sling.api:2.20.0"),
+                          Collections.emptyList(),
+                          Collections.emptyList());
+        verifyFeatureFile(outputDirectory,
+                          "asd.retail.all-publish.json",
+                          "org.apache.sling:asd.retail.all:slingosgifeature:cp2fm-converted-feature-publish:0.0.1",
+                          Arrays.asList("org.apache.sling:org.apache.sling.models.api:1.3.8"),
+                          Arrays.asList("org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-asd-retail"),
+                          Collections.emptyList());
+
+        ZipFile zipFile = new ZipFile(new File(outputDirectory, "bundles/org/apache/sling/asd.retail.all/0.0.1/asd.retail.all-0.0.1-cp2fm-converted-feature.zip"));
+        for (String expectedEntry : new String[] {
+                "jcr_root/content/asd/.content.xml",
+                "jcr_root/content/asd/resources.xml",
+                "jcr_root/apps/.content.xml",
+                "META-INF/vault/properties.xml",
+                "META-INF/vault/config.xml",
+                "META-INF/vault/settings.xml",
+                "META-INF/vault/definition/.content.xml",
+                "jcr_root/etc/packages/asd/test-bundles.zip",
+                "jcr_root/etc/packages/asd/test-configurations.zip",
+                "jcr_root/etc/packages/asd/test-content.zip",
+                }) {
+            assertNotNull(zipFile.getEntry(expectedEntry));
+        }
+        zipFile.close();
+    }
+
+    private void verifyFeatureFile(File outputDirectory,
+                                   String name,
+                                   String expectedArtifactId,
+                                   List<String> expectedBundles,
+                                   List<String> expectedConfigurations,
+                                   List<String> expectedContentPackagesExtensions) throws Exception {
+        File featureFile = new File(outputDirectory, name);
+        assertTrue(featureFile + " was not correctly created", featureFile.exists());
+
+        try (Reader reader = new FileReader(featureFile)) {
+            Feature feature = FeatureJSONReader.read(reader, featureFile.getAbsolutePath());
+
+            assertEquals(expectedArtifactId, feature.getId().toMvnId());
+
+            for (String expectedBundle : expectedBundles) {
+                assertTrue(expectedBundle + " not found in Feature " + expectedArtifactId, feature.getBundles().containsExact(ArtifactId.fromMvnId(expectedBundle)));
+                verifyInstalledBundle(outputDirectory, expectedBundle);
+            }
+
+            for (String expectedConfiguration : expectedConfigurations) {
+                assertNotNull(expectedConfiguration + " not found in Feature " + expectedArtifactId, feature.getConfigurations().getConfiguration(expectedConfiguration));
+            }
+
+            for (String expectedContentPackagesExtension : expectedContentPackagesExtensions) {
+                assertTrue(expectedContentPackagesExtension + " not found in Feature " + expectedArtifactId,
+                           feature.getExtensions().getByName("content-packages").getArtifacts().containsExact(ArtifactId.fromMvnId(expectedContentPackagesExtension)));
+                verifyInstalledBundle(outputDirectory, expectedContentPackagesExtension);
+            }
+        }
+    }
+
+    private void verifyInstalledBundle(File outputDirectory, String coordinates) {
+        ArtifactId bundleId = ArtifactId.fromMvnId(coordinates);
+
+        File bundleDirectory = new File(outputDirectory, "bundles");
+
+        StringTokenizer tokenizer = new StringTokenizer(bundleId.getGroupId(), ".");
+        while (tokenizer.hasMoreTokens()) {
+            bundleDirectory = new File(bundleDirectory, tokenizer.nextToken());
+        }
+
+        bundleDirectory = new File(bundleDirectory, bundleId.getArtifactId());
+        bundleDirectory = new File(bundleDirectory, bundleId.getVersion());
+
+        StringBuilder bundleFileName = new StringBuilder()
+                                       .append(bundleId.getArtifactId())
+                                       .append('-')
+                                       .append(bundleId.getVersion());
+        if (bundleId.getClassifier() != null) {
+            bundleFileName.append('-').append(bundleId.getClassifier());
+        }
+        bundleFileName.append('.').append(bundleId.getType());
+
+        File bundleFile = new File(bundleDirectory, bundleFileName.toString());
+        assertTrue("Bundle " + bundleFile + " does not exist", bundleFile.exists());
+
+        File pomFile = new File(bundleDirectory, String.format("%s-%s.pom", bundleId.getArtifactId(), bundleId.getVersion()));
+        assertTrue("POM file " + pomFile + " does not exist", pomFile.exists());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void verifyFilteringOutUndesiredPackages() throws Exception {
+        converter.addFilteringPattern(".*\\/install(?!(\\.runMode1\\/|\\.runMode2\\/|\\/))(.*)(?=\\.zip$).*");
+
+        URL packageUrl = getClass().getResource("test-content-package-unacceptable.zip");
+        File packageFile = FileUtils.toFile(packageUrl);
+
+        File outputDirectory = new File(System.getProperty("testDirectory"), getClass().getName() + '_' + System.currentTimeMillis());
+
+        converter.setBundlesStartOrder(5).setOutputDirectory(outputDirectory).convert(packageFile);
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/DefaultBundlesDeployerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/DefaultBundlesDeployerTest.java
new file mode 100644
index 0000000..1fe0429
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/cpconverter/DefaultBundlesDeployerTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.cpconverter;
+
+import static org.junit.Assert.*;
+
+import static org.mockito.Mockito.mock;
+
+import java.io.File;
+
+import org.apache.sling.feature.cpconverter.spi.ArtifactWriter;
+import org.apache.sling.feature.cpconverter.spi.BundlesDeployer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DefaultBundlesDeployerTest {
+
+    private BundlesDeployer artifactDeployer;
+
+    @Before
+    public void setUp() {
+        File outputDirectory = new File(System.getProperty("testDirectory"), getClass().getName() + '_' + System.currentTimeMillis());
+        artifactDeployer = new DefaultBundlesDeployer(outputDirectory);
+    }
+
+    @After
+    public void tearDown() {
+        artifactDeployer = null;
+    }
+
+    @Test
+    public void verifyBundlesDirectory() {
+        File bundlesDirectory = artifactDeployer.getBundlesDirectory();
+        assertNotNull(bundlesDirectory);
+        assertTrue(bundlesDirectory.exists());
+        assertTrue(bundlesDirectory.isDirectory());
+        assertEquals("bundles", bundlesDirectory.getName());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void deployLocallyAndAttachRequiresNonNullInput() throws Exception {
+        artifactDeployer.deploy(null, null, null, null, null, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void deployLocallyAndAttachRequiresNonNullGroupId() throws Exception {
+        artifactDeployer.deploy(mock(ArtifactWriter.class), null, null, null, null, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void deployLocallyAndAttachRequiresNonNullArtifactId() throws Exception {
+        artifactDeployer.deploy(mock(ArtifactWriter.class), "org.apache.sling", null, null, null, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void deployLocallyAndAttachRequiresNonNullVersion() throws Exception {
+        artifactDeployer.deploy(mock(ArtifactWriter.class), "org.apache.sling", "org.apache.sling.cm2fm", null, null, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void deployLocallyAndAttachRequiresNonNullType() throws Exception {
+        artifactDeployer.deploy(mock(ArtifactWriter.class), "org.apache.sling", "org.apache.sling.cm2fm", "0.0.1", null, null);
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/RegexBasedResourceFilterTest.java b/src/test/java/org/apache/sling/feature/cpconverter/RegexBasedResourceFilterTest.java
new file mode 100644
index 0000000..346d4f7
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/cpconverter/RegexBasedResourceFilterTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.cpconverter;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RegexBasedResourceFilterTest {
+
+    private RegexBasedResourceFilter filter;
+
+    @Before
+    public void setUp() {
+        filter = new RegexBasedResourceFilter();
+    }
+
+    @After
+    public void tearDown() {
+        filter = null;
+    }
+
+    @Test
+    public void packagesFilteredIn() {
+        filter.addFilteringPattern(".*\\/myEnvironment(?!(\\.runMode1\\/|\\.runMode2\\/|\\/))(.*)(?=\\.zip$).*");
+
+        assertFalse(filter.isFilteredOut("/apps/myapp/myEnvironment/something.zip"));
+        assertFalse(filter.isFilteredOut("/apps/myapp/myEnvironment.runMode1/something.zip"));
+        assertFalse(filter.isFilteredOut("/apps/myapp/myEnvironment.runMode2/something.zip"));
+
+        assertFalse(filter.isFilteredOut("/apps/myEnvironment/asd.zip"));
+    }
+
+    @Test
+    public void configFilteredOut() {
+        filter.addFilteringPattern(".*\\/myEnvironment(?!(\\.runMode1\\/|\\.runMode2\\/|\\/))(.*)(?=\\.config$).*");
+
+        assertTrue(filter.isFilteredOut("/apps/myapp/myEnvironment.subRunMode/something.config"));
+        assertTrue(filter.isFilteredOut("/apps/myapp/myEnvironment.runMode1.subRunMode/something.config"));
+        assertTrue(filter.isFilteredOut("/apps/myapp/myEnvironment.subRunMode.runMode1/something.config"));
+    }
+
+    @Test
+    public void packagesFilteredOut() {
+        filter.addFilteringPattern(".*\\/myEnvironment(?!(\\.runMode1\\/|\\.runMode2\\/|\\/))(.*)(?=\\.zip$).*");
+
+        assertTrue(filter.isFilteredOut("/apps/myapp/myEnvironment.subRunMode/something.zip"));
+        assertTrue(filter.isFilteredOut("/apps/myapp/myEnvironment.runMode1.subRunMode/something.zip"));
+        assertTrue(filter.isFilteredOut("/apps/myapp/myEnvironment.subRunMode.runMode1/something.zip"));
+        assertTrue(filter.isFilteredOut("/apps/myapp/myEnvironment.xyz/something.zip"));
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandlerTest.java
new file mode 100644
index 0000000..8753d61
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandlerTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.cpconverter.handlers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.apache.sling.feature.cpconverter.DefaultBundlesDeployer;
+import org.apache.sling.feature.cpconverter.spi.EntryHandler;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+@RunWith(Parameterized.class)
+public final class BundleEntryHandlerTest {
+
+    private final String bundleLocation;
+
+    private final EntryHandler bundleEntryHandler;
+
+    public BundleEntryHandlerTest(String bundleLocation, EntryHandler bundleEntryHandler) {
+        this.bundleLocation = bundleLocation;
+        this.bundleEntryHandler = bundleEntryHandler;
+    }
+
+    @Test
+    public void doesNotMatch() {
+        assertFalse(bundleEntryHandler.matches("jcr_root/not/a/valid/recognised/bundle.jar"));
+    }
+
+    @Test
+    public void matches() {
+        assertTrue(bundleEntryHandler.matches(bundleLocation));
+    }
+
+    @Test
+    public void deployBundle() throws Exception {
+        Archive archive = mock(Archive.class);
+        Entry entry = mock(Entry.class);
+
+        when(entry.getName()).thenReturn("test-framework.jar");
+        when(archive.openInputStream(entry)).then(new Answer<InputStream>() {
+
+            @Override
+            public InputStream answer(InvocationOnMock invocation) throws Throwable {
+                return getClass().getResourceAsStream(bundleLocation);
+            }
+
+        });
+
+        ContentPackage2FeatureModelConverter converter = spy(ContentPackage2FeatureModelConverter.class);
+
+        File testDirectory = new File(System.getProperty("testDirectory"), getClass().getName() + '_' + System.currentTimeMillis());
+        when(converter.getOutputDirectory()).thenReturn(testDirectory);
+
+        doCallRealMethod().when(converter).attach(anyString(), anyString(), anyString(), anyString(), anyString(), anyString());
+        when(converter.getArtifactDeployer()).thenReturn(new DefaultBundlesDeployer(testDirectory));
+
+        Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null));
+        when(converter.getTargetFeature()).thenReturn(feature);
+        when(converter.getRunMode(anyString())).thenReturn(feature);
+
+        bundleEntryHandler.handle(bundleLocation, archive, entry, converter);
+
+        assertTrue(new File(testDirectory, "bundles/org/apache/felix/org.apache.felix.framework/6.0.1/org.apache.felix.framework-6.0.1.pom").exists());
+        assertTrue(new File(testDirectory, "bundles/org/apache/felix/org.apache.felix.framework/6.0.1/org.apache.felix.framework-6.0.1.jar").exists());
+
+        assertFalse(converter.getTargetFeature().getBundles().isEmpty());
+        assertEquals(1, feature.getBundles().size());
+        assertEquals("org.apache.felix:org.apache.felix.framework:6.0.1", feature.getBundles().get(0).getId().toMvnId());
+    }
+
+    @Parameters
+    public static Collection<Object[]> data() {
+        final BundleEntryHandler bundleEntryHandler = new BundleEntryHandler();
+
+        return Arrays.asList(new Object[][] {
+            { "jcr_root/apps/asd/install/test-framework-no-pom.jar", bundleEntryHandler },
+            { "jcr_root/apps/asd/install/test-framework.jar", bundleEntryHandler },
+            { "jcr_root/apps/asd/install.author/test-framework.jar", bundleEntryHandler },
+            { "jcr_root/apps/asd/install.publish/test-framework.jar", bundleEntryHandler }
+        });
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/ConfigurationEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/ConfigurationEntryHandlerTest.java
new file mode 100644
index 0000000..649a07f
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/ConfigurationEntryHandlerTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.cpconverter.handlers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Configuration;
+import org.apache.sling.feature.Configurations;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConfigurationEntryHandlerTest {
+
+    private static final String EXPECTED_PID = "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl";
+
+    private final String resourceConfiguration;
+
+    private final int expectedConfigurationsSize;
+
+    private final AbstractConfigurationEntryHandler configurationEntryHandler;
+
+    public ConfigurationEntryHandlerTest(String resourceConfiguration,
+                                         int expectedConfigurationsSize,
+                                         AbstractConfigurationEntryHandler configurationEntryHandler) {
+        this.resourceConfiguration = resourceConfiguration;
+        this.expectedConfigurationsSize = expectedConfigurationsSize;
+        this.configurationEntryHandler = configurationEntryHandler;
+    }
+
+    @Test
+    public void doesNotMatch() {
+        assertFalse(configurationEntryHandler.matches("/this/is/a/path/not/pointing/to/a/valid/configuration.asd"));
+    }
+
+    @Test
+    public void matches() {
+        assertTrue(resourceConfiguration, configurationEntryHandler.matches(resourceConfiguration));
+    }
+
+    @Test
+    public void parseConfiguration() throws Exception {
+        Archive archive = mock(Archive.class);
+        Entry entry = mock(Entry.class);
+
+        when(entry.getName()).thenReturn(resourceConfiguration.substring(resourceConfiguration.lastIndexOf('/') + 1));
+        when(archive.openInputStream(entry)).thenReturn(getClass().getResourceAsStream(resourceConfiguration));
+
+        Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null));
+        ContentPackage2FeatureModelConverter converter = spy(ContentPackage2FeatureModelConverter.class);
+        when(converter.getTargetFeature()).thenReturn(feature);
+        doCallRealMethod().when(converter).addConfiguration(anyString(), anyString(), any());
+        when(converter.getRunMode(anyString())).thenReturn(feature);
+
+        configurationEntryHandler.handle(resourceConfiguration, archive, entry, converter);
+
+        Configurations configurations = converter.getTargetFeature().getConfigurations();
+
+        assertEquals(expectedConfigurationsSize, configurations.size());
+
+        if (expectedConfigurationsSize > 0) {
+            Configuration configuration = configurations.get(0);
+
+            assertTrue(configuration.getPid(), configuration.getPid().startsWith(EXPECTED_PID));
+            assertEquals("Unmatching size: " + configuration.getProperties().size(), 2, configuration.getProperties().size());
+        }
+    }
+
+    @Parameters
+    public static Collection<Object[]> data() {
+        String path = "jcr_root/apps/asd/config/";
+
+        return Arrays.asList(new Object[][] {
+            { path + EXPECTED_PID + ".empty.cfg", 0, new PropertiesConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".cfg", 1, new PropertiesConfigurationEntryHandler() },
+
+            { path + EXPECTED_PID + ".empty.cfg.json", 0, new JsonConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".cfg.json", 1, new JsonConfigurationEntryHandler() },
+
+            { path + EXPECTED_PID + ".empty.config", 0, new ConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".config", 1, new ConfigurationEntryHandler() },
+
+            { path + EXPECTED_PID + ".empty.xml", 0, new XmlConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".xml", 1, new XmlConfigurationEntryHandler() },
+
+            { path + EXPECTED_PID + ".empty.xml.cfg", 0, new PropertiesConfigurationEntryHandler() },
+            { path + EXPECTED_PID + ".xml.cfg", 1, new PropertiesConfigurationEntryHandler() },
+
+            // runmode aware folders
+            { "jcr_root/apps/asd/config.author/" + EXPECTED_PID + ".config", 1, new ConfigurationEntryHandler() },
+            { "jcr_root/apps/asd/config.publish/" + EXPECTED_PID + ".config", 1, new ConfigurationEntryHandler() },
+        });
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandlerTest.java
new file mode 100644
index 0000000..185bce0
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/ContentPackageEntryHandlerTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.cpconverter.handlers;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.sling.feature.cpconverter.spi.EntryHandler;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public final class ContentPackageEntryHandlerTest {
+
+    private EntryHandler contentPackageEntryhandler;
+
+    @Before
+    public void setUp() {
+        contentPackageEntryhandler = new ContentPackageEntryHandler();
+    }
+
+    @After
+    public void tearDown() {
+        contentPackageEntryhandler = null;
+    }
+
+    @Test
+    public void doesNotMatch() {
+        assertFalse(contentPackageEntryhandler.matches("/this/is/a/path/not/pointing/to/a/valid/configuration.asd"));
+    }
+
+    @Test
+    public void matches() {
+        assertTrue(contentPackageEntryhandler.matches("jcr_root/etc/packages/asd/v6/sample/asd.content-1.0.zip"));
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/JsonConfigurationEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/JsonConfigurationEntryHandlerTest.java
new file mode 100644
index 0000000..8289cd5
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/JsonConfigurationEntryHandlerTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.cpconverter.handlers;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
+import org.junit.Test;
+
+public class JsonConfigurationEntryHandlerTest {
+
+    @Test(expected = IOException.class)
+    public void invalidConfigurationThrowsException() throws Exception {
+        String resourceConfiguration = "jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.INVALID.cfg.json";
+
+        Archive archive = mock(Archive.class);
+        Entry entry = mock(Entry.class);
+
+        when(entry.getName()).thenReturn(resourceConfiguration.substring(resourceConfiguration.lastIndexOf('/') + 1));
+        when(archive.openInputStream(entry)).thenReturn(getClass().getResourceAsStream(resourceConfiguration));
+
+        ContentPackage2FeatureModelConverter converter = mock(ContentPackage2FeatureModelConverter.class);
+
+        new JsonConfigurationEntryHandler().handle(resourceConfiguration, archive, entry, converter);
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssemblerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssemblerTest.java
new file mode 100644
index 0000000..d2aa3cc
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssemblerTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.cpconverter.vltpkg;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.vault.packaging.VaultPackage;
+import org.apache.jackrabbit.vault.packaging.impl.PackageManagerImpl;
+import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class VaultPackageAssemblerTest {
+
+    private File testDirectory;
+
+    private final VaultPackageAssembler assembler;
+
+    private final String resourceLocation;
+
+    public VaultPackageAssemblerTest(String resourceLocation, VaultPackageAssembler assembler) {
+        this.resourceLocation = resourceLocation;
+        this.assembler = assembler;
+    }
+
+    @Before
+    public void setUp() {
+        testDirectory = new File(System.getProperty("testDirectory"), getClass().getName() + '_' + System.currentTimeMillis());
+    }
+
+    @Test
+    public void matchAll() {
+        assembler.matches(resourceLocation);
+    }
+
+    @Test
+    public void packageResource() throws Exception {
+        assembler.addEntry(resourceLocation, getClass().getResourceAsStream("../handlers/" + resourceLocation));
+        File contentPackage = assembler.createPackage(testDirectory);
+
+        ZipFile zipFile = new ZipFile(contentPackage);
+        ZipEntry entry = zipFile.getEntry(resourceLocation);
+        assertNotNull(entry);
+        zipFile.close();
+    }
+
+    @Parameters
+    public static Collection<Object[]> data() throws Exception {
+        URL resource = VaultPackageAssemblerTest.class.getResource("../test-content-package.zip");
+        File file = FileUtils.toFile(resource);
+        VaultPackage vaultPackage = new PackageManagerImpl().open(file);
+
+        VaultPackageAssembler assembler = VaultPackageAssembler.create(vaultPackage);
+
+        return Arrays.asList(new Object[][] {
+            { "jcr_root/.content.xml", assembler },
+            { "jcr_root/asd/.content.xml", assembler },
+            { "jcr_root/asd/public/_rep_policy.xml", assembler },
+            { "jcr_root/asd/public/license.txt", assembler }
+        });
+    }
+
+}
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/.content.xml
new file mode 100644
index 0000000..54fbe67
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/.content.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with this
+ work for additional information regarding copyright ownership. The ASF
+ licenses this file to You under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations under
+ the License.
+-->
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal"
+    jcr:mixinTypes="[rep:AccessControllable,rep:RepoAccessControllable]"
+    jcr:primaryType="rep:root"
+    sling:resourceType="sling:redirect"
+    sling:target="/index.html"/>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config.author/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.config b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config.author/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.config
new file mode 100644
index 0000000..4c50ea3
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config.author/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.config
@@ -0,0 +1,17 @@
+# 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.
+
+user.default="admin"
+user.mapping=["com.adobe.acs.acs-aem-samples-bundle\=admin","com.adobe.acs.acs-aem-samples-bundle:sample-service\=oauthservice"]
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config.publish/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.config b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config.publish/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.config
new file mode 100644
index 0000000..4c50ea3
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config.publish/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.config
@@ -0,0 +1,17 @@
+# 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.
+
+user.default="admin"
+user.mapping=["com.adobe.acs.acs-aem-samples-bundle\=admin","com.adobe.acs.acs-aem-samples-bundle:sample-service\=oauthservice"]
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.INVALID.cfg.json b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.INVALID.cfg.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.INVALID.cfg.json
@@ -0,0 +1 @@
+[]
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.cfg b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.cfg
new file mode 100644
index 0000000..bf91183
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.cfg
@@ -0,0 +1,17 @@
+# 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.
+
+user.default=admin
+user.mapping=[com.adobe.acs.acs-aem-samples-bundle=admin,com.adobe.acs.acs-aem-samples-bundle:sample-service=oauthservice]
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.cfg.json b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.cfg.json
new file mode 100644
index 0000000..98e506c
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.cfg.json
@@ -0,0 +1,7 @@
+{
+    "user.default":"admin",
+    "user.mapping": [
+        "com.adobe.acs.acs-aem-samples-bundle=admin",
+        "com.adobe.acs.acs-aem-samples-bundle:sample-service=oauthservice"
+    ]
+}
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.config b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.config
new file mode 100644
index 0000000..4c50ea3
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.config
@@ -0,0 +1,17 @@
+# 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.
+
+user.default="admin"
+user.mapping=["com.adobe.acs.acs-aem-samples-bundle\=admin","com.adobe.acs.acs-aem-samples-bundle:sample-service\=oauthservice"]
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.cfg b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.cfg
new file mode 100644
index 0000000..5db1a19
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.cfg
@@ -0,0 +1,14 @@
+# 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.
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.cfg.json b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.cfg.json
new file mode 100644
index 0000000..49d1a20
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.cfg.json
@@ -0,0 +1,3 @@
+{
+    
+}
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.config b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.config
new file mode 100644
index 0000000..5db1a19
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.config
@@ -0,0 +1,14 @@
+# 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.
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.xml
new file mode 100644
index 0000000..6211b85
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with this
+ work for additional information regarding copyright ownership. The ASF
+ licenses this file to You under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations under
+ the License.
+-->
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
+    jcr:primaryType="sling:OsgiConfig"/>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.xml.cfg b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.xml.cfg
new file mode 100644
index 0000000..87a44e8
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.empty.xml.cfg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<!--
+ 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.
+-->
+<properties />
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.xml
new file mode 100644
index 0000000..f23afc9
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with this
+ work for additional information regarding copyright ownership. The ASF
+ licenses this file to You under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations under
+ the License.
+-->
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
+    jcr:primaryType="sling:OsgiConfig"
+    user.default="admin"
+    user.mapping="[com.adobe.acs.acs-aem-samples-bundle=admin,com.adobe.acs.acs-aem-samples-bundle:sample-service=oauthservice]"/>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.xml.cfg b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.xml.cfg
new file mode 100644
index 0000000..05ce515
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.xml.cfg
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<!--
+ 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.
+-->
+<properties>
+  <entry key="user.default">admin</entry>
+  <entry key="user.mapping">[com.adobe.acs.acs-aem-samples-bundle=admin,com.adobe.acs.acs-aem-samples-bundle:sample-service=oauthservice]</entry>
+</properties>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/install.author/test-framework.jar b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/install.author/test-framework.jar
new file mode 100644
index 0000000..9ce022d
Binary files /dev/null and b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/install.author/test-framework.jar differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/install.publish/test-framework.jar b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/install.publish/test-framework.jar
new file mode 100644
index 0000000..9ce022d
Binary files /dev/null and b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/install.publish/test-framework.jar differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/install/test-framework-no-pom.jar b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/install/test-framework-no-pom.jar
new file mode 100644
index 0000000..cb3116e
Binary files /dev/null and b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/install/test-framework-no-pom.jar differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/install/test-framework.jar b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/install/test-framework.jar
new file mode 100644
index 0000000..9ce022d
Binary files /dev/null and b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/apps/asd/install/test-framework.jar differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/asd/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/asd/.content.xml
new file mode 100644
index 0000000..0d5ab1d
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/asd/.content.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with this
+ work for additional information regarding copyright ownership. The ASF
+ licenses this file to You under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations under
+ the License.
+-->
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal"
+    jcr:mixinTypes="[rep:AccessControllable]"
+    jcr:primaryType="sling:Folder"/>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/asd/public/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/asd/public/.content.xml
new file mode 100644
index 0000000..0589bcb
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/asd/public/.content.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with this
+ work for additional information regarding copyright ownership. The ASF
+ licenses this file to You under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations under
+ the License.
+-->
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
+    jcr:primaryType="sling:Folder"/>
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/asd/public/_rep_policy.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/asd/public/_rep_policy.xml
new file mode 100644
index 0000000..2b1119a
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/asd/public/_rep_policy.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with this
+ work for additional information regarding copyright ownership. The ASF
+ licenses this file to You under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations under
+ the License.
+-->
+<jcr:root xmlns:crx="http://www.day.com/crx/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal"
+    jcr:primaryType="rep:ACL">
+    <allow
+        jcr:primaryType="rep:GrantACE"
+        rep:principalName="everyone"
+        rep:privileges="{Name}[jcr:read]"/>
+</jcr:root>
\ No newline at end of file
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/asd/public/license.txt b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/asd/public/license.txt
new file mode 100644
index 0000000..805f6a4
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/asd/public/license.txt
@@ -0,0 +1,14 @@
+ 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.
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/test-content-package-unacceptable.zip b/src/test/resources/org/apache/sling/feature/cpconverter/test-content-package-unacceptable.zip
new file mode 100644
index 0000000..840ee6d
Binary files /dev/null and b/src/test/resources/org/apache/sling/feature/cpconverter/test-content-package-unacceptable.zip differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/test-content-package.zip b/src/test/resources/org/apache/sling/feature/cpconverter/test-content-package.zip
new file mode 100644
index 0000000..cfe6a94
Binary files /dev/null and b/src/test/resources/org/apache/sling/feature/cpconverter/test-content-package.zip differ