You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by tj...@apache.org on 2020/03/06 19:10:45 UTC

[felix-atomos] branch master updated: maven native image builder

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

tjwatson pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-atomos.git


The following commit(s) were added to refs/heads/master by this push:
     new d6d93ca  maven native image builder
     new c1a471a  Merge pull request #1 from stbischof/maven
d6d93ca is described below

commit d6d93ca145a1e7c69cf765c46ca1115fe1a2db29
Author: Stefan Bischof <st...@bipolis.org>
AuthorDate: Fri Mar 6 15:25:07 2020 +0100

    maven native image builder
---
 .../additionalResourceConfig.json                  |  16 +
 .../pom.xml                                        | 171 ++++++
 .../proxy_config.json                              |   3 +
 .../reflectConfig_felix_atomos.json                |   9 +
 .../reflectConfig_felix_gogo.json                  |  40 ++
 .../reflectConfig_felix_web.json                   |  33 ++
 .../reflectConfig_jdk.json                         |   7 +
 .../reflectConfig_jetty.json                       |  13 +
 atomos.examples/pom.xml                            |   1 +
 atomos.maven/pom.xml                               | 193 +++++++
 .../felix/atomos/maven/NativeImageBuilder.java     | 265 +++++++++
 .../apache/felix/atomos/maven/NativeImageMojo.java | 175 ++++++
 .../felix/atomos/maven/ReflectConfigUtil.java      | 642 +++++++++++++++++++++
 .../felix/atomos/maven/ResourceConfigUtil.java     | 199 +++++++
 .../apache/felix/atomos/maven/SubstrateUtil.java   | 244 ++++++++
 .../atomos/maven/reflect/ConstructorConfig.java    |  40 ++
 .../felix/atomos/maven/reflect/MethodConfig.java   |  36 ++
 .../felix/atomos/maven/reflect/ReflectConfig.java  |  89 +++
 .../atomos/maven/scrmock/EmptyBundeLogger.java     | 468 +++++++++++++++
 .../felix/atomos/maven/scrmock/EmptySCRLogger.java |  49 ++
 .../felix/atomos/maven/scrmock/PathBundle.java     | 206 +++++++
 .../org/apache/felix/atomos/maven/MojoTest.java    |  49 ++
 .../felix/atomos/maven/MojoTestExperiments.java    |  46 ++
 .../felix/atomos/maven/ReflectConfigTest.java      | 248 ++++++++
 .../apache/felix/atomos/maven/SubstrateTest.java   |  52 ++
 .../org/apache/felix/atomos/maven/TestBase.java    |  35 ++
 .../apache/felix/atomos/maven/TestConstants.java   | 118 ++++
 .../modulepath/service/ModulepathLaunchTest.java   |  25 +-
 .../atomos.tests.testbundles.bom/pom.xml           |  70 +++
 .../pom.xml                                        |  81 +++
 .../src/main/java/module-info.java                 |  18 +
 .../tests/testbundles/reflect/command/A.java       |  20 +
 .../testbundles/reflect/command/AbstractCmd.java   |  23 +
 .../testbundles/reflect/command/CmdExample.java    |  46 ++
 .../reflect/command/CommandFunction.java           |  35 ++
 .../testbundles/reflect/command/CommandScope.java  |  34 ++
 .../atomos.tests.testbundles.reflect.dto/pom.xml   |  76 +++
 .../src/main/java/module-info.java                 |  18 +
 .../tests/testbundles/reflect/command/OneDTO.java  |  22 +
 .../service/impl/activator/Activator.java          |  12 +-
 .../service/impl/activator/ActivatorEcho.java      |  27 +
 .../tests/testbundles/service/user/EchoUser.java   |   2 +-
 .../service/user/{EchoUser.java => EchoUser2.java} |  21 +-
 .../pom.xml                                        |  82 +++
 .../tests/testbundles/substrate/main/Main.java     |  72 +++
 atomos.tests/atomos.tests.testbundles/pom.xml      |   4 +
 pom.xml                                            |   1 +
 47 files changed, 4110 insertions(+), 26 deletions(-)

diff --git a/atomos.examples/atomos.examples.substrate.maven.equinox/additionalResourceConfig.json b/atomos.examples/atomos.examples.substrate.maven.equinox/additionalResourceConfig.json
new file mode 100644
index 0000000..2c0fa21
--- /dev/null
+++ b/atomos.examples/atomos.examples.substrate.maven.equinox/additionalResourceConfig.json
@@ -0,0 +1,16 @@
+{
+	"resources": [
+		{
+			"pattern": "META-INF/services/.*$"
+		},
+		{
+			"pattern": "templates/.*$"
+		},
+		{
+			"pattern": "res/.*$"
+		},
+		{
+			"pattern": "/system/console/res/imgs/favicon.ico"
+		}
+	]
+}
diff --git a/atomos.examples/atomos.examples.substrate.maven.equinox/pom.xml b/atomos.examples/atomos.examples.substrate.maven.equinox/pom.xml
new file mode 100644
index 0000000..d4527c5
--- /dev/null
+++ b/atomos.examples/atomos.examples.substrate.maven.equinox/pom.xml
@@ -0,0 +1,171 @@
+<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.felix.atomos.examples</groupId>
+        <artifactId>org.apache.felix.atomos.examples</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>org.apache.felix.atomos.examples.substrate.maven.equinox</artifactId>
+    <name>atomos.examples.substrate.maven.equinox</name>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <java.version>1.8</java.version>
+        <maven.compiler.release combine.self="override"></maven.compiler.release>
+        <maven.compiler.source>${java.version}</maven.compiler.source>
+        <maven.compiler.target>${java.version}</maven.compiler.target>
+    </properties>
+    <repositories>
+        <repository>
+            <id>atomos-temp-m2repo</id>
+            <url>https://github.com/tjwatson/atomos-temp-m2repo/raw/master/repository</url>
+            <snapshots>
+                <enabled>true</enabled>
+                <updatePolicy>interval:60</updatePolicy>
+            </snapshots>
+        </repository>
+    </repositories>
+    <dependencies>
+        <dependency>
+            <groupId>org.eclipse.platform</groupId>
+            <artifactId>org.eclipse.osgi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.platform</groupId>
+            <artifactId>org.eclipse.osgi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.atomos.runtime</artifactId>
+            <version>${atomos.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>*</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.atomos.tests</groupId>
+            <artifactId>org.apache.felix.atomos.tests.testbundles.bom</artifactId>
+            <version>${atomos.version}</version>
+            <type>pom</type>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.promise</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.gogo.command</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.gogo.runtime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.gogo.shell</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.http.jetty</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.http.api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.http.whiteboard</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.webconsole</artifactId>
+            <classifier>all</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.http.servlet-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.cm</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.event</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <version>1.3.2</version>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy-dependencies</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.build.directory}/classpath_lib</outputDirectory>
+                            <overWriteReleases>false</overWriteReleases>
+                            <overWriteSnapshots>false</overWriteSnapshots>
+                            <overWriteIfNewer>true</overWriteIfNewer>
+                            <excludeTransitive>false</excludeTransitive>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>org.apache.felix.atomos.maven</artifactId>
+                <version>${atomos.version}</version>
+                <configuration>
+                    <debug>true</debug>
+                    <additionalInitializeAtBuildTime>
+                        <additionalInitializeAtBuildTime>org.apache.felix.atomos.runtime</additionalInitializeAtBuildTime>
+                        <additionalInitializeAtBuildTime>org.apache.felix.atomos.impl</additionalInitializeAtBuildTime>
+                        <additionalInitializeAtBuildTime>javax.servlet</additionalInitializeAtBuildTime>
+                        <additionalInitializeAtBuildTime>org.apache.felix.service.command.Converter</additionalInitializeAtBuildTime>
+                        <!-- <additionalInitializeAtBuildTime>org.apache.felix.atomos.impl.runtime.base</additionalInitializeAtBuildTime> -->
+                    </additionalInitializeAtBuildTime>
+
+                    <graalResourceConfigFiles>
+                        <graalResourceConfigFile>additionalResourceConfig.json</graalResourceConfigFile>
+                    </graalResourceConfigFiles>
+                    <dynamicProxyConfigurationFiles>
+                        <dynamicProxyConfigurationFile>proxy_config.json</dynamicProxyConfigurationFile>
+                    </dynamicProxyConfigurationFiles>
+                    <reflectConfigFiles>
+                        <reflectConfigFile>reflectConfig_felix_atomos.json</reflectConfigFile>
+                        <reflectConfigFile>reflectConfig_felix_gogo.json</reflectConfigFile>
+                        <reflectConfigFile>reflectConfig_felix_web.json</reflectConfigFile>
+                        <reflectConfigFile>reflectConfig_jdk.json</reflectConfigFile>
+                        <reflectConfigFile>reflectConfig_jetty.json</reflectConfigFile>
+                    </reflectConfigFiles>
+                    <mainClass>org.apache.felix.atomos.tests.testbundles.substrate.main.Main</mainClass>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>atomos-native-image</goal>
+                        </goals>
+                        <phase>package</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/atomos.examples/atomos.examples.substrate.maven.equinox/proxy_config.json b/atomos.examples/atomos.examples.substrate.maven.equinox/proxy_config.json
new file mode 100644
index 0000000..f5c672c
--- /dev/null
+++ b/atomos.examples/atomos.examples.substrate.maven.equinox/proxy_config.json
@@ -0,0 +1,3 @@
+[
+  ["org.apache.felix.service.command.Converter"]
+]
diff --git a/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_felix_atomos.json b/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_felix_atomos.json
new file mode 100644
index 0000000..9751c7a
--- /dev/null
+++ b/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_felix_atomos.json
@@ -0,0 +1,9 @@
+[
+   {
+    "name" : "org.apache.felix.atomos.impl.runtime.base.AtomosCommands",
+    "allPublicMethods" : true,
+    "allDeclaredMethods" : true,
+    "allPublicFields" : true,
+    "allDeclaredFields" : true
+   }
+]
diff --git a/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_felix_gogo.json b/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_felix_gogo.json
new file mode 100644
index 0000000..8366da8
--- /dev/null
+++ b/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_felix_gogo.json
@@ -0,0 +1,40 @@
+[
+  {
+     "name":"org.apache.felix.service.command.Converter"
+  },
+    {
+    "name" : "org.apache.felix.gogo.shell.Builtin",
+    "allPublicMethods" : true,
+    "allDeclaredMethods" : true,
+    "allPublicFields" : true,
+    "allDeclaredFields" : true
+  },
+  {
+    "name" : "org.apache.felix.gogo.shell.Shell",
+    "allPublicMethods" : true,
+    "allDeclaredMethods" : true,
+    "allPublicFields" : true,
+    "allDeclaredFields" : true
+  },
+  {
+    "name" : "org.apache.felix.gogo.shell.Procedural",
+    "allPublicMethods" : true,
+    "allDeclaredMethods" : true,
+    "allPublicFields" : true,
+    "allDeclaredFields" : true
+  },
+  {
+    "name" : "org.apache.felix.gogo.shell.Posix",
+    "allPublicMethods" : true,
+    "allDeclaredMethods" : true,
+    "allPublicFields" : true,
+    "allDeclaredFields" : true
+  },
+  {
+    "name" : "org.apache.felix.gogo.shell.Telnet",
+    "allPublicMethods" : true,
+    "allDeclaredMethods" : true,
+    "allPublicFields" : true,
+    "allDeclaredFields" : true
+  }
+]
diff --git a/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_felix_web.json b/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_felix_web.json
new file mode 100644
index 0000000..97e6494
--- /dev/null
+++ b/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_felix_web.json
@@ -0,0 +1,33 @@
+[
+
+  {
+    "name" : "org.apache.felix.webconsole.internal.core.ServicesServlet",
+    "allPublicConstructors" : true,
+    "allPublicMethods" : true
+  },
+  {
+    "name" : "org.apache.felix.webconsole.internal.system.VMStatPlugin",
+    "allPublicConstructors" : true,
+    "allPublicMethods" : true
+  },
+  {
+    "name" : "org.apache.felix.webconsole.internal.configuration.ConfigManager",
+    "allPublicConstructors" : true,
+    "allPublicMethods" : true
+  },
+  {
+    "name" : "org.apache.felix.webconsole.internal.core.BundlesServlet",
+    "allPublicConstructors" : true,
+    "allPublicMethods" : true
+  },
+  {
+    "name" : "org.apache.felix.webconsole.internal.misc.LicenseServlet",
+    "allPublicConstructors" : true,
+    "allPublicMethods" : true
+  },
+  {
+    "name" : "org.apache.felix.webconsole.internal.compendium.LogServlet",
+    "allPublicConstructors" : true,
+    "allPublicMethods" : true
+  }
+]
diff --git a/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_jdk.json b/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_jdk.json
new file mode 100644
index 0000000..281fba6
--- /dev/null
+++ b/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_jdk.json
@@ -0,0 +1,7 @@
+[
+   {
+    "name" : "java.io.File",
+    "allPublicConstructors" : true,
+    "allPublicMethods" : true
+  }
+]
diff --git a/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_jetty.json b/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_jetty.json
new file mode 100644
index 0000000..8f4b6a3
--- /dev/null
+++ b/atomos.examples/atomos.examples.substrate.maven.equinox/reflectConfig_jetty.json
@@ -0,0 +1,13 @@
+[
+  {
+    "name" : "org.eclipse.jetty.servlet.ServletMapping[]",
+    "allPublicConstructors" : true,
+    "allPublicMethods" : true
+  },
+  {
+    "name" : "org.eclipse.jetty.servlet.FilterMapping[]",
+    "allPublicConstructors" : true,
+    "allPublicMethods" : true
+  }
+  
+]
diff --git a/atomos.examples/pom.xml b/atomos.examples/pom.xml
index d0f45df..afc2339 100644
--- a/atomos.examples/pom.xml
+++ b/atomos.examples/pom.xml
@@ -20,6 +20,7 @@
                 <module>atomos.examples.jlink</module>
                 <module>atomos.examples.substrate.equinox</module>
                 <module>atomos.examples.substrate.felix</module>
+                <module>atomos.examples.substrate.maven.equinox</module>
             </modules>
         </profile>
     </profiles>
