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:05 UTC

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

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