diff --git a/atomos.maven/pom.xml b/atomos.maven/pom.xml
new file mode 100644
index 0000000..a827a46
--- /dev/null
+++ b/atomos.maven/pom.xml
@@ -0,0 +1,193 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>atomos-parent</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>org.apache.felix.atomos.maven</artifactId>
+    <packaging>maven-plugin</packaging>
+    <name>atomos.maven</name>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-plugin-api</artifactId>
+            <version>3.6.3</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.plugin-tools</groupId>
+            <artifactId>maven-plugin-annotations</artifactId>
+            <version>3.6.0</version>
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-core</artifactId>
+            <version>3.6.3</version>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>3.15.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.javassist</groupId>
+            <artifactId>javassist</artifactId>
+            <version>3.26.0-GA</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.atomos.tests</groupId>
+            <artifactId>org.apache.felix.atomos.tests.testbundles.bom</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+            <version>${atomos.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <version>6.0.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.http</artifactId>
+            <version>1.2.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.gogo.command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.http.jetty</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.http.api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.http.whiteboard</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.webconsole</artifactId>
+            <scope>test</scope>
+            <classifier>all</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.http.servlet-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy-dependencies</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.build.directory}/test-dependencies</outputDirectory>
+                            <overWriteReleases>false</overWriteReleases>
+                            <overWriteSnapshots>false</overWriteSnapshots>
+                            <overWriteIfNewer>true</overWriteIfNewer>
+                            <excludeTransitive>false</excludeTransitive>
+                            <includeGroupIds>org.osgi,org.apache.felix,org.apache.felix.atomos.tests</includeGroupIds>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <!-- <plugin> -->
+            <!-- <artifactId>maven-invoker-plugin</artifactId> -->
+            <!-- <version>3.2.1</version> -->
+            <!-- <configuration> -->
+            <!-- <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo> -->
+            <!-- <localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath> -->
+            <!-- <postBuildHookScript>verify</postBuildHookScript> -->
+            <!-- <goals> -->
+            <!-- <goal>install</goal> -->
+            <!-- </goals> -->
+            <!-- </configuration> -->
+            <!-- <executions> -->
+            <!-- <execution> -->
+            <!-- <id>integration-test</id> -->
+            <!-- <goals> -->
+            <!-- <goal>install</goal> -->
+            <!-- <goal>run</goal> -->
+            <!-- </goals> -->
+            <!-- </execution> -->
+            <!-- </executions> -->
+            <!-- </plugin> -->
+        </plugins>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-plugin-plugin</artifactId>
+                    <version>3.6.0</version>
+                    <executions>
+                        <execution>
+                            <id>default-descriptor</id>
+                            <phase>process-classes</phase>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.eclipse.m2e</groupId>
+                    <artifactId>lifecycle-mapping</artifactId>
+                    <version>1.0.0</version>
+                    <configuration>
+                        <lifecycleMappingMetadata>
+                            <pluginExecutions>
+                                <pluginExecution>
+                                    <pluginExecutionFilter>
+                                        <groupId>org.apache.maven.plugins</groupId>
+                                        <artifactId>maven-plugin-plugin</artifactId>
+                                        <versionRange>[3.6,)</versionRange>
+                                        <goals>
+                                            <goal>descriptor</goal>
+                                            <goal>helpmojo</goal>
+                                        </goals>
+                                    </pluginExecutionFilter>
+                                    <action>
+                                        <ignore/>
+                                    </action>
+                                </pluginExecution>
+                            </pluginExecutions>
+                        </lifecycleMappingMetadata>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+</project>
diff --git a/atomos.maven/src/main/java/org/apache/felix/atomos/maven/NativeImageBuilder.java b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/NativeImageBuilder.java
new file mode 100644
index 0000000..cbfd48b
--- /dev/null
+++ b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/NativeImageBuilder.java
@@ -0,0 +1,265 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.felix.atomos.maven.ResourceConfigUtil.ResourceConfigResult;
+
+public class NativeImageBuilder
+{
+
+    private static final String GRAAL_HOME = "GRAAL_HOME";
+
+    private static final String JAVA_HOME = "java.home";
+
+    private static final String NI_DEFAULT_NAME = "Application";
+
+    private static final String NI_PARAM_ALLOW_INCOMPLETE_CLASSPATH = "--allow-incomplete-classpath";
+
+    private static final String NI_PARAM_DEBUG_ATTACH = "--debug-attach";
+
+    private static final String NI_PARAM_H_CLASS = "-H:Class=";
+
+    private static final String NI_PARAM_H_DYNAMIC_PROXY_CONFIGURATION_FILES = "-H:DynamicProxyConfigurationFiles=";
+
+    private static final String NI_PARAM_H_NAME = "-H:Name=";
+
+    private static final String NI_PARAM_H_PRINT_CLASS_INITIALIZATION = "-H:+PrintClassInitialization";
+
+    private static final String NI_PARAM_H_REFLECTION_CONFIGURATION_FILES = "-H:ReflectionConfigurationFiles=";
+
+    private static final String NI_PARAM_H_REPORT_EXCEPTION_STACK_TRACES = "-H:+ReportExceptionStackTraces";
+
+    private static final String NI_PARAM_H_REPORT_UNSUPPORTED_ELEMENTS_AT_RUNTIME = "-H:+ReportUnsupportedElementsAtRuntime";
+
+    private static final String NI_PARAM_H_RESOURCE_CONFIGURATION_FILES = "-H:ResourceConfigurationFiles=";
+
+    private static final String NI_PARAM_H_TRACE_CLASS_INITIALIZATION = "-H:+TraceClassInitialization";
+
+    private static final String NI_PARAM_INITIALIZE_AT_BUILD_TIME = "--initialize-at-build-time=";
+
+    private static final String NI_PARAM_NO_FALLBACK = "--no-fallback";
+
+    public static List<String> createExecutionArgs(
+        List<String> additionalInitializeAtBuildTime, List<Path> reflectConfigFiles,
+        List<Path> resourceConfigs, List<Path> dynamicProxyConfigurationFiles,
+        ResourceConfigResult resourceConfigResult, boolean debug, String mainClass,
+        String imageName) throws IOException
+    {
+        final List<String> args = new ArrayList<>();
+
+        args.add(NI_PARAM_ALLOW_INCOMPLETE_CLASSPATH);
+
+        //initialize-at-build-time
+
+        final List<String> in = new ArrayList<>();
+        if (resourceConfigResult.allResourcePackages != null)
+        {
+            in.addAll(resourceConfigResult.allResourcePackages);
+        }
+        if (additionalInitializeAtBuildTime != null)
+        {
+            in.addAll(additionalInitializeAtBuildTime);
+        }
+
+        final String initBuildTime = in.stream().sorted(
+            (o1, o2) -> o1.compareTo(o2)).collect(Collectors.joining(","));
+
+        if (initBuildTime != null && !initBuildTime.isEmpty())
+        {
+            args.add(NI_PARAM_INITIALIZE_AT_BUILD_TIME + initBuildTime);
+        }
+
+        //H:ReflectionConfigurationFiles
+
+        if (reflectConfigFiles != null && !reflectConfigFiles.isEmpty())
+        {
+            final String reflCfgFiles = reflectConfigFiles.stream().map(
+                p -> p.toAbsolutePath().toString()).collect(Collectors.joining(","));
+
+            args.add(NI_PARAM_H_REFLECTION_CONFIGURATION_FILES + reflCfgFiles);
+
+        }
+        //H:ResourceConfigurationFiles
+
+        if (resourceConfigs != null && !resourceConfigs.isEmpty())
+        {
+            final String files = resourceConfigs.stream().map(
+                p -> p.toAbsolutePath().toString()).collect(Collectors.joining(","));
+            args.add(NI_PARAM_H_RESOURCE_CONFIGURATION_FILES + files);
+        }
+
+        //H:DynamicProxyConfigurationFiles
+        if (dynamicProxyConfigurationFiles != null
+            && !dynamicProxyConfigurationFiles.isEmpty())
+        {
+            args.add(NI_PARAM_H_DYNAMIC_PROXY_CONFIGURATION_FILES
+                + dynamicProxyConfigurationFiles.stream().map(
+                    p -> p.toAbsolutePath().toString()).collect(Collectors.joining(",")));
+        }
+        //other
+        args.add(NI_PARAM_H_REPORT_UNSUPPORTED_ELEMENTS_AT_RUNTIME);
+        args.add(NI_PARAM_H_REPORT_EXCEPTION_STACK_TRACES);
+        args.add(NI_PARAM_H_TRACE_CLASS_INITIALIZATION);
+        args.add(NI_PARAM_H_PRINT_CLASS_INITIALIZATION);
+        args.add(NI_PARAM_NO_FALLBACK);
+        if (debug)
+        {
+            args.add(NI_PARAM_DEBUG_ATTACH);
+        }
+        args.add(NI_PARAM_H_CLASS + mainClass);
+        args.add(NI_PARAM_H_NAME + imageName);
+        return args;
+    }
+
+    public static Path execute(Path nativeImageExec, Path outputDir, List<Path> classpath,
+        List<String> args) throws Exception
+    {
+        final Optional<Path> exec = findNativeImageExecutable(nativeImageExec);
+
+        if (exec.isEmpty())
+        {
+            throw new Exception("Missing native image executable. Set '" + GRAAL_HOME
+                + "' with the path as an environment variable");
+        }
+
+        Path resultFile = null;
+
+        Optional<String> imageName = args.stream().filter(
+            s -> s.startsWith(NI_PARAM_H_NAME)).findFirst();
+        if (imageName.isPresent())
+        {
+            final String name = imageName.get().substring(NI_PARAM_H_NAME.length());
+            resultFile = outputDir.resolve(name);
+        }
+        else
+        {
+            args.add(NI_PARAM_H_NAME + NI_DEFAULT_NAME);
+            resultFile = outputDir.resolve(NI_DEFAULT_NAME);
+        }
+
+        final String cp = classpath.stream().map(
+            p -> p.toAbsolutePath().toString()).collect(Collectors.joining(":"));
+
+        final List<String> commands = new ArrayList<>();
+        commands.add(exec.get().toAbsolutePath().toString());
+        commands.add("-cp");
+        commands.add(cp);
+        commands.addAll(args);
+
+        final ProcessBuilder pB = new ProcessBuilder(commands);
+        pB.inheritIO();
+        pB.directory(outputDir.toFile());
+
+        final String cmds = pB.command().stream().collect(Collectors.joining(" "));
+
+        System.out.println(cmds);
+
+        final Process process = pB.start();
+        final int exitValue = process.waitFor();
+        if (exitValue != 0)
+        {
+            throw new Exception("native-image returns exit value: " + exitValue);
+        }
+        if (Files.exists(resultFile))
+        {
+            return resultFile;
+        }
+        throw new Exception(
+            "native-image could not be found: " + resultFile.toAbsolutePath().toString());
+
+    }
+
+    private static Path findNativeImageExec(Path path)
+    {
+
+        Path candidate = null;
+        if (!Files.exists(path))
+        {
+            return candidate;
+        }
+        if (Files.isDirectory(path))
+        {
+            candidate = findNativeImageExec(path.resolve("native-image"));
+
+            if (candidate == null)
+            {
+                candidate = findNativeImageExec(path.resolve("bin"));
+            }
+
+        }
+        else //file o
+        {
+
+            try
+            {
+                final ProcessBuilder processBuilder = new ProcessBuilder(path.toString(),
+                    "--version");
+
+                final Process versionProcess = processBuilder.start();
+                final Stream<String> lines = new BufferedReader(
+                    new InputStreamReader(versionProcess.getInputStream())).lines();
+                final Optional<String> versionLine = lines.filter(
+                    l -> l.contains("GraalVM Version")).findFirst();
+
+                if (!versionLine.isEmpty())
+                {
+                    System.out.println(versionLine.get());
+                    candidate = path;
+                }
+            }
+            catch (final IOException e)
+            {
+                e.printStackTrace();
+            }
+
+        }
+        return candidate;
+
+    }
+
+    private static Optional<Path> findNativeImageExecutable(Path nativeImageExec)
+    {
+
+        Path exec = null;
+        if (nativeImageExec != null)
+        {
+            exec = findNativeImageExec(nativeImageExec);
+        }
+        if (exec == null)
+        {
+            exec = findNativeImageExec(Paths.get("native-image"));
+        }
+        if (exec == null && System.getenv(GRAAL_HOME) != null)
+        {
+            exec = findNativeImageExec(Paths.get(System.getenv(GRAAL_HOME)));
+        }
+        if (exec == null && System.getProperty(JAVA_HOME) != null)
+        {
+            exec = findNativeImageExec(Paths.get(System.getProperty(JAVA_HOME)));
+        }
+        return Optional.ofNullable(exec);
+    }
+}
diff --git a/atomos.maven/src/main/java/org/apache/felix/atomos/maven/NativeImageMojo.java b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/NativeImageMojo.java
new file mode 100644
index 0000000..ae47886
--- /dev/null
+++ b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/NativeImageMojo.java
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+
+import org.apache.felix.atomos.maven.ResourceConfigUtil.ResourceConfigResult;
+import org.apache.felix.atomos.maven.reflect.ReflectConfig;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+
+@Mojo(name = "atomos-native-image", defaultPhase = LifecyclePhase.GENERATE_RESOURCES)
+public class NativeImageMojo extends AbstractMojo
+{
+
+    private class Config
+    {
+        public List<String> additionalInitializeAtBuildTime = new ArrayList<>();
+
+        public List<Path> dynamicProxyConfigurationFiles = new ArrayList<>();
+        public List<Path> reflectConfigFiles = new ArrayList<>();
+        public List<Path> resourceConfigs = new ArrayList<>();
+    }
+
+    final private static String ATOMOS_PATH = "ATOMOS";
+
+    public static boolean isJarFile(Path path)
+    {
+        try (JarFile j = new JarFile(path.toFile());)
+        {
+
+            return true;
+        }
+        catch (IOException e)
+        {
+
+        }
+
+        return false;
+    }
+
+    @Parameter
+    private List<String> additionalInitializeAtBuildTime;
+
+    @Parameter(defaultValue = "${project.build.directory}/" + "classpath_lib")
+    private File classpath_lib;
+
+    @Parameter(defaultValue = "false") //TODO: CHECK GRAAL EE ONLY
+    private boolean debug;
+
+    @Parameter
+    private List<File> dynamicProxyConfigurationFiles;
+
+    @Parameter
+    private List<File> graalResourceConfigFiles;
+
+    @Parameter
+    private String imageName;
+
+    @Parameter
+    private String mainClass;
+
+    @Parameter(defaultValue = "graal.native.image.build.args")
+    private String nativeImageArgsPropertyName;
+
+    @Parameter
+    private String nativeImageExecutable;
+
+    @Parameter(defaultValue = "${project.build.directory}/" + ATOMOS_PATH)
+    private File outputDirectory;
+
+    @Parameter(defaultValue = "${project}", required = true, readonly = false)
+    private MavenProject project;
+
+    @Parameter
+    private List<File> reflectConfigFiles;
+
+    @Override
+    public void execute() throws MojoExecutionException
+    {
+        getLog().info("outputDirectory" + outputDirectory);
+        try
+        {
+            Files.createDirectories(outputDirectory.toPath());
+
+            Config config = new Config();
+
+            config.additionalInitializeAtBuildTime = additionalInitializeAtBuildTime;
+            if (imageName == null || imageName.isEmpty())
+            {
+                imageName = project.getArtifactId();
+            }
+
+            if (graalResourceConfigFiles != null && !graalResourceConfigFiles.isEmpty())
+            {
+                config.resourceConfigs = graalResourceConfigFiles.stream().map(
+                    File::toPath).collect(Collectors.toList());
+            }
+
+            if (reflectConfigFiles != null && !reflectConfigFiles.isEmpty())
+            {
+                config.reflectConfigFiles = reflectConfigFiles.stream().map(
+                    File::toPath).collect(Collectors.toList());
+            }
+
+            if (dynamicProxyConfigurationFiles != null
+                && !dynamicProxyConfigurationFiles.isEmpty())
+            {
+                config.dynamicProxyConfigurationFiles = dynamicProxyConfigurationFiles.stream().map(
+                    File::toPath).collect(Collectors.toList());
+            }
+
+            List<Path> paths = Files.list(classpath_lib.toPath()).filter(
+                NativeImageMojo::isJarFile).collect(Collectors.toList());
+
+            Path p = SubstrateUtil.substrate(paths, outputDirectory.toPath());
+
+            List<ReflectConfig> reflectConfigs = ReflectConfigUtil.reflectConfig(paths);
+
+            String content = ReflectConfigUtil.json(reflectConfigs);
+
+            if (!content.isEmpty())
+            {
+                Path reflectConfig = outputDirectory.toPath().resolve(
+                    "graal_reflect_config.json");
+                Files.write(reflectConfig, content.getBytes());
+
+                config.reflectConfigFiles.add(reflectConfig);
+            }
+
+            ResourceConfigResult resourceConfigResult = ResourceConfigUtil.resourceConfig(
+                paths);
+
+            List<String> argsPath = NativeImageBuilder.createExecutionArgs(
+                config.additionalInitializeAtBuildTime, config.reflectConfigFiles,
+                config.resourceConfigs, config.dynamicProxyConfigurationFiles,
+                resourceConfigResult, false, mainClass, imageName);
+
+            paths.add(p);
+            Path nip = nativeImageExecutable == null ? null
+                : Paths.get(nativeImageExecutable);
+            NativeImageBuilder.execute(nip, outputDirectory.toPath(), paths, argsPath);
+        }
+        catch (
+
+            Exception e)
+        {
+            throw new MojoExecutionException("Error", e);
+        }
+
+    }
+}
diff --git a/atomos.maven/src/main/java/org/apache/felix/atomos/maven/ReflectConfigUtil.java b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/ReflectConfigUtil.java
new file mode 100644
index 0000000..16ac6fd
--- /dev/null
+++ b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/ReflectConfigUtil.java
@@ -0,0 +1,642 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UncheckedIOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.StringTokenizer;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.felix.atomos.maven.reflect.ConstructorConfig;
+import org.apache.felix.atomos.maven.reflect.MethodConfig;
+import org.apache.felix.atomos.maven.reflect.ReflectConfig;
+import org.apache.felix.atomos.maven.scrmock.EmptyBundeLogger;
+import org.apache.felix.atomos.maven.scrmock.PathBundle;
+import org.apache.felix.scr.impl.logger.BundleLogger;
+import org.apache.felix.scr.impl.metadata.ComponentMetadata;
+import org.apache.felix.scr.impl.metadata.ReferenceMetadata;
+import org.apache.felix.scr.impl.parser.KXml2SAXParser;
+import org.apache.felix.scr.impl.xml.XmlHandler;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.ComponentConstants;
+
+public class ReflectConfigUtil
+{
+    private static final String OSGI_COMMAND_FUNCTION = "osgi.command.function";
+
+    private static String CLASS_START = "{\n";
+    private static String CLASS_END = "}";
+    private static String COMMA_SPACE = ", ";
+    private static String COMMA_ENTER = ",\n";
+    private static String NAME_PATTERN = "\"name\":\"%s\"";
+    private static String CLASS_NAME = NAME_PATTERN;
+    private static String FIELDS_START = "\"fields\" : [\n";
+    private static String FIELD_NAME = NAME_PATTERN;
+    private static String FIELDS_END = "]";
+    private static String METHODS_START = "\"methods\" : [\n";
+    private static String METHOD_NAME = NAME_PATTERN;
+    private static String METHODS_END = FIELDS_END;
+    private static String CONSTRUCTOR_METHOD_NAME = "<init>";
+
+    private static String PARAMETER_TYPE = "\"parameterTypes\":[%s]";
+
+    private static String COMPONENT_CONSTRUCTOR = "\"allPublicConstructors\" : true";
+
+    public static List<ReflectConfig> reflectConfig(List<Path> paths) throws Exception
+    {
+        URL[] urls = paths.stream().map(p -> {
+            try
+            {
+                return p.toUri().toURL();
+            }
+            catch (MalformedURLException e1)
+            {
+                throw new UncheckedIOException(e1);
+            }
+        }).toArray(URL[]::new);
+
+        try (URLClassLoader cl = URLClassLoader.newInstance(urls, null))
+        {
+
+            List<Class<?>> classes = loadClasses(paths, cl);
+
+            List<ReflectConfig> reflectConfigs = new ArrayList<>();
+            for (Path p : paths)
+            {
+                try (JarFile jar = new JarFile(p.toFile()))
+                {
+                    discoverBundleActivators(cl, jar, reflectConfigs);
+                    discoverSeriviceComponents(cl, jar, reflectConfigs);
+                    discoverDTOs(classes, reflectConfigs);
+
+                }
+            }
+            return reflectConfigs;
+        }
+    }
+
+    private static List<Class<?>> loadClasses(List<Path> paths, URLClassLoader cl)
+    {
+        return paths.stream().map(p -> {
+            try
+            {
+                return new JarFile(p.toFile());
+            }
+            catch (IOException e)
+            {
+                throw new UncheckedIOException(e);
+            }
+        }).flatMap(j -> j.stream()).filter(e -> !e.isDirectory()).filter(
+            e -> e.getName().endsWith(".class")).filter(
+                e -> !e.getName().endsWith("module-info.class")).map(e -> {
+                    try
+                    {
+                        String name = e.getName().replace("/", ".").substring(0,
+                            e.getName().length() - 6);
+                        return cl.loadClass(name);
+                    }
+                    catch (NoClassDefFoundError | ClassNotFoundException e1)
+                    {
+                        //   happened when incomplete classpath
+                    }
+                    return null;
+                }).collect(Collectors.toList());
+    }
+
+    private static void discoverDTOs(final List<Class<?>> classes,
+        List<ReflectConfig> reflectConfigs)
+    {
+        classes.stream().filter(c -> c != null).filter(c -> {
+            Class<?> clazz = c;
+            while (clazz != null && clazz != Object.class)
+            {
+
+                if ("org.osgi.dto.DTO".equals(clazz.getName()))
+                {
+                    return true;
+                }
+                clazz = clazz.getSuperclass();
+            }
+            return false;
+        }).forEach(clazz -> {
+
+            for (Field field : clazz.getFields())
+            {
+                addField(field.getName(), clazz, reflectConfigs);
+            }
+        });
+
+    }
+
+    public static String json(ReflectConfig reflectConfig)
+    {
+        StringBuilder builder = new StringBuilder();
+        builder.append(ind(1)).append(CLASS_START);
+
+        builder.append(ind(2)).append(String.format(CLASS_NAME, reflectConfig.className));
+
+        AtomicReference<String> comma = new AtomicReference<>("");
+        if (!reflectConfig.fields.isEmpty())
+        {
+            builder.append(COMMA_ENTER).append(ind(2)).append(FIELDS_START);
+            reflectConfig.fields.forEach(
+                f -> builder.append(comma.getAndSet(COMMA_ENTER)).append(ind(3)).append(
+                    "{").append(String.format(FIELD_NAME, f)).append("}"));
+            builder.append('\n').append(ind(2)).append(FIELDS_END);
+        }
+
+        comma.set("");
+        if (!reflectConfig.methods.isEmpty() || !reflectConfig.constructor.isEmpty())
+        {
+            builder.append(COMMA_ENTER).append(ind(2)).append(METHODS_START);
+            reflectConfig.constructor.forEach(c -> {
+                builder.append(comma.getAndSet(COMMA_ENTER)).append(ind(3)).append(
+                    "{").append(String.format(METHOD_NAME, CONSTRUCTOR_METHOD_NAME));
+
+                if (c.methodParameterTypes != null)
+                {
+                    String types = Stream.of(c.methodParameterTypes).sequential().collect(
+                        Collectors.joining("\",\""));
+                    if (!types.isEmpty())
+                    {
+                        types = "\"" + types + "\"";
+                    }
+                    builder.append(COMMA_SPACE).append(
+                        String.format(PARAMETER_TYPE, types));
+                }
+                builder.append("}");
+            });
+            reflectConfig.methods.forEach(m -> {
+                builder.append(comma.getAndSet(COMMA_ENTER))//
+                .append(ind(3)).append("{").append(
+                    String.format(METHOD_NAME, m.name));
+                if (m.methodParameterTypes != null)
+                {
+                    String types = Stream.of(m.methodParameterTypes).sequential().collect(
+                        Collectors.joining("\",\""));
+                    if (!types.isEmpty())
+                    {
+                        types = "\"" + types + "\"";
+                    }
+                    builder.append(COMMA_SPACE).append(
+                        String.format(PARAMETER_TYPE, types));
+                }
+                builder.append("}");
+            });
+            builder.append('\n').append(ind(2)).append(METHODS_END);
+        }
+
+        builder.append('\n').append(ind(1)).append(CLASS_END);
+
+        return builder.toString();
+    }
+
+    private static Object ind(int num)
+    {
+        StringBuilder indent = new StringBuilder();
+        for (int i = 0; i < num; i++)
+        {
+            indent.append("  ");
+        }
+        return indent.toString();
+    }
+
+    public static String json(List<ReflectConfig> reflectConfigs)
+    {
+        StringBuilder builder = new StringBuilder();
+        builder.append('[').append('\n');
+        AtomicReference<String> comma = new AtomicReference<>("");
+
+        Collections.sort(reflectConfigs, new Comparator<ReflectConfig>()
+        {
+            @Override
+            public int compare(ReflectConfig o1, ReflectConfig o2)
+            {
+                return o1.className.compareTo(o2.className);
+            }
+        });
+
+        for (ReflectConfig config : reflectConfigs)
+        {
+            builder.append(comma.getAndSet(COMMA_ENTER)).append(json(config));
+        }
+        builder.append('\n').append(']');
+        return builder.toString();
+    }
+
+    private static void discoverBundleActivators(URLClassLoader cl, JarFile jar,
+        List<ReflectConfig> reflectConfigs) throws IOException
+    {
+
+        Attributes attributes = jar.getManifest().getMainAttributes();
+        String bundleActivatorClassName = attributes.getValue(Constants.BUNDLE_ACTIVATOR);
+        if (bundleActivatorClassName == null)
+        {
+            bundleActivatorClassName = attributes.getValue(
+                Constants.EXTENSION_BUNDLE_ACTIVATOR);
+        }
+        if (bundleActivatorClassName != null)
+        {
+            bundleActivatorClassName = bundleActivatorClassName.trim();
+            try
+            {
+                Class<?> bundleActivatorClazz = cl.loadClass(bundleActivatorClassName);
+                Class<?> bundleContextClass = cl.loadClass(
+                    "org.osgi.framework.BundleContext");
+                ReflectConfig rc = computeIfAbsent(reflectConfigs,
+                    bundleActivatorClassName);
+                rc.constructor.add(new ConstructorConfig(new String[] {}));
+
+                addMethod("start", new Class[] { bundleContextClass },
+                    bundleActivatorClazz, reflectConfigs);
+                addMethod("stop", new Class[] { bundleContextClass },
+                    bundleActivatorClazz, reflectConfigs);
+                magic(cl, bundleActivatorClazz, reflectConfigs);
+            }
+            catch (ClassNotFoundException e)
+            {
+                // TODO log
+            }
+        }
+    }
+
+    private static ReflectConfig computeIfAbsent(List<ReflectConfig> reflectConfigs,
+        final String activator)
+    {
+        Optional<ReflectConfig> oConfig = reflectConfigs.stream().filter(
+            c -> c.className.equals(activator)).findFirst();
+
+        ReflectConfig rc = null;
+        if (oConfig.isPresent())
+        {
+            rc = oConfig.get();
+        }
+        else
+        {
+            rc = new ReflectConfig(activator);
+            reflectConfigs.add(rc);
+        }
+        return rc;
+    }
+
+    private static void magic(URLClassLoader cl, Class<?> bundleActivatorClass,
+        List<ReflectConfig> reflectConfigs)
+    {
+        try
+        {
+
+            Object o = bundleActivatorClass.newInstance();
+
+            Method startMethod = null;
+            for (Method m : bundleActivatorClass.getMethods())
+            {
+                if (m.getName().equals("start") && m.getReturnType().equals(void.class)
+                    && m.getParameterCount() == 1
+                    && m.getParameters()[0].getParameterizedType().getTypeName().equals(
+                        "org.osgi.framework.BundleContext"))
+                {
+                    startMethod = m;
+                    break;
+                }
+            }
+
+            if (startMethod != null)
+            {
+                InvocationHandler invocationHandler = new InvocationHandler()
+                {
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args)
+                        throws Throwable
+                    {
+                        try
+                        {
+                            //System.out.println("----------------------------------");
+                            //System.out.println(method);
+                            if (method.getName().equals("registerService")
+                                && method.getParameterCount() == 3
+                                && method.getParameters()[1].getType().getTypeName().equals(
+                                    "java.lang.Object")
+                                && method.getParameters()[2].getType().getTypeName().equals(
+                                    "java.util.Dictionary"))
+                            {
+                                Object service = args[1];
+                                Dictionary<?, ?> dict = (Dictionary<?, ?>) args[2];
+                                //System.out.println("service: "+service.getClass());
+                                //System.out.println("cfg: "+dict);
+                                if (dict != null)
+                                {
+                                    Map<?, ?> dictCopy = Collections.list(
+                                        dict.keys()).stream().collect(
+                                            Collectors.toMap(Function.identity(),
+                                                dict::get));
+                                    if (dictCopy.containsKey("osgi.command.function"))
+                                    {
+                                        String[] functions = (String[]) dict.get(
+                                            "osgi.command.function");
+
+                                        addMethodsFromGogoCommand(reflectConfigs,
+                                            service.getClass(), functions);
+                                    }
+                                }
+                            }
+                            if (method.getName().equals("getBundleId"))
+                            {
+                                return 1L;
+                            }
+                            if (method.getName().equals("getVersion"))
+                            {
+                                return cl.loadClass(
+                                    "org.osgi.framework.Version").getConstructor(
+                                        int.class, int.class, int.class).newInstance(0, 0,
+                                            0);
+                            }
+                            else if (method.getReturnType().isInterface())
+                            {
+                                return Proxy.newProxyInstance(cl,
+                                    new Class[] { method.getReturnType() }, this);
+                            }
+                        }
+                        catch (Exception e)
+                        {
+                            // expected
+                            //e.printStackTrace();
+                        }
+                        return null;
+                    }
+                };
+                Object p = Proxy.newProxyInstance(cl,
+                    new Class[] { cl.loadClass("org.osgi.framework.BundleContext") },
+                    invocationHandler);
+                startMethod.invoke(o, p);
+            }
+        }
+        catch (Throwable e)
+        { // expected
+            // TODO Log and maybe show cp errors
+            // e.printStackTrace();
+        }
+
+    }
+
+    private static void discoverSeriviceComponents(URLClassLoader cl, JarFile jar,
+        List<ReflectConfig> reflectConfigs) throws Exception
+    {
+
+        List<ComponentMetadata> cDDTOs = readCDDTO(jar);
+
+        for (ComponentMetadata c : cDDTOs)
+        {
+            c.validate();
+            Class<?> clazz = cl.loadClass(c.getImplementationClassName());
+            // Activate Deactivate Modify
+            Optional.ofNullable(c.getActivate()).ifPresent(
+                (m) -> addMethod(m, clazz, reflectConfigs));
+            Optional.ofNullable(c.getModified()).ifPresent(
+                (m) -> addMethod(m, clazz, reflectConfigs));
+            Optional.ofNullable(c.getDeactivate()).ifPresent(
+                (m) -> addMethod(m, clazz, reflectConfigs));
+            if (c.getActivationFields() != null)
+            {
+                for (String fName : c.getActivationFields())
+                {
+                    addField(fName, clazz, reflectConfigs);
+                }
+            }
+            //Reference
+            if (c.getDependencies() != null)
+            {
+
+                String[] constrParams = new String[c.getNumberOfConstructorParameters()];
+                for (ReferenceMetadata r : c.getDependencies())
+                {
+                    Optional.ofNullable(r.getParameterIndex()).ifPresent(
+                        (i) -> constrParams[i] = r.getInterface());
+                    Optional.ofNullable(r.getField()).ifPresent(
+                        (f) -> addField(f, clazz, reflectConfigs));
+                    Optional.ofNullable(r.getBind()).ifPresent(
+                        (m) -> addMethod(m, clazz, reflectConfigs));
+                    Optional.ofNullable(r.getUpdated()).ifPresent(
+                        (m) -> addMethod(m, clazz, reflectConfigs));
+                    Optional.ofNullable(r.getUnbind()).ifPresent(
+                        (m) -> addMethod(m, clazz, reflectConfigs));
+                    Optional.ofNullable(r.getInterface()).ifPresent(
+                        (i) -> computeIfAbsent(reflectConfigs, i));
+                }
+                ReflectConfig config = computeIfAbsent(reflectConfigs, clazz.getName());
+
+                boolean foundConstructor = false;
+                if (c.getNumberOfConstructorParameters() == 0)
+                {
+                    config.constructor.add(new ConstructorConfig());
+                    foundConstructor = true;
+                }
+                else
+                {
+                    for (Constructor<?> constructor : clazz.getConstructors())
+                    {
+                        if (constructor.getParameterCount() != c.getNumberOfConstructorParameters())
+                        {
+
+                            continue;
+                        }
+                        boolean match = true;
+                        for (int j = 0; j < constructor.getParameterCount(); j++)
+                        {
+                            String p = constructor.getParameters()[j].getType().getName();
+                            String s = constrParams[j];
+                            if (s != null && !p.equals(s))
+                            {
+                                match = false;
+                                break;
+                            }
+                        }
+                        if (match)
+                        {
+                            String[] ps = Stream.of(constructor.getParameters()).map(
+                                p -> p.getType().getName()).toArray(String[]::new);
+                            config.constructor.add(new ConstructorConfig(ps));
+                            foundConstructor = true;
+                            break;
+                        }
+                    }
+                    if (!foundConstructor)
+                    {
+                        config.allPublicConstructors = true;
+                    }
+                }
+            }
+            // Gogo osgi.command.function
+            if (c.getProperties().containsKey(OSGI_COMMAND_FUNCTION))
+            {
+                Object oFunctions = c.getProperties().get(OSGI_COMMAND_FUNCTION);
+                String[] functions = null;
+                if (oFunctions instanceof String[])
+                {
+                    functions = (String[]) oFunctions;
+                }
+                else
+                {
+                    functions = new String[] { oFunctions.toString() };
+                }
+                addMethodsFromGogoCommand(reflectConfigs, clazz, functions);
+            }
+        }
+    }
+
+    private static void addMethodsFromGogoCommand(List<ReflectConfig> classes,
+        Class<?> clazz, String[] functions)
+    {
+        Class<?> tmpClass = clazz;
+
+        for (String function : functions)
+        {
+            addMethod(function, tmpClass, classes);
+        }
+        tmpClass = tmpClass.getSuperclass();
+
+        if (tmpClass != null && !tmpClass.equals(Object.class))
+        {
+            addMethodsFromGogoCommand(classes, tmpClass, functions);
+        }
+    }
+
+    private static List<ComponentMetadata> readCDDTO(JarFile jar) throws Exception
+    {
+
+        BundleLogger logger = new EmptyBundeLogger();
+        List<ComponentMetadata> list = new ArrayList<>();
+        Attributes attributes = jar.getManifest().getMainAttributes();
+        String descriptorLocations = attributes.getValue(
+            ComponentConstants.SERVICE_COMPONENT);
+        if (descriptorLocations == null)
+        {
+            return list;
+        }
+        StringTokenizer st = new StringTokenizer(descriptorLocations, ", ");
+        while (st.hasMoreTokens())
+        {
+            String descriptorLocation = st.nextToken();
+            InputStream stream = jar.getInputStream(jar.getEntry(descriptorLocation));
+            BufferedReader in = new BufferedReader(
+                new InputStreamReader(stream, "UTF-8"));
+            XmlHandler handler = new XmlHandler(new PathBundle(jar), logger, true, true);
+
+
+            KXml2SAXParser parser = new KXml2SAXParser(in);
+            parser.parseXML(handler);
+            list.addAll(handler.getComponentMetadataList());
+        }
+
+        //      //Felix SCR Version-> 2.1.17-SNAPSHOT
+        //      //https://github.com/apache/felix-dev/commit/2d035e21d69c2bb8892d5d5d3e1027befcc3c50b#diff-dad1c7cc45e5c46bca969c95ac501546
+        //      while (st.hasMoreTokens())
+        //      {
+        //          String descriptorLocation = st.nextToken();
+        //          InputStream stream = jar.getInputStream(jar.getEntry(descriptorLocation));
+        //          XmlHandler handler = new XmlHandler(new PathBundle(jar), logger, true, true);
+        //
+        //          final SAXParserFactory factory = SAXParserFactory.newInstance();
+        //          factory.setNamespaceAware(true);
+        //          final SAXParser parser = factory.newSAXParser();
+        //          parser.parse( stream, handler );
+        //          list.addAll(handler.getComponentMetadataList());
+        //      }
+
+        return list;
+    }
+
+    private static void addMethod(String mName, Class<?> clazz,
+        List<ReflectConfig> classes)
+    {
+        addMethod(mName, null, clazz, classes);
+    }
+
+    //parameterTypes==null means not Set
+    //parameterTypes=={} means no method parameter
+    private static void addMethod(String mName, Class<?>[] parameterTypes, Class<?> clazz,
+        List<ReflectConfig> reflectConfigs)
+    {
+        for (Method m : clazz.getDeclaredMethods())
+        {
+            if (mName.equals(m.getName()))
+            {
+                if (parameterTypes == null)
+                {
+                    ReflectConfig config = computeIfAbsent(reflectConfigs,
+                        clazz.getName());
+                    config.methods.add(new MethodConfig(mName, null));
+                    break;
+                }
+                else if (Arrays.equals(m.getParameterTypes(), parameterTypes))
+                {
+                    ReflectConfig config = computeIfAbsent(reflectConfigs,
+                        clazz.getName());
+                    String[] sParameterTypes = Stream.of(parameterTypes).sequential().map(
+                        Class::getName).toArray(String[]::new);
+                    config.methods.add(new MethodConfig(mName, sParameterTypes));
+                    break;
+                }
+            }
+        }
+        Class<?> superClass = clazz.getSuperclass();
+        if (superClass != null)
+        {
+            addMethod(mName, parameterTypes, superClass, reflectConfigs);
+        }
+    }
+
+    private static void addField(String fName, Class<?> clazz,
+        List<ReflectConfig> reflectConfig)
+    {
+        boolean exists = Stream.of(clazz.getDeclaredFields()).anyMatch(
+            f -> f.getName().equals(fName));
+        if (exists)
+        {
+            ReflectConfig config = computeIfAbsent(reflectConfig, clazz.getName());
+            config.fields.add(fName);
+        }
+
+        Class<?> superClass = clazz.getSuperclass();
+        if (superClass != null)
+        {
+            addField(fName, superClass, reflectConfig);
+        }
+    }
+
+}
diff --git a/atomos.maven/src/main/java/org/apache/felix/atomos/maven/ResourceConfigUtil.java b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/ResourceConfigUtil.java
new file mode 100644
index 0000000..10f8005
--- /dev/null
+++ b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/ResourceConfigUtil.java
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+
+public class ResourceConfigUtil
+{
+    //TODO: remove Duplicats with substrateService
+    private static Collection<String> EXCLUDE_DIRS = Collections.unmodifiableList(
+        Arrays.asList("META-INF/", "OSGI-INF/", "OSGI-OPT/"));
+    private static Collection<String> EXCLUDE_NAMES = Collections.unmodifiableList(
+        Arrays.asList("/packageinfo"));
+    private static String SERVICES = "META-INF/services/";
+    private static String CLASS_SUFFIX = ".class";
+    private static String START = "{\n";
+    private static String END = "\n}";
+    private static String COMMA = ",\n";
+    private static char NL = '\n';
+    private static String BUNDLES_START = "\"bundles\" : [\n";
+    private static String BUNDLE_NAME = "{ \"name\" : \"%s\" }";
+    private static String BUNDLES_END = "]";
+    private static String RESOURCES_START = "\"resources\" : [\n";
+    private static String RESOURCE_PATTERN = "{ \"pattern\" : \"%s\" }";
+    private static String RESOURCES_END = BUNDLES_END;
+    private static String FRENCH_BUNDLE_CLASS = "_fr.class";
+    private static String FRENCH_BUNDLE_PROPS = "_fr.properties";
+
+    public static ResourceConfigResult resourceConfig(List<Path> paths) throws IOException
+    {
+
+        Set<String> allResourceBundles = new TreeSet<>();
+        Set<String> allResourcePatterns = new TreeSet<>();
+        Set<String> allResourcePackages = new TreeSet<>();
+        discoverResources(paths, allResourceBundles, allResourcePatterns,
+            allResourcePackages);
+
+        ResourceConfigResult result = new ResourceConfigResult();
+        result.allResourceBundles = allResourceBundles;
+        result.allResourcePackages = allResourcePackages;
+        result.allResourcePatterns = allResourcePatterns;
+
+        return result;
+    }
+
+    public static String createResourceJson(ResourceConfigResult result)
+    {
+        //        if (true)
+        //        {
+        //            return "{\"resources\" : [ {\"pattern\": \"atomos/.*$\"} ] }";
+        //        }
+
+        TreeSet<String> allResourceBundles = new TreeSet<>((o1, o2) -> o1.compareTo(o2));
+        allResourceBundles.addAll(result.allResourceBundles);
+        TreeSet<String> allResourcePatterns = new TreeSet<>((o1, o2) -> o1.compareTo(o2));
+        allResourcePatterns.addAll(result.allResourcePatterns);
+
+        AtomicBoolean first = new AtomicBoolean();
+        StringBuilder resourceConfig = new StringBuilder();
+        resourceConfig.append(START);
+        if (!allResourceBundles.isEmpty())
+        {
+            resourceConfig.append(ind(1)).append(BUNDLES_START);
+            allResourceBundles.forEach(b -> {
+                if (!first.compareAndSet(false, true))
+                {
+                    resourceConfig.append(COMMA);
+                }
+                resourceConfig.append(ind(2)).append(String.format(BUNDLE_NAME, b));
+            });
+            resourceConfig.append(NL).append(ind(1)).append(BUNDLES_END);
+        }
+        first.set(false);
+        if (!allResourcePatterns.isEmpty())
+        {
+            if (!allResourceBundles.isEmpty())
+            {
+                resourceConfig.append(COMMA);
+            }
+            resourceConfig.append(ind(1)).append(RESOURCES_START);
+            allResourcePatterns.forEach(p -> {
+                if (!first.compareAndSet(false, true))
+                {
+                    resourceConfig.append(COMMA);
+                }
+                resourceConfig.append(ind(2)).append(String.format(RESOURCE_PATTERN, p));
+            });
+            resourceConfig.append(NL).append(ind(1)).append(RESOURCES_END);
+        }
+        resourceConfig.append(END);
+        return resourceConfig.toString();
+
+    }
+
+    private static void discoverResources(List<Path> paths,
+        Set<String> allResourceBundles, Set<String> allResourcePatterns,
+        Set<String> allResourcePackages) throws IOException
+    {
+        for (Path path : paths)
+        {
+
+            try (JarFile jar = new JarFile(path.toFile());)
+            {
+                pattern: for (JarEntry entry : jar.stream().collect(Collectors.toList()))
+                {
+                    String entryName = entry.getName();
+                    if (entry.isDirectory())
+                    {
+                        continue;
+                    }
+                    if (entryName.indexOf('/') == -1)
+                    {
+                        continue;
+                    }
+                    if (entryName.startsWith(SERVICES))
+                    {
+                        allResourcePatterns.add(entryName);
+                        continue;
+                    }
+                    for (String excluded : EXCLUDE_NAMES)
+                    {
+                        if (entryName.endsWith(excluded))
+                        {
+                            continue pattern;
+                        }
+                    }
+                    for (String excluded : EXCLUDE_DIRS)
+                    {
+                        if (entryName.startsWith(excluded))
+                        {
+                            continue pattern;
+                        }
+                    }
+                    if (entryName.endsWith(CLASS_SUFFIX))
+                    {
+                        // just looking for resource bundle for french as an indicator
+                        if (entryName.endsWith(FRENCH_BUNDLE_CLASS))
+                        {
+                            String bundleName = entryName.substring(0, entryName.length()
+                                - FRENCH_BUNDLE_CLASS.length()).replace('/', '.');
+                            String bundlePackage = bundleName.substring(0,
+                                bundleName.lastIndexOf('.'));
+                            allResourceBundles.add(bundleName);
+                            allResourcePackages.add(bundlePackage);
+                        }
+                        continue;
+                    }
+                    if (entryName.endsWith(FRENCH_BUNDLE_PROPS))
+                    {
+                        allResourceBundles.add(entryName.substring(0,
+                            entryName.length() - FRENCH_BUNDLE_PROPS.length()).replace(
+                                '/', '.'));
+                        continue;
+                    }
+                    allResourcePatterns.add(entryName);
+                }
+            }
+        }
+    }
+
+    private static Object ind(int num)
+    {
+        StringBuilder indent = new StringBuilder();
+        for (int i = 0; i < num; i++)
+        {
+            indent.append("  ");
+        }
+        return indent.toString();
+    }
+
+    static class ResourceConfigResult
+    {
+        Set<String> allResourceBundles = new TreeSet<>();
+        Set<String> allResourcePatterns = new TreeSet<>();
+        Set<String> allResourcePackages = new TreeSet<>();
+    }
+}
diff --git a/atomos.maven/src/main/java/org/apache/felix/atomos/maven/SubstrateUtil.java b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/SubstrateUtil.java
new file mode 100644
index 0000000..a946f71
--- /dev/null
+++ b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/SubstrateUtil.java
@@ -0,0 +1,244 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.felix.atomos.maven.ResourceConfigUtil.ResourceConfigResult;
+import org.osgi.framework.Constants;
+
+public class SubstrateUtil
+{
+
+    /**
+     *
+     */
+    private static final String ATOMOS_CATH_ALL = "atomos/.*$";
+    /**
+     *
+     */
+    private static final String ATOMOS_SUBSTRATE_JAR = "atomos.substrate.jar";
+    private static final Collection<String> DEFAULT_EXCLUDE_NAMES = Arrays.asList(
+        "about.html", "DEPENDENCIES", "LICENSE", "NOTICE", "changelog.txt",
+        "LICENSE.txt");
+    private static final Collection<String> DEFAULT_EXCLUDE_PATHS = Arrays.asList(
+        "META-INF/maven/", "OSGI-OPT/");
+    public static final String ATOMOS_BUNDLES_BASE_PATH = "atomos/";
+    public static final String ATOMOS_BUNDLES_INDEX = ATOMOS_BUNDLES_BASE_PATH
+        + "bundles.index";
+    private static final String ATOMOS_BUNDLE_SEPARATOR = "ATOMOS_BUNDLE";
+
+    enum EntryType
+    {
+        PACKAGE, NON_PACKAGE, PACKAGE_CLASS, PACKAGE_RESOURCE, DEFAULT_PACKAGE_CLASS, DEFAULT_PACKAGE_RESOURCE, NON_PACKAGE_RESOURCE
+    }
+
+    private static boolean isClass(String path)
+    {
+        return path.endsWith(".class");
+    }
+
+    private static boolean filter(JarEntry entry)
+    {
+        final String path = entry.getName();
+        if (entry.isDirectory() || isClass(path))
+        {
+            return false;
+        }
+        for (final String excludedPath : DEFAULT_EXCLUDE_PATHS)
+        {
+            if (path.startsWith(excludedPath))
+            {
+                return false;
+            }
+        }
+        for (final String excludedName : DEFAULT_EXCLUDE_NAMES)
+        {
+            if (path.endsWith(excludedName))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static Path substrate(List<Path> files, Path outputDir)
+        throws IOException, NoSuchAlgorithmException
+    {
+        if (!outputDir.toFile().isDirectory())
+        {
+            throw new IllegalArgumentException(
+                "Output file must be a directory." + outputDir);
+        }
+        if (!outputDir.toFile().exists())
+        {
+            Files.createDirectories(outputDir);
+        }
+
+        final Path p = outputDir.resolve(ATOMOS_SUBSTRATE_JAR);
+
+        final Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
+        try (final JarOutputStream z = new JarOutputStream(
+            new FileOutputStream(p.toFile()), manifest);)
+        {
+
+            final List<String> bundleIndexLines = new ArrayList<>();
+            final List<String> resources = new ArrayList<>();
+            final AtomicLong counter = new AtomicLong(0);
+            final Stream<SubstrateInfo> bis = files.stream()//
+                .map(path -> create(z, counter.getAndIncrement(), path));
+
+            bis.forEach(s -> {
+                if (s.bsn != null)
+                {
+                    bundleIndexLines.add(ATOMOS_BUNDLE_SEPARATOR);
+                    bundleIndexLines.add(s.id);
+                    bundleIndexLines.add(s.bsn);
+                    bundleIndexLines.add(s.version);
+                    s.files.forEach(f -> {
+                        bundleIndexLines.add(f);
+                        resources.add(ATOMOS_BUNDLES_BASE_PATH + s.id + "/" + f);
+                    });
+                }
+            });
+            writeBundleIndexFile(z, bundleIndexLines);
+            writeGraalResourceConfig(z, resources);
+        }
+        return p;
+    }
+
+    private static void writeGraalResourceConfig(JarOutputStream jos,
+        List<String> resources) throws IOException
+    {
+        //        resources.add(ATOMOS_BUNDLES_INDEX);
+        resources.add(ATOMOS_CATH_ALL); // This alone could be enough,
+
+        final ResourceConfigResult result = new ResourceConfigResult();
+        result.allResourcePatterns.addAll(resources);
+
+        final String graalResConfJson = ResourceConfigUtil.createResourceJson(result);
+
+        final JarEntry graalResConfEntry = new JarEntry(
+            "META-INF/native-image/resource-config.json");
+        jos.putNextEntry(graalResConfEntry);
+        jos.write(graalResConfJson.getBytes());
+
+    }
+
+    private static void writeBundleIndexFile(JarOutputStream jos,
+        final List<String> resources) throws IOException
+    {
+
+        final JarEntry atomosIndexEntry = new JarEntry(ATOMOS_BUNDLES_INDEX);
+        jos.putNextEntry(atomosIndexEntry);
+
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out)))
+        {
+            resources.forEach((l) -> {
+                try
+                {
+                    writer.append(l).append('\n');
+                }
+                catch (final IOException ex)
+                {
+                    throw new UncheckedIOException(ex);
+                }
+            });
+        }
+        jos.write(out.toByteArray());
+
+    }
+
+    static SubstrateInfo create(JarOutputStream jos, long id, Path path)
+    {
+        final SubstrateInfo info = new SubstrateInfo();
+        info.path = path;
+        try (final JarFile jar = new JarFile(info.path.toFile()))
+        {
+            final Attributes attributes = jar.getManifest().getMainAttributes();
+            info.bsn = attributes.getValue(Constants.BUNDLE_SYMBOLICNAME);
+            info.version = attributes.getValue(Constants.BUNDLE_VERSION);
+            info.id = Long.toString(id);
+
+            if (info.bsn == null)
+            {
+                return info;
+            }
+            if (info.version == null)
+            {
+                info.version = "0.0";
+            }
+            info.files = jar.stream().filter(j -> filter(j)).peek(j -> {
+                try
+                {
+                    final JarEntry entry = new JarEntry(
+                        ATOMOS_BUNDLES_BASE_PATH + id + "/" + j.getName());
+                    if (j.getCreationTime() != null)
+                    {
+                        entry.setCreationTime(j.getCreationTime());
+                    }
+                    if (j.getComment() != null)
+                    {
+                        entry.setComment(j.getComment());
+                    }
+                    jos.putNextEntry(entry);
+                    jos.write(jar.getInputStream(j).readAllBytes());
+                }
+                catch (final IOException e)
+                {
+                    throw new UncheckedIOException(e);
+                }
+
+            }).map(JarEntry::getName).collect(Collectors.toList());
+        }
+        catch (final IOException e)
+        {
+            throw new UncheckedIOException(e);
+        }
+        return info;
+    }
+
+    static class SubstrateInfo
+    {
+        Path out;
+        Path path;
+        String id;
+        String bsn;
+        String version;
+        List<String> files = new ArrayList<>();
+
+    }
+}
diff --git a/atomos.maven/src/main/java/org/apache/felix/atomos/maven/reflect/ConstructorConfig.java b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/reflect/ConstructorConfig.java
new file mode 100644
index 0000000..94c2349
--- /dev/null
+++ b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/reflect/ConstructorConfig.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven.reflect;
+
+import java.util.Arrays;
+
+public class ConstructorConfig
+{
+    public String[] methodParameterTypes;
+
+    public ConstructorConfig()
+    {
+        this(new String[] {});
+    }
+
+    public ConstructorConfig(String[] methodParameters)
+    {
+        methodParameterTypes = methodParameters;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "ConstructorConfig [methodParameters=" + Arrays.toString(methodParameterTypes)
+        + "]";
+    }
+}
+
+
diff --git a/atomos.maven/src/main/java/org/apache/felix/atomos/maven/reflect/MethodConfig.java b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/reflect/MethodConfig.java
new file mode 100644
index 0000000..09e5ff0
--- /dev/null
+++ b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/reflect/MethodConfig.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven.reflect;
+
+import java.util.Arrays;
+
+public class MethodConfig extends ConstructorConfig
+{
+    @Override
+    public String toString()
+    {
+        return "MethodConfig [name=" + name + ", methodParameters="
+            + Arrays.toString(methodParameterTypes) + "]";
+    }
+
+    public String name;
+
+    public MethodConfig(String name, String[] methodParameters)
+    {
+        super(methodParameters);
+        this.name = name;
+    }
+
+
+}
\ No newline at end of file
diff --git a/atomos.maven/src/main/java/org/apache/felix/atomos/maven/reflect/ReflectConfig.java b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/reflect/ReflectConfig.java
new file mode 100644
index 0000000..f3dacfe
--- /dev/null
+++ b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/reflect/ReflectConfig.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven.reflect;
+
+import java.util.Comparator;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class ReflectConfig
+{
+    static Comparator<MethodConfig> mc = new Comparator<>()
+    {
+
+        @Override
+        public int compare(MethodConfig o1, MethodConfig o2)
+        {
+            int i = o1.name.compareTo(o2.name);
+            if (i == 0)
+            {
+                return cc.compare(o1, o2);
+            }
+            return i;
+        }
+    };
+
+    static Comparator<ConstructorConfig> cc = new Comparator<>()
+    {
+
+        @Override
+        public int compare(ConstructorConfig o1, ConstructorConfig o2)
+        {
+            String s1 = o1.methodParameterTypes == null ? ""
+                : Stream.of(o1.methodParameterTypes).collect(Collectors.joining(","));
+            String s2 = o2.methodParameterTypes == null ? ""
+                : Stream.of(o2.methodParameterTypes).collect(Collectors.joining(","));
+
+            return s1.compareTo(s2);
+        }
+    };
+    public String className;
+
+    public boolean allPublicConstructors = false;
+    public Set<ConstructorConfig> constructor = new TreeSet<>(cc);
+    public Set<String> fields = new TreeSet<>();
+    public Set<MethodConfig> methods = new TreeSet<>(mc);
+
+    public ReflectConfig(String className)
+    {
+        this.className = className;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "ReflectConfig [className=" + className + ", allPublicConstructors="
+            + allPublicConstructors + ", constructor=" + constructor + ", fields="
+            + fields + ", methods=" + methods + "]";
+    }
+
+    @Override
+    public boolean equals(Object other)
+    {
+        if (!(other instanceof ReflectConfig))
+        {
+            return false;
+        }
+        return className == ((ReflectConfig) other).className;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return className.hashCode();
+    }
+
+}
diff --git a/atomos.maven/src/main/java/org/apache/felix/atomos/maven/scrmock/EmptyBundeLogger.java b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/scrmock/EmptyBundeLogger.java
new file mode 100644
index 0000000..1270f34
--- /dev/null
+++ b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/scrmock/EmptyBundeLogger.java
@@ -0,0 +1,468 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven.scrmock;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.scr.impl.logger.BundleLogger;
+import org.apache.felix.scr.impl.logger.ScrLogger;
+import org.apache.felix.scr.impl.manager.ScrConfiguration;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceObjects;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.Version;
+
+public class EmptyBundeLogger extends BundleLogger
+{
+    static Bundle b = new Bundle()
+    {
+        @Override
+        public int compareTo(Bundle o)
+        {
+            return 0;
+        }
+
+        @Override
+        public void update(InputStream input) throws BundleException
+        {
+        }
+
+        @Override
+        public void update() throws BundleException
+        {
+        }
+
+        @Override
+        public void uninstall() throws BundleException
+        {
+        }
+
+        @Override
+        public void stop(int options) throws BundleException
+        {
+        }
+
+        @Override
+        public void stop() throws BundleException
+        {
+        }
+
+        @Override
+        public void start(int options) throws BundleException
+        {
+        }
+
+        @Override
+        public void start() throws BundleException
+        {
+        }
+
+        @Override
+        public Class<?> loadClass(String name) throws ClassNotFoundException
+        {
+            return null;
+        }
+
+        @Override
+        public boolean hasPermission(Object permission)
+        {
+            return false;
+        }
+
+        @Override
+        public Version getVersion()
+        {
+            return null;
+        }
+
+        @Override
+        public String getSymbolicName()
+        {
+            return null;
+        }
+
+        @Override
+        public int getState()
+        {
+            return 0;
+        }
+
+        @Override
+        public Map<X509Certificate, List<X509Certificate>> getSignerCertificates(
+            int signersType)
+        {
+            return null;
+        }
+
+        @Override
+        public ServiceReference<?>[] getServicesInUse()
+        {
+            return null;
+        }
+
+        @Override
+        public Enumeration<URL> getResources(String name) throws IOException
+        {
+            return null;
+        }
+
+        @Override
+        public URL getResource(String name)
+        {
+            return null;
+        }
+
+        @Override
+        public ServiceReference<?>[] getRegisteredServices()
+        {
+            return null;
+        }
+
+        @Override
+        public String getLocation()
+        {
+            return null;
+        }
+
+        @Override
+        public long getLastModified()
+        {
+            return 0;
+        }
+
+        @Override
+        public Dictionary<String, String> getHeaders(String locale)
+        {
+            return null;
+        }
+
+        @Override
+        public Dictionary<String, String> getHeaders()
+        {
+            return null;
+        }
+
+        @Override
+        public Enumeration<String> getEntryPaths(String path)
+        {
+            return null;
+        }
+
+        @Override
+        public URL getEntry(String path)
+        {
+            return null;
+        }
+
+        @Override
+        public File getDataFile(String filename)
+        {
+            return null;
+        }
+
+        @Override
+        public long getBundleId()
+        {
+            return 0;
+        }
+
+        @Override
+        public BundleContext getBundleContext()
+        {
+            return null;
+        }
+
+        @Override
+        public Enumeration<URL> findEntries(String path, String filePattern,
+            boolean recurse)
+        {
+            return null;
+        }
+
+        @Override
+        public <A> A adapt(Class<A> type)
+        {
+            return null;
+        }
+    };
+    static BundleContext bc = new BundleContext()
+    {
+        @Override
+        public boolean ungetService(ServiceReference<?> reference)
+        {
+            return false;
+        }
+
+        @Override
+        public void removeServiceListener(ServiceListener listener)
+        {
+        }
+
+        @Override
+        public void removeFrameworkListener(FrameworkListener listener)
+        {
+        }
+
+        @Override
+        public void removeBundleListener(BundleListener listener)
+        {
+        }
+
+        @Override
+        public <S> ServiceRegistration<S> registerService(Class<S> clazz,
+            ServiceFactory<S> factory, Dictionary<String, ?> properties)
+        {
+            return null;
+        }
+
+        @Override
+        public <S> ServiceRegistration<S> registerService(Class<S> clazz, S service,
+            Dictionary<String, ?> properties)
+        {
+            return null;
+        }
+
+        @Override
+        public ServiceRegistration<?> registerService(String clazz, Object service,
+            Dictionary<String, ?> properties)
+        {
+            return null;
+        }
+
+        @Override
+        public ServiceRegistration<?> registerService(String[] clazzes, Object service,
+            Dictionary<String, ?> properties)
+        {
+            return null;
+        }
+
+        @Override
+        public Bundle installBundle(String location, InputStream input)
+            throws BundleException
+        {
+            return null;
+        }
+
+        @Override
+        public Bundle installBundle(String location) throws BundleException
+        {
+            return null;
+        }
+
+        @Override
+        public <S> Collection<ServiceReference<S>> getServiceReferences(Class<S> clazz,
+            String filter) throws InvalidSyntaxException
+        {
+            return null;
+        }
+
+        @Override
+        public ServiceReference<?>[] getServiceReferences(String clazz, String filter)
+            throws InvalidSyntaxException
+        {
+            return null;
+        }
+
+        @Override
+        public <S> ServiceReference<S> getServiceReference(Class<S> clazz)
+        {
+            return null;
+        }
+
+        @Override
+        public ServiceReference<?> getServiceReference(String clazz)
+        {
+            return null;
+        }
+
+        @Override
+        public <S> ServiceObjects<S> getServiceObjects(ServiceReference<S> reference)
+        {
+            return null;
+        }
+
+        @Override
+        public <S> S getService(ServiceReference<S> reference)
+        {
+            return null;
+        }
+
+        @Override
+        public String getProperty(String key)
+        {
+            return null;
+        }
+
+        @Override
+        public File getDataFile(String filename)
+        {
+            return null;
+        }
+
+        @Override
+        public Bundle[] getBundles()
+        {
+            return null;
+        }
+
+        @Override
+        public Bundle getBundle(String location)
+        {
+            return null;
+        }
+
+        @Override
+        public Bundle getBundle(long id)
+        {
+            return null;
+        }
+
+        @Override
+        public Bundle getBundle()
+        {
+            return b;
+        }
+
+        @Override
+        public ServiceReference<?>[] getAllServiceReferences(String clazz, String filter)
+            throws InvalidSyntaxException
+        {
+            return null;
+        }
+
+        @Override
+        public Filter createFilter(String filter) throws InvalidSyntaxException
+        {
+            return null;
+        }
+
+        @Override
+        public void addServiceListener(ServiceListener listener, String filter)
+            throws InvalidSyntaxException
+        {
+        }
+
+        @Override
+        public void addServiceListener(ServiceListener listener)
+        {
+        }
+
+        @Override
+        public void addFrameworkListener(FrameworkListener listener)
+        {
+        }
+
+        @Override
+        public void addBundleListener(BundleListener listener)
+        {
+        }
+    };
+    static ScrConfiguration conf = new ScrConfiguration()
+    {
+        @Override
+        public long stopTimeout()
+        {
+            return 0;
+        }
+
+        @Override
+        public long serviceChangecountTimeout()
+        {
+            return 0;
+        }
+
+        @Override
+        public long lockTimeout()
+        {
+            return 0;
+        }
+
+        @Override
+        public boolean keepInstances()
+        {
+            return false;
+        }
+
+        @Override
+        public boolean isFactoryEnabled()
+        {
+            return false;
+        }
+
+        @Override
+        public boolean infoAsService()
+        {
+            return false;
+        }
+
+        @Override
+        public boolean globalExtender()
+        {
+            return false;
+        }
+
+        @Override
+        public int getLogLevel()
+        {
+            return 0;
+        }
+    };
+    static ScrLogger scrLogger = new EmptySCRLogger(conf, bc);
+
+    public EmptyBundeLogger()
+    {
+        super(bc, scrLogger);
+    }
+
+    @Override
+    public boolean log(int level, String pattern, Throwable ex, Object... arguments)
+    {
+        return true;
+    }
+
+    @Override
+    public boolean log(int level, String message, Throwable ex)
+    {
+        return true;
+    }
+
+    @Override
+    public void close()
+    {
+    }
+
+    @Override
+    public boolean isLogEnabled(int level)
+    {
+        return false;
+    }
+}
diff --git a/atomos.maven/src/main/java/org/apache/felix/atomos/maven/scrmock/EmptySCRLogger.java b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/scrmock/EmptySCRLogger.java
new file mode 100644
index 0000000..8402205
--- /dev/null
+++ b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/scrmock/EmptySCRLogger.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven.scrmock;
+
+import org.apache.felix.scr.impl.logger.ScrLogger;
+import org.apache.felix.scr.impl.manager.ScrConfiguration;
+import org.osgi.framework.BundleContext;
+
+public class EmptySCRLogger extends ScrLogger
+{
+    public EmptySCRLogger(ScrConfiguration config, BundleContext bundleContext)
+    {
+        super(config, bundleContext);
+    }
+
+    @Override
+    public void close()
+    {
+    }
+
+    @Override
+    public boolean isLogEnabled(int level)
+    {
+        return false;
+    }
+
+    @Override
+    public boolean log(int level, String pattern, Throwable ex, Object... arguments)
+    {
+        return true;
+    }
+
+    @Override
+    public boolean log(int level, String message, Throwable ex)
+    {
+        return true;
+    }
+}
diff --git a/atomos.maven/src/main/java/org/apache/felix/atomos/maven/scrmock/PathBundle.java b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/scrmock/PathBundle.java
new file mode 100644
index 0000000..4d43cae
--- /dev/null
+++ b/atomos.maven/src/main/java/org/apache/felix/atomos/maven/scrmock/PathBundle.java
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven.scrmock;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.cert.X509Certificate;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarFile;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+
+public class PathBundle implements Bundle
+{
+    public PathBundle(JarFile jar)
+    {
+    }
+
+    @Override
+    public int compareTo(Bundle o)
+    {
+        return 0;
+    }
+
+    @Override
+    public int getState()
+    {
+        return 0;
+    }
+
+    @Override
+    public void start(int options) throws BundleException
+    {
+    }
+
+    @Override
+    public void start() throws BundleException
+    {
+    }
+
+    @Override
+    public void stop(int options) throws BundleException
+    {
+    }
+
+    @Override
+    public void stop() throws BundleException
+    {
+    }
+
+    @Override
+    public void update(InputStream input) throws BundleException
+    {
+    }
+
+    @Override
+    public void update() throws BundleException
+    {
+    }
+
+    @Override
+    public void uninstall() throws BundleException
+    {
+    }
+
+    @Override
+    public Dictionary<String, String> getHeaders()
+    {
+        return null;
+    }
+
+    @Override
+    public long getBundleId()
+    {
+        return 0;
+    }
+
+    @Override
+    public String getLocation()
+    {
+        return null;
+    }
+
+    @Override
+    public ServiceReference<?>[] getRegisteredServices()
+    {
+        return null;
+    }
+
+    @Override
+    public ServiceReference<?>[] getServicesInUse()
+    {
+        return null;
+    }
+
+    @Override
+    public boolean hasPermission(Object permission)
+    {
+        return false;
+    }
+
+    @Override
+    public URL getResource(String name)
+    {
+        return null;
+    }
+
+    @Override
+    public Dictionary<String, String> getHeaders(String locale)
+    {
+        return null;
+    }
+
+    @Override
+    public String getSymbolicName()
+    {
+        return null;
+    }
+
+    @Override
+    public Class<?> loadClass(String name) throws ClassNotFoundException
+    {
+        return null;
+    }
+
+    @Override
+    public Enumeration<URL> getResources(String name) throws IOException
+    {
+        return null;
+    }
+
+    @Override
+    public Enumeration<String> getEntryPaths(String path)
+    {
+        return null;
+    }
+
+    @Override
+    public URL getEntry(String path)
+    {
+        return null;
+    }
+
+    @Override
+    public long getLastModified()
+    {
+        return 0;
+    }
+
+    @Override
+    public Enumeration<URL> findEntries(String path, String filePattern, boolean recurse)
+    {
+        return null;
+    }
+
+    @Override
+    public BundleContext getBundleContext()
+    {
+        return null;
+    }
+
+    @Override
+    public Map<X509Certificate, List<X509Certificate>> getSignerCertificates(
+        int signersType)
+    {
+        return null;
+    }
+
+    @Override
+    public Version getVersion()
+    {
+        return null;
+    }
+
+    @Override
+    public <A> A adapt(Class<A> type)
+    {
+        return null;
+    }
+
+    @Override
+    public File getDataFile(String filename)
+    {
+        return null;
+    }
+}
diff --git a/atomos.maven/src/test/java/org/apache/felix/atomos/maven/MojoTest.java b/atomos.maven/src/test/java/org/apache/felix/atomos/maven/MojoTest.java
new file mode 100644
index 0000000..7c8f0b1
--- /dev/null
+++ b/atomos.maven/src/test/java/org/apache/felix/atomos/maven/MojoTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven;
+
+import static org.apache.felix.atomos.maven.TestConstants.getAllDependencys;
+
+import java.nio.file.Path;
+import java.util.List;
+
+import org.apache.felix.atomos.maven.ResourceConfigUtil.ResourceConfigResult;
+import org.apache.felix.atomos.maven.reflect.ReflectConfig;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+public class MojoTest extends TestBase
+{
+
+    @Test
+    void testFull(@TempDir Path tempDir) throws Exception
+    {
+
+        List<Path> paths = getAllDependencys();
+
+        SubstrateUtil.substrate(paths, tempDir);
+        List<ReflectConfig> reflectConfigs = ReflectConfigUtil.reflectConfig(paths);
+        ResourceConfigResult resourceConfigResult = ResourceConfigUtil.resourceConfig(
+            paths);
+
+        List<String> args = NativeImageBuilder.createExecutionArgs(List.of(), List.of(),
+            List.of(), List.of(), resourceConfigResult, true, "", "");
+        //        for (String arg : args)
+        //        {
+        //            System.out.println(arg);
+        //
+        //        }
+        //
+    }
+}
\ No newline at end of file
diff --git a/atomos.maven/src/test/java/org/apache/felix/atomos/maven/MojoTestExperiments.java b/atomos.maven/src/test/java/org/apache/felix/atomos/maven/MojoTestExperiments.java
new file mode 100644
index 0000000..ace9ce0
--- /dev/null
+++ b/atomos.maven/src/test/java/org/apache/felix/atomos/maven/MojoTestExperiments.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven;
+
+import static org.apache.felix.atomos.maven.TestConstants.DEP_FELIX_HTTP_SERVLET_API;
+import static org.apache.felix.atomos.maven.TestConstants.DEP_FELIX_WEBCONSOLE;
+import static org.apache.felix.atomos.maven.TestConstants.DEP_ORG_OSGI_CORE;
+import static org.apache.felix.atomos.maven.TestConstants.DEP_ORG_OSGI_SERVICE_HTTP;
+import static org.apache.felix.atomos.maven.TestConstants.DEP_ORG_OSGI_SERVICE_LOG;
+import static org.apache.felix.atomos.maven.TestConstants.getDependencys;
+
+import java.nio.file.Path;
+import java.util.List;
+
+import org.apache.felix.atomos.maven.reflect.ReflectConfig;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+public class MojoTestExperiments extends TestBase
+{
+
+    @Test
+    void testReflectBundleActivatorMagicTests(@TempDir Path tempDir) throws Exception
+    {
+        List<Path> paths = getDependencys(DEP_FELIX_WEBCONSOLE, DEP_ORG_OSGI_SERVICE_LOG,
+            //            DEP_FELIX_GOGO_RUNTIME, DEP_FELIX_GOGO_COMMAND, DEP_FELIX_SCR,
+            DEP_ORG_OSGI_CORE, DEP_FELIX_HTTP_SERVLET_API, DEP_ORG_OSGI_SERVICE_HTTP);
+
+        List<ReflectConfig> rcs = ReflectConfigUtil.reflectConfig(paths);
+
+        System.out.println(ReflectConfigUtil.json(rcs));
+
+    }
+
+}
\ No newline at end of file
diff --git a/atomos.maven/src/test/java/org/apache/felix/atomos/maven/ReflectConfigTest.java b/atomos.maven/src/test/java/org/apache/felix/atomos/maven/ReflectConfigTest.java
new file mode 100644
index 0000000..a070fb3
--- /dev/null
+++ b/atomos.maven/src/test/java/org/apache/felix/atomos/maven/ReflectConfigTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven;
+
+import static org.apache.felix.atomos.maven.TestConstants.DEP_ATOMOS_TESTS_TESTBUNDLES_REFLECT_COMMAND;
+import static org.apache.felix.atomos.maven.TestConstants.DEP_ATOMOS_TESTS_TESTBUNDLES_REFLECT_DTO;
+import static org.apache.felix.atomos.maven.TestConstants.DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_CONTRACT;
+import static org.apache.felix.atomos.maven.TestConstants.DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_IMPL;
+import static org.apache.felix.atomos.maven.TestConstants.DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_IMPL_ACTIVATOR;
+import static org.apache.felix.atomos.maven.TestConstants.DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_USER;
+import static org.apache.felix.atomos.maven.TestConstants.DEP_ORG_OSGI_CORE;
+import static org.apache.felix.atomos.maven.TestConstants.DEP_ORG_OSGI_DTO;
+import static org.apache.felix.atomos.maven.TestConstants.filterConstructor;
+import static org.apache.felix.atomos.maven.TestConstants.filterMethod;
+import static org.apache.felix.atomos.maven.TestConstants.filterReflectConfigByClassName;
+import static org.apache.felix.atomos.maven.TestConstants.getDependencys;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.felix.atomos.maven.reflect.ConstructorConfig;
+import org.apache.felix.atomos.maven.reflect.MethodConfig;
+import org.apache.felix.atomos.maven.reflect.ReflectConfig;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+public class ReflectConfigTest extends TestBase
+{
+    @Test
+    void testJsonSimple() throws Exception
+    {
+        ReflectConfig rc = new ReflectConfig("a");
+        String json = ReflectConfigUtil.json(rc);
+        json = shrinkJson(json);
+        assertEquals("{\"name\":\"a\"}", json);
+    }
+
+    @Test
+    void testJson() throws Exception
+    {
+        ReflectConfig rc = new ReflectConfig("a");
+        rc.fields.add("f1");
+        rc.fields.add("f2");
+
+        rc.constructor.add(new ConstructorConfig());
+        rc.constructor.add(new ConstructorConfig(new String[] { "p1" }));
+
+        rc.methods.add(new MethodConfig("m1", null));
+        rc.methods.add(new MethodConfig("m2", new String[] { "m2p1" }));
+        rc.methods.add(new MethodConfig("m3", new String[] { "m3p1", "m3p2" }));
+
+        String json = ReflectConfigUtil.json(rc);
+        json = shrinkJson(json);
+        String exp = "{\"name\":\"a\",\"fields\":[{\"name\":\"f1\"},{\"name\":\"f2\"}],\"methods\":[{\"name\":\"<init>\",\"parameterTypes\":[]},{\"name\":\"<init>\",\"parameterTypes\":[\"p1\"]},{\"name\":\"m1\"},{\"name\":\"m2\",\"parameterTypes\":[\"m2p1\"]},{\"name\":\"m3\",\"parameterTypes\":[\"m3p1\",\"m3p2\"]}]}";
+        assertEquals(exp, json);
+    }
+
+
+    private String shrinkJson(String json)
+    {
+        json = json.replace(" ", "").replace("\n", "");
+        return json;
+    }
+
+    @Test
+    void testJsonFull() throws Exception
+    {
+        List<ReflectConfig> rcs = new ArrayList<>();
+        rcs.add(new ReflectConfig("z"));
+        rcs.add(new ReflectConfig("a"));
+        String json = ReflectConfigUtil.json(rcs);
+        json = shrinkJson(json);
+        assertEquals("[{\"name\":\"a\"},{\"name\":\"z\"}]", json);
+    }
+
+    @Test
+    void testActivateMethod(@TempDir Path tempDir) throws Exception
+    {
+        List<Path> paths = getDependencys(DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_CONTRACT,
+            DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_IMPL);
+        List<ReflectConfig> rcs = ReflectConfigUtil.reflectConfig(paths);
+        //System.out.println(ReflectConfigUtil.json(rcs));
+
+        ReflectConfig rc = filterReflectConfigByClassName(rcs,
+            "org.apache.felix.atomos.tests.testbundles.service.impl.EchoImpl");
+        assertThat(rc.fields).isEmpty();
+
+        Optional<ConstructorConfig> oc = filterConstructor(rc, new String[] {});
+        assertTrue(oc.isPresent());
+
+        Optional<MethodConfig> omc1 = filterMethod(rc, "activate", null);
+        assertTrue(omc1.isPresent());
+    }
+
+    @Test
+    void testReflectBundleActivator(@TempDir Path tempDir) throws Exception
+    {
+        List<Path> paths = getDependencys(DEP_ORG_OSGI_CORE,
+            DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_IMPL_ACTIVATOR);
+
+        List<ReflectConfig> list = ReflectConfigUtil.reflectConfig(paths);
+
+        //System.out.println(ReflectConfigUtil.json(list));
+        ReflectConfig rc = filterReflectConfigByClassName(list,
+            "org.apache.felix.atomos.tests.testbundles.service.impl.activator.Activator");
+        assertThat(rc.fields).isEmpty();
+
+        Optional<ConstructorConfig> oc = filterConstructor(rc, new String[] {});
+        assertTrue(oc.isPresent());
+
+        Optional<MethodConfig> omc1 = filterMethod(rc, "start",
+            new String[] { "org.osgi.framework.BundleContext" });
+        assertTrue(omc1.isPresent());
+        Optional<MethodConfig> omc2 = filterMethod(rc, "stop",
+            new String[] { "org.osgi.framework.BundleContext" });
+        assertTrue(omc2.isPresent());
+    }
+
+    @Test
+    void testBundleActivatorMagic(@TempDir Path tempDir) throws Exception
+    {
+        List<Path> paths = getDependencys(DEP_ORG_OSGI_CORE,
+            DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_IMPL_ACTIVATOR,
+            DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_CONTRACT);
+
+        List<ReflectConfig> rcs = ReflectConfigUtil.reflectConfig(paths);
+
+        //System.out.println(ReflectConfigUtil.json(rcs));
+
+        ReflectConfig rc = filterReflectConfigByClassName(rcs,
+            "org.apache.felix.atomos.tests.testbundles.service.impl.activator.ActivatorEcho");
+        assertThat(rc.constructor).isEmpty();
+        assertThat(rc.fields).isEmpty();
+
+        Optional<MethodConfig> omc1 = filterMethod(rc, "echo", null);
+        assertTrue(omc1.isPresent());
+    }
+
+    @Test
+    void testDTO(@TempDir Path tempDir) throws Exception
+    {
+
+        List<Path> paths = getDependencys(DEP_ATOMOS_TESTS_TESTBUNDLES_REFLECT_DTO,
+            DEP_ORG_OSGI_DTO);
+
+        List<ReflectConfig> rcs = ReflectConfigUtil.reflectConfig(paths);
+        //System.out.println(ReflectConfigUtil.json(rcs));
+
+        ReflectConfig rc = filterReflectConfigByClassName(rcs,
+            "org.apache.felix.atomos.tests.testbundles.reflect.command.OneDTO");
+        assertThat(rc.constructor).isEmpty();
+        assertThat(rc.methods).isEmpty();
+
+        assertThat(rc.fields).containsExactly("one");
+    }
+
+    @Test
+    void testGOGOCommand(@TempDir Path tempDir) throws Exception
+    {
+        List<Path> paths = getDependencys(DEP_ATOMOS_TESTS_TESTBUNDLES_REFLECT_COMMAND);
+        List<ReflectConfig> rcs = ReflectConfigUtil.reflectConfig(paths);
+        //System.out.println(ReflectConfigUtil.json(rcs));
+
+        ReflectConfig rc1 = filterReflectConfigByClassName(rcs,
+            "org.apache.felix.atomos.tests.testbundles.reflect.command.AbstractCmd");
+        assertThat(rc1.constructor).isEmpty();
+        assertThat(rc1.fields).isEmpty();
+
+        Optional<MethodConfig> rc1mc1 = filterMethod(rc1, "multiple", null);
+        assertNotNull(rc1mc1);
+
+        ReflectConfig rc2 = filterReflectConfigByClassName(rcs,
+            "org.apache.felix.atomos.tests.testbundles.reflect.command.CmdExample");
+
+        assertThat(rc2.fields).isEmpty();
+
+        Optional<ConstructorConfig> rc2cc1 = filterConstructor(rc2, new String[] {});
+        assertTrue(rc2cc1.isPresent());
+
+        Optional<MethodConfig> rc2mc1 = filterMethod(rc2, "a", null);
+        assertTrue(rc2mc1.isPresent());
+
+        Optional<MethodConfig> rc2mc2 = filterMethod(rc2, "multiple", null);
+        assertTrue(rc2mc2.isPresent());
+
+        Optional<MethodConfig> rc2mc3 = filterMethod(rc2, "single", null);
+        assertTrue(rc2mc3.isPresent());
+
+    }
+
+    @Test
+    void testReference(@TempDir Path tempDir) throws Exception
+    {
+        List<Path> paths = getDependencys(DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_CONTRACT,
+            DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_USER);
+        List<ReflectConfig> rcs = ReflectConfigUtil.reflectConfig(paths);
+        //System.out.println(ReflectConfigUtil.json(rcs));
+
+        ReflectConfig rc1 = filterReflectConfigByClassName(rcs,
+            "org.apache.felix.atomos.tests.testbundles.service.contract.Echo");
+        assertThat(rc1.constructor).isEmpty();
+        assertThat(rc1.fields).isEmpty();
+        assertThat(rc1.methods).isEmpty();
+
+        ReflectConfig rc2 = filterReflectConfigByClassName(rcs,
+            "org.apache.felix.atomos.tests.testbundles.service.user.EchoUser");
+
+        assertThat(rc2.fields).isEmpty();
+
+        Optional<ConstructorConfig> rc2cc1 = filterConstructor(rc2, new String[] {});
+        assertTrue(rc2cc1.isPresent());
+
+        Optional<MethodConfig> rc2mc1 = filterMethod(rc2, "activate", null);
+        assertTrue(rc2mc1.isPresent());
+
+        Optional<MethodConfig> rc2mc2 = filterMethod(rc2, "setEcho", null);
+        assertTrue(rc2mc2.isPresent());
+
+        Optional<MethodConfig> rc2mc3 = filterMethod(rc2, "unsetEcho", null);
+        assertTrue(rc2mc3.isPresent());
+
+        ReflectConfig rc3 = filterReflectConfigByClassName(rcs,
+            "org.apache.felix.atomos.tests.testbundles.service.user.EchoUser2");
+        assertThat(rc3.fields).isEmpty();
+        assertThat(rc3.methods).isEmpty();
+        Optional<ConstructorConfig> rc3cc1 = filterConstructor(rc3,
+            new String[] { "java.util.Map",
+        "org.apache.felix.atomos.tests.testbundles.service.contract.Echo" });
+        assertTrue(rc3cc1.isPresent());
+    }
+}
\ No newline at end of file
diff --git a/atomos.maven/src/test/java/org/apache/felix/atomos/maven/SubstrateTest.java b/atomos.maven/src/test/java/org/apache/felix/atomos/maven/SubstrateTest.java
new file mode 100644
index 0000000..dcb80fa
--- /dev/null
+++ b/atomos.maven/src/test/java/org/apache/felix/atomos/maven/SubstrateTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven;
+
+import static org.apache.felix.atomos.maven.TestConstants.DEP_ATOMOS_TESTS_TESTBUNDLES_RESOURCE_A;
+import static org.apache.felix.atomos.maven.TestConstants.getDependency;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+public class SubstrateTest extends TestBase
+{
+
+    @Test
+    void testSubstrate(@TempDir Path tempDir) throws Exception
+    {
+        Path path = getDependency(DEP_ATOMOS_TESTS_TESTBUNDLES_RESOURCE_A);
+        Path atomosSubstrateJar = SubstrateUtil.substrate(Arrays.asList(path), tempDir);
+        assertThat(atomosSubstrateJar).exists().isRegularFile();
+
+        try (JarFile jarFile = new JarFile(atomosSubstrateJar.toFile());)
+        {
+            assertThat(jarFile.stream().map(JarEntry::getName).collect(
+                Collectors.toList())).containsOnly("META-INF/MANIFEST.MF", //
+                    "atomos/0/META-INF/MANIFEST.MF", //
+                    "atomos/0/META-TEXT/file.txt", //
+                    "atomos/0/org/apache/felix/atomos/tests/testbundles/resource/a/file.txt", //
+                    "atomos/0/file.txt", //
+                    "atomos/bundles.index", //
+                    "META-INF/native-image/resource-config.json");
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/atomos.maven/src/test/java/org/apache/felix/atomos/maven/TestBase.java b/atomos.maven/src/test/java/org/apache/felix/atomos/maven/TestBase.java
new file mode 100644
index 0000000..dff4ddf
--- /dev/null
+++ b/atomos.maven/src/test/java/org/apache/felix/atomos/maven/TestBase.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.maven;
+
+import static org.apache.felix.atomos.maven.TestConstants.getAllDependencys;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+
+import org.junit.jupiter.api.BeforeAll;
+
+public class TestBase
+{
+
+    @BeforeAll
+    public static void setup() throws IOException
+    {
+
+        assertThat(getAllDependencys()).size().describedAs(
+            "No Dependencys - Please run maven-phase 'generate-resources' first. './mvnw generate-resources -f atomos.maven/pom.xml'").isGreaterThan(
+                0);
+    }
+
+}
\ No newline at end of file
diff --git a/atomos.maven/src/test/java/org/apache/felix/atomos/maven/TestConstants.java b/atomos.maven/src/test/java/org/apache/felix/atomos/maven/TestConstants.java
new file mode 100644
index 0000000..eaf43f7
--- /dev/null
+++ b/atomos.maven/src/test/java/org/apache/felix/atomos/maven/TestConstants.java
@@ -0,0 +1,118 @@
+/**
+ *
+ */
+package org.apache.felix.atomos.maven;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.apache.felix.atomos.maven.reflect.ConstructorConfig;
+import org.apache.felix.atomos.maven.reflect.MethodConfig;
+import org.apache.felix.atomos.maven.reflect.ReflectConfig;
+
+public class TestConstants
+{
+
+    static String ACTIVATOR_CONSTRUCTOR = "\"methods\":[{\"name\":\"<init>\",\"parameterTypes\":[] }]";
+    static String COMPONENT_CONSTRUCTOR = "\"allPublicConstructors\" : true";
+    static String DEP_ATOMOS_TESTS_TESTBUNDLES_REFLECT_COMMAND = "org.apache.felix.atomos.tests.testbundles.reflect.command-";
+    static String DEP_ATOMOS_TESTS_TESTBUNDLES_REFLECT_DTO = "org.apache.felix.atomos.tests.testbundles.reflect.dto-";
+    static String DEP_ATOMOS_TESTS_TESTBUNDLES_RESOURCE_A = "org.apache.felix.atomos.tests.testbundles.resource.a-";
+    static String DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_CONTRACT = "org.apache.felix.atomos.tests.testbundles.service.contract-";
+    static String DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_IMPL = "org.apache.felix.atomos.tests.testbundles.service.impl-";
+    static String DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_IMPL_ACTIVATOR = "org.apache.felix.atomos.tests.testbundles.service.impl.activator-";
+    static String DEP_ATOMOS_TESTS_TESTBUNDLES_SERVICE_USER = "org.apache.felix.atomos.tests.testbundles.service.user-";
+    static String DEP_FELIX_GOGO_COMMAND = "org.apache.felix.gogo.command-";
+    static String DEP_FELIX_GOGO_RUNTIME = "org.apache.felix.gogo.runtime-";
+    static String DEP_FELIX_HTTP_API = "org.apache.felix.http.api-";
+    static String DEP_FELIX_HTTP_SERVLET_API = "org.apache.felix.http.servlet-api-";
+    static String DEP_FELIX_SCR = "org.apache.felix.scr-";
+    static String DEP_FELIX_WEBCONSOLE = "org.apache.felix.webconsole-";
+    static String DEP_ORG_OSGI_CORE = "org.osgi.core-";
+    static String DEP_ORG_OSGI_DTO = "org.osgi.dto-";
+    static String DEP_ORG_OSGI_SERVICE_HTTP = "org.osgi.service.http-";
+    static String DEP_ORG_OSGI_SERVICE_LOG = "org.osgi.service.log-";
+
+    static Optional<ConstructorConfig> filterConstructor(ReflectConfig reflectConfig,
+        String[] parameterTypes)
+    {
+        assertNotNull(reflectConfig);
+        assertNotNull(reflectConfig.constructor);
+
+        return reflectConfig.constructor.stream().filter(
+            c -> Arrays.equals(parameterTypes, c.methodParameterTypes)).findAny();
+
+    }
+
+    static Optional<MethodConfig> filterMethod(ReflectConfig reflectConfig, String name,
+        String[] parameterTypes)
+    {
+        assertNotNull(reflectConfig);
+        assertNotNull(reflectConfig.methods);
+
+        return reflectConfig.methods.stream().filter(c -> c.name.equals(name)
+            && Arrays.equals(parameterTypes, c.methodParameterTypes)).findAny();
+
+    }
+
+    static ReflectConfig filterReflectConfigByClassName(List<ReflectConfig> list,
+        String checkClass)
+    {
+        assertThat(list).isNotNull();
+        Optional<ReflectConfig> optional = list.stream().filter(
+            c -> c.className.equals(checkClass)).findFirst();
+        assertTrue(optional.isPresent());
+        return optional.get();
+    }
+
+    static List<Path> getAllDependencys() throws IOException
+    {
+        return getAllDependencysFrom("target/test-dependencies/");
+    }
+
+    static List<Path> getAllDependencysFrom(String dir) throws IOException
+    {
+        Path dirp = Paths.get(dir);
+
+        if (!Files.exists(dirp))
+        {
+            return new ArrayList<>();
+        }
+        return Files.list(dirp).filter(p -> p.toString().endsWith(".jar")).collect(
+            Collectors.toList());
+    }
+
+    static Path getDependency(String depName) throws IOException
+    {
+        Path testDepsDir = Paths.get("target/test-dependencies/");
+        List<Path> paths = Files.list(testDepsDir).filter(
+            p -> p.getFileName().toString().startsWith(depName)).collect(
+                Collectors.toList());
+        assertEquals(1, paths.size(),
+            String.format("Must be exact one test Dependency with the name %s", depName));
+        return paths.get(0);
+    }
+
+    static List<Path> getDependencys(String... depNames) throws IOException
+    {
+        List<Path> paths = new ArrayList<>();
+        for (String depName : depNames)
+        {
+            paths.add(getDependency(depName));
+        }
+        return paths;
+    }
+
+}
\ No newline at end of file
diff --git a/atomos.tests/atomos.tests.modulepath.service/src/test/java/org/apache/felix/atomos/tests/modulepath/service/ModulepathLaunchTest.java b/atomos.tests/atomos.tests.modulepath.service/src/test/java/org/apache/felix/atomos/tests/modulepath/service/ModulepathLaunchTest.java
index 9186079..bb229e0 100644
--- a/atomos.tests/atomos.tests.modulepath.service/src/test/java/org/apache/felix/atomos/tests/modulepath/service/ModulepathLaunchTest.java
+++ b/atomos.tests/atomos.tests.modulepath.service/src/test/java/org/apache/felix/atomos/tests/modulepath/service/ModulepathLaunchTest.java
@@ -26,7 +26,16 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.net.URL;
 import java.nio.file.Path;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 import org.apache.felix.atomos.launch.AtomosLauncher;
@@ -498,7 +507,7 @@ public class ModulepathLaunchTest
         {
             f = AtomosLauncher.newFramework(
                 Map.of(Constants.FRAMEWORK_STORAGE, storage2.getAbsolutePath(),
-                        "felix.cache.locking", "false"),
+                    "felix.cache.locking", "false"),
                 atomosRuntime);
             f.start();
             fail();
@@ -519,7 +528,7 @@ public class ModulepathLaunchTest
 
     @Test
     void testLoaderType(@TempDir Path storage) throws BundleException,
-        ClassNotFoundException
+    ClassNotFoundException
     {
         ModulepathLaunch.main(new String[] {
                 Constants.FRAMEWORK_STORAGE + '=' + storage.toFile().getAbsolutePath() });
@@ -685,8 +694,8 @@ public class ModulepathLaunchTest
         children.iterator().next().getAtomosContents().stream().map(
             AtomosContent::getBundle).filter(
                 Objects::nonNull).filter(
-                (b) -> b.getSymbolicName().equals(
-                    TESTBUNDLES_SERVICE_IMPL_A)).findFirst().orElseThrow().uninstall();
+                    (b) -> b.getSymbolicName().equals(
+                        TESTBUNDLES_SERVICE_IMPL_A)).findFirst().orElseThrow().uninstall();
         checkServices(bc, 7);
 
         testFramework.stop();
@@ -725,9 +734,11 @@ public class ModulepathLaunchTest
 
         final ServiceReference<?>[] refs = b.getRegisteredServices();
         assertNotNull(refs, "No services.");
-        assertEquals(1, refs.length, "Wrong number of services.");
+        assertEquals(2, refs.length, "Wrong number of services.");
         assertEquals(Boolean.TRUE, refs[0].getProperty("echo.reference"),
             "Wrong service.");
+        assertEquals(Boolean.TRUE, refs[1].getProperty("echo.reference"),
+            "Wrong service.");
     }
 
     @Test
@@ -796,7 +807,7 @@ public class ModulepathLaunchTest
             final URL resc = clazz.getResource("/file.txt");
             assertNotNull(resc, "Expected URL, got null ");
             assertEquals("/file.txt", new BufferedReader(
-                    new InputStreamReader(resc.openStream())).readLine(),
+                new InputStreamReader(resc.openStream())).readLine(),
                 "Incorrect contents from URL");
         }
         catch (final ClassNotFoundException e)
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.bom/pom.xml b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.bom/pom.xml
new file mode 100644
index 0000000..27dfd39
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.bom/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.felix.atomos.tests</groupId>
+        <artifactId>org.apache.felix.atomos.tests.testbundles</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>org.apache.felix.atomos.tests.testbundles.bom</artifactId>
+    <packaging>pom</packaging>
+    <name>atomos.tests.testbundles.bom</name>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.felix.atomos.tests</groupId>
+            <artifactId>org.apache.felix.atomos.tests.testbundles.resource.a</artifactId>
+            <version>${atomos.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.atomos.tests</groupId>
+            <artifactId>org.apache.felix.atomos.tests.testbundles.service.contract</artifactId>
+            <version>${atomos.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.atomos.tests</groupId>
+            <artifactId>org.apache.felix.atomos.tests.testbundles.service.impl</artifactId>
+            <version>${atomos.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.atomos.tests</groupId>
+            <artifactId>org.apache.felix.atomos.tests.testbundles.service.impl.a</artifactId>
+            <version>${atomos.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.atomos.tests</groupId>
+            <artifactId>org.apache.felix.atomos.tests.testbundles.service.impl.activator</artifactId>
+            <version>${atomos.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.atomos.tests</groupId>
+            <artifactId>org.apache.felix.atomos.tests.testbundles.service.impl.b</artifactId>
+            <version>${atomos.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.atomos.tests</groupId>
+            <artifactId>org.apache.felix.atomos.tests.testbundles.service.library</artifactId>
+            <version>${atomos.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.atomos.tests</groupId>
+            <artifactId>org.apache.felix.atomos.tests.testbundles.service.user</artifactId>
+            <version>${atomos.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.atomos.tests</groupId>
+            <artifactId>org.apache.felix.atomos.tests.testbundles.substrate.main</artifactId>
+            <version>${atomos.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.atomos.tests</groupId>
+            <artifactId>org.apache.felix.atomos.tests.testbundles.reflect.command</artifactId>
+            <version>${atomos.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.atomos.tests</groupId>
+            <artifactId>org.apache.felix.atomos.tests.testbundles.reflect.dto</artifactId>
+            <version>${atomos.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/pom.xml b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/pom.xml
new file mode 100644
index 0000000..ff6c4b4
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/pom.xml
@@ -0,0 +1,81 @@
+<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.felix.atomos.tests</groupId>
+        <artifactId>org.apache.felix.atomos.tests.testbundles</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>org.apache.felix.atomos.tests.testbundles.reflect.command</artifactId>
+    <name>atomos.tests.testbundles.reflect.command</name>
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.annotation</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component.annotations</artifactId>
+        <version>1.4.0</version>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>java8</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-resources-plugin</artifactId>
+                        <version>3.1.0</version>
+                        <configuration>
+
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>default-compile</id>
+                                <configuration>
+                                    <!-- compile everything with the default to ensure module-info is correct -->
+                                    <release>${java.version}</release>
+                                </configuration>
+                            </execution>
+                            <!-- compile the packages that must work with java 8 -->
+                            <execution>
+                                <id>java8-compile</id>
+                                <goals>
+                                    <goal>compile</goal>
+                                </goals>
+                                <configuration>
+                                    <excludes>
+                                        <exclude>module-info.java</exclude>
+                                    </excludes>
+                                    <release>8</release>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/module-info.java b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/module-info.java
new file mode 100644
index 0000000..0768315
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/module-info.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+open module org.apache.felix.atomos.tests.testbundles.reflect.command
+{
+    exports org.apache.felix.atomos.tests.testbundles.reflect.command;
+    requires org.osgi.service.component.annotations;
+}
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/A.java b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/A.java
new file mode 100644
index 0000000..30bd0aa
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/A.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.tests.testbundles.reflect.command;
+
+
+public class A
+{
+    public String a;
+}
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/AbstractCmd.java b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/AbstractCmd.java
new file mode 100644
index 0000000..1754f83
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/AbstractCmd.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.tests.testbundles.reflect.command;
+
+public class AbstractCmd
+{
+    public String multiple()
+    {
+        return "abstractmultiple";
+    }
+
+}
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/CmdExample.java b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/CmdExample.java
new file mode 100644
index 0000000..25e0601
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/CmdExample.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package org.apache.felix.atomos.tests.testbundles.reflect.command;
+
+import org.osgi.service.component.annotations.Component;
+
+@Component(immediate = true, service = CmdExample.class)
+@CommandScope("reflect-test")
+@CommandFunction(value = { "a", "single", "multiple" })
+public class CmdExample extends AbstractCmd
+{
+
+    public String single()
+    {
+        return "single";
+    }
+
+    public String multiple(String s)
+    {
+        return "multiple" + s;
+    }
+
+    public String multiple(String s, boolean b)
+    {
+        return "multiple" + s + b;
+    }
+
+    public A a()
+    {
+        A a = new A();
+        a.a = "a";
+        return a;
+    }
+}
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/CommandFunction.java b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/CommandFunction.java
new file mode 100644
index 0000000..87bd505
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/CommandFunction.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package org.apache.felix.atomos.tests.testbundles.reflect.command;
+
+import org.osgi.service.component.annotations.ComponentPropertyType;
+
+
+/**
+ * The Interface CommandFunction.
+ */
+@ComponentPropertyType
+public @interface CommandFunction {
+
+    /** The Constant PREFIX_. */
+    public static final String PREFIX_ = "osgi.";
+
+    /**
+     * Value.
+     *
+     * @return the string[]
+     */
+    public String[] value();
+}
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/CommandScope.java b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/CommandScope.java
new file mode 100644
index 0000000..3d3e916
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.command/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/CommandScope.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.tests.testbundles.reflect.command;
+
+import org.osgi.service.component.annotations.ComponentPropertyType;
+
+
+/**
+ * The Interface CommandScope.
+ */
+@ComponentPropertyType
+public @interface CommandScope {
+
+    /** The Constant PREFIX_. */
+    public static final String PREFIX_ = "osgi.";
+
+    /**
+     * Value.
+     *
+     * @return the string
+     */
+    public String value();
+}
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.dto/pom.xml b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.dto/pom.xml
new file mode 100644
index 0000000..6c0ed63
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.dto/pom.xml
@@ -0,0 +1,76 @@
+<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.felix.atomos.tests</groupId>
+        <artifactId>org.apache.felix.atomos.tests.testbundles</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>org.apache.felix.atomos.tests.testbundles.reflect.dto</artifactId>
+    <name>atomos.tests.testbundles.reflect.dto</name>
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.dto</artifactId>
+            <version>1.1.0</version>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>java8</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-resources-plugin</artifactId>
+                        <version>3.1.0</version>
+                        <configuration>
+
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>default-compile</id>
+                                <configuration>
+                                    <!-- compile everything with the default to ensure module-info is correct -->
+                                    <release>${java.version}</release>
+                                </configuration>
+                            </execution>
+                            <!-- compile the packages that must work with java 8 -->
+                            <execution>
+                                <id>java8-compile</id>
+                                <goals>
+                                    <goal>compile</goal>
+                                </goals>
+                                <configuration>
+                                    <excludes>
+                                        <exclude>module-info.java</exclude>
+                                    </excludes>
+                                    <release>8</release>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.dto/src/main/java/module-info.java b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.dto/src/main/java/module-info.java
new file mode 100644
index 0000000..b3a461f
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.dto/src/main/java/module-info.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+open module org.apache.felix.atomos.tests.testbundles.reflect.command
+{
+    exports org.apache.felix.atomos.tests.testbundles.reflect.command;
+    requires org.osgi.dto;
+}
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.dto/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/OneDTO.java b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.dto/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/OneDTO.java
new file mode 100644
index 0000000..d3037eb
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.reflect.dto/src/main/java/org/apache/felix/atomos/tests/testbundles/reflect/command/OneDTO.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.tests.testbundles.reflect.command;
+
+import org.osgi.dto.DTO;
+
+public class OneDTO extends DTO
+{
+    public String one;
+
+}
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.impl.activator/src/main/java/org/apache/felix/atomos/tests/testbundles/service/impl/activator/Activator.java b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.impl.activator/src/main/java/org/apache/felix/atomos/tests/testbundles/service/impl/activator/Activator.java
index 43f5536..58ecc4a 100644
--- a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.impl.activator/src/main/java/org/apache/felix/atomos/tests/testbundles/service/impl/activator/Activator.java
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.impl.activator/src/main/java/org/apache/felix/atomos/tests/testbundles/service/impl/activator/Activator.java
@@ -13,7 +13,6 @@
  */
 package org.apache.felix.atomos.tests.testbundles.service.impl.activator;
 
-import java.util.Collections;
 import java.util.Hashtable;
 
 import org.apache.felix.atomos.tests.testbundles.service.contract.Echo;
@@ -29,9 +28,14 @@ public class Activator implements BundleActivator
     @Override
     public void start(BundleContext context) throws Exception
     {
-        Echo impl = (m) -> "impl.activator " + m;
-        context.registerService(Echo.class, impl, new Hashtable<String, String>(
-            Collections.singletonMap("type", "impl.activator")));
+        Echo impl = new ActivatorEcho();
+
+        Hashtable<String, Object> ht = new Hashtable<>();
+        ht.put("type", "impl.activator");
+        ht.put("osgi.command.scope", "echo");
+        ht.put("osgi.command.function", new String[] { "echo" });
+
+        context.registerService(Echo.class, impl, ht);
         System.out.println("Registered Echo service from activator.");
     }
 
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.impl.activator/src/main/java/org/apache/felix/atomos/tests/testbundles/service/impl/activator/ActivatorEcho.java b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.impl.activator/src/main/java/org/apache/felix/atomos/tests/testbundles/service/impl/activator/ActivatorEcho.java
new file mode 100644
index 0000000..047b453
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.impl.activator/src/main/java/org/apache/felix/atomos/tests/testbundles/service/impl/activator/ActivatorEcho.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.tests.testbundles.service.impl.activator;
+
+import org.apache.felix.atomos.tests.testbundles.service.contract.Echo;
+
+public class ActivatorEcho implements Echo
+{
+
+    @Override
+    public String echo(String msg)
+    {
+        return "impl.activator " + msg;
+    }
+
+}
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.user/src/main/java/org/apache/felix/atomos/tests/testbundles/service/user/EchoUser.java b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.user/src/main/java/org/apache/felix/atomos/tests/testbundles/service/user/EchoUser.java
index 723d3df..357ed25 100644
--- a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.user/src/main/java/org/apache/felix/atomos/tests/testbundles/service/user/EchoUser.java
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.user/src/main/java/org/apache/felix/atomos/tests/testbundles/service/user/EchoUser.java
@@ -19,7 +19,7 @@ import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
 
 @Component(service = EchoUser.class, property = {
-        "echo.reference:Boolean=true" }, immediate = true)
+"echo.reference:Boolean=true" }, immediate = true)
 @org.osgi.annotation.bundle.Requirement(namespace = "osgi.ee", filter = "(&(osgi.ee=JavaSE)(version=1.8))")
 public class EchoUser
 {
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.user/src/main/java/org/apache/felix/atomos/tests/testbundles/service/user/EchoUser.java b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.user/src/main/java/org/apache/felix/atomos/tests/testbundles/service/user/EchoUser2.java
similarity index 67%
copy from atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.user/src/main/java/org/apache/felix/atomos/tests/testbundles/service/user/EchoUser.java
copy to atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.user/src/main/java/org/apache/felix/atomos/tests/testbundles/service/user/EchoUser2.java
index 723d3df..0cc2863 100644
--- a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.user/src/main/java/org/apache/felix/atomos/tests/testbundles/service/user/EchoUser.java
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.service.user/src/main/java/org/apache/felix/atomos/tests/testbundles/service/user/EchoUser2.java
@@ -13,30 +13,23 @@
  */
 package org.apache.felix.atomos.tests.testbundles.service.user;
 
+import java.util.Map;
+
 import org.apache.felix.atomos.tests.testbundles.service.contract.Echo;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
 
-@Component(service = EchoUser.class, property = {
-        "echo.reference:Boolean=true" }, immediate = true)
+@Component(service = EchoUser2.class, property = {
+"echo.reference:Boolean=true" }, immediate = true)
 @org.osgi.annotation.bundle.Requirement(namespace = "osgi.ee", filter = "(&(osgi.ee=JavaSE)(version=1.8))")
-public class EchoUser
+public class EchoUser2
 {
     @Activate
-    public void activate()
+    public EchoUser2(Map<String, Object> componentProps, @Reference Echo echo)
     {
-        System.out.println("Activated: " + getClass().getName());
+        System.out.println("Activated via constructor: " + getClass().getName());
     }
 
-    @Reference
-    protected void setEcho(Echo echo)
-    {
-        System.out.println("Echo service found: " + echo.echo("hello"));
-    }
 
-    protected void unsetEcho(Echo echo)
-    {
-        System.out.println("Echo service unset: " + echo.echo("goodbye"));
-    }
 }
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.substrate.main/pom.xml b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.substrate.main/pom.xml
new file mode 100644
index 0000000..cd6a57b
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.substrate.main/pom.xml
@@ -0,0 +1,82 @@
+<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.felix.atomos.tests</groupId>
+        <artifactId>org.apache.felix.atomos.tests.testbundles</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>org.apache.felix.atomos.tests.testbundles.substrate.main</artifactId>
+    <name>atomos.tests.testbundles.substrate.main</name>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.atomos.runtime</artifactId>
+            <version>${atomos.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.log</artifactId>
+            <version>1.4.0</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>java8</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-resources-plugin</artifactId>
+                        <version>3.1.0</version>
+                        <configuration>
+
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>default-compile</id>
+                                <configuration>
+                                    <!-- compile everything with the default to ensure module-info is correct -->
+                                    <release>${java.version}</release>
+                                </configuration>
+                            </execution>
+                            <!-- compile the packages that must work with java 8 -->
+                            <execution>
+                                <id>java8-compile</id>
+                                <goals>
+                                    <goal>compile</goal>
+                                </goals>
+                                <configuration>
+                                    <excludes>
+                                        <exclude>module-info.java</exclude>
+                                    </excludes>
+                                    <release>8</release>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.substrate.main/src/main/java/org/apache/felix/atomos/tests/testbundles/substrate/main/Main.java b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.substrate.main/src/main/java/org/apache/felix/atomos/tests/testbundles/substrate/main/Main.java
new file mode 100644
index 0000000..dea2e67
--- /dev/null
+++ b/atomos.tests/atomos.tests.testbundles/atomos.tests.testbundles.substrate.main/src/main/java/org/apache/felix/atomos/tests/testbundles/substrate/main/Main.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+package org.apache.felix.atomos.tests.testbundles.substrate.main;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.felix.atomos.launch.AtomosLauncher;
+import org.apache.felix.atomos.runtime.AtomosRuntime;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.launch.Framework;
+import org.osgi.service.log.LogEntry;
+import org.osgi.service.log.LogLevel;
+import org.osgi.service.log.LogReaderService;
+import org.osgi.service.log.admin.LoggerContext;
+
+public class Main
+{
+    public static void main(String[] args) throws BundleException, ClassNotFoundException
+    {
+        final long start = System.nanoTime();
+
+        final AtomosRuntime atomosRuntime = AtomosRuntime.newAtomosRuntime();
+        final Map<String, String> config = AtomosLauncher.getConfiguration(args);
+        config.putIfAbsent(LoggerContext.LOGGER_CONTEXT_DEFAULT_LOGLEVEL,
+            LogLevel.AUDIT.name());
+        final Framework framework = AtomosLauncher.newFramework(config, atomosRuntime);
+        framework.init();
+        final BundleContext bc = framework.getBundleContext();
+        final LogReaderService logReader = bc.getService(
+            bc.getServiceReference(LogReaderService.class));
+        logReader.addLogListener((e) -> {
+            System.out.println(getLogMessage(e));
+        });
+        framework.start();
+
+        final long total = System.nanoTime() - start;
+        System.out.println("Total time: " + TimeUnit.NANOSECONDS.toMillis(total));
+
+        if (Arrays.asList(args).contains("-exit"))
+        {
+            System.exit(0);
+        }
+    }
+
+    private static String getLogMessage(LogEntry e)
+    {
+        final StringBuilder builder = new StringBuilder(e.getMessage());
+        if (e.getBundle() != null)
+        {
+            builder.append(" - bundle: " + e.getBundle());
+        }
+        if (e.getServiceReference() != null)
+        {
+            builder.append(" - service: " + e.getServiceReference());
+        }
+        return builder.toString();
+    }
+}
diff --git a/atomos.tests/atomos.tests.testbundles/pom.xml b/atomos.tests/atomos.tests.testbundles/pom.xml
index 3544d21..73ef4c2 100644
--- a/atomos.tests/atomos.tests.testbundles/pom.xml
+++ b/atomos.tests/atomos.tests.testbundles/pom.xml
@@ -18,5 +18,9 @@
         <module>atomos.tests.testbundles.service.user</module>
         <module>atomos.tests.testbundles.service.library</module>
         <module>atomos.tests.testbundles.resources.a</module>
+        <module>atomos.tests.testbundles.substrate.main</module>
+        <module>atomos.tests.testbundles.bom</module>
+        <module>atomos.tests.testbundles.reflect.command</module>
+        <module>atomos.tests.testbundles.reflect.dto</module>
     </modules>
 </project>
diff --git a/pom.xml b/pom.xml
index 4eed20c..b9d3b64 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,6 +22,7 @@
         <module>atomos.tests</module>
         <module>atomos.substrate.config</module>
         <module>atomos.examples</module>
+        <module>atomos.maven</module>
     </modules>
 
     <build>