You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by rm...@apache.org on 2016/09/26 17:33:46 UTC

tomee git commit: TOMEE-1945 extract tomee embedded single runner logic to let it be reusable

Repository: tomee
Updated Branches:
  refs/heads/master 920f6722a -> a7dacaf2b


TOMEE-1945 extract tomee embedded single runner logic to let it be reusable


Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/a7dacaf2
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/a7dacaf2
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/a7dacaf2

Branch: refs/heads/master
Commit: a7dacaf2bb6eaf92a95fd39e018a7b01ab95a494
Parents: 920f672
Author: rmannibucau <rm...@apache.org>
Authored: Mon Sep 26 19:33:38 2016 +0200
Committer: rmannibucau <rm...@apache.org>
Committed: Mon Sep 26 19:33:38 2016 +0200

----------------------------------------------------------------------
 .../openejb/config/WebappAggregatedArchive.java |  29 +-
 .../openejb/testing/ApplicationComposers.java   |   4 +-
 tomee/tomee-embedded/pom.xml                    |  20 +-
 .../apache/tomee/embedded/Configuration.java    | 290 +++++++-------
 .../org/apache/tomee/embedded/Container.java    |  72 +++-
 .../TomEEEmbeddedApplicationRunner.java         | 386 +++++++++++++++++++
 .../junit/TomEEEmbeddedSingleRunner.java        | 316 ++-------------
 .../embedded/NoScannerSingleRunnerTest.java     |  67 ++++
 .../embedded/SingleInstanceRunnerTest.java      |   9 +-
 9 files changed, 723 insertions(+), 470 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tomee/blob/a7dacaf2/container/openejb-core/src/main/java/org/apache/openejb/config/WebappAggregatedArchive.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/WebappAggregatedArchive.java b/container/openejb-core/src/main/java/org/apache/openejb/config/WebappAggregatedArchive.java
index 54d8dc2..58f7743 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/config/WebappAggregatedArchive.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/config/WebappAggregatedArchive.java
@@ -25,6 +25,7 @@ import org.apache.xbean.finder.filter.Filter;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -37,14 +38,12 @@ import static java.util.Collections.singletonList;
 import static org.apache.openejb.loader.JarLocation.jarLocation;
 
 public class WebappAggregatedArchive implements Archive, ScanConstants {
-    private final Map<URL, List<String>> map = new HashMap<URL, List<String>>();
+    private final Map<URL, List<String>> map = new HashMap<>();
     private ScanUtil.ScanHandler handler;
     private boolean scanXmlExists; // faster than using an empty handler
     private Archive archive;
 
     public WebappAggregatedArchive(final Module module, final Iterable<URL> urls, final Filter filter) {
-        final List<Archive> archives = new ArrayList<Archive>();
-
         final URL scanXml = (URL) module.getAltDDs().get(ScanConstants.SCAN_XML_NAME);
         if (scanXml != null) {
             try {
@@ -77,16 +76,34 @@ public class WebappAggregatedArchive implements Archive, ScanConstants {
             }
         }
 
+        archive = new CompositeArchive(doScan(module.getClassLoader(), urls, filter));
+    }
+
+    private List<Archive> doScan(final ClassLoader loader, final Iterable<URL> urls, final Filter filter) {
+        final List<Archive> archives = new ArrayList<>();
         for (final URL url : urls) {
-            final List<String> classes = new ArrayList<String>();
+            final List<String> classes = new ArrayList<>();
             final Archive archive = new FilteredArchive(
-                    new ConfigurableClasspathArchive(module.getClassLoader(), singletonList(url)),
+                    new ConfigurableClasspathArchive(loader, singletonList(url)),
                     new ScanXmlSaverFilter(scanXmlExists, handler, classes, filter));
             map.put(url, classes);
             archives.add(archive);
         }
+        return archives;
+    }
 
-        archive = new CompositeArchive(archives);
+    // for internal usage mainly like faked modules
+    public WebappAggregatedArchive(final Archive delegate, final Iterable<URL> urls) {
+        final List<Archive> archives = doScan(Thread.currentThread().getContextClassLoader(), urls, null);
+        final List<String> classes = new ArrayList<String>();
+        final Archive archive = new FilteredArchive(delegate, new ScanXmlSaverFilter(scanXmlExists, handler, classes, null));
+        try {
+            this.map.put(new URL("jar:file://!/META-INF/beans.xml"), classes);
+        } catch (final MalformedURLException e) {
+            throw new IllegalArgumentException(e);
+        }
+        archives.add(archive);
+        this.archive = new CompositeArchive(archives);
     }
 
     public WebappAggregatedArchive(final Module module, final Iterable<URL> urls) {

http://git-wip-us.apache.org/repos/asf/tomee/blob/a7dacaf2/container/openejb-core/src/main/java/org/apache/openejb/testing/ApplicationComposers.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/testing/ApplicationComposers.java b/container/openejb-core/src/main/java/org/apache/openejb/testing/ApplicationComposers.java
index c698a8e..30bf950 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/testing/ApplicationComposers.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/testing/ApplicationComposers.java
@@ -981,7 +981,7 @@ public class ApplicationComposers {
         }
     }
 
-    private Collection<File> findFiles(final Jars jarsAnnotation) {
+    public static Collection<File> findFiles(final Jars jarsAnnotation) {
         if (jarsAnnotation == null) {
             return null;
         }
@@ -1231,7 +1231,7 @@ public class ApplicationComposers {
         return new HashMap<>();
     }
 
-    private IAnnotationFinder finderFromClasses(final DeploymentModule module, final Class<?>[] value, final Collection<File> others, final String[] excludes) {
+    private static IAnnotationFinder finderFromClasses(final DeploymentModule module, final Class<?>[] value, final Collection<File> others, final String[] excludes) {
         final Collection<Archive> archives = new ArrayList<>(1 + (others == null ? 0 : others.size()));
 
         final Filter filter = excludes == null || excludes.length == 0 ? null : Filters.invert(Filters.prefixes(excludes));

http://git-wip-us.apache.org/repos/asf/tomee/blob/a7dacaf2/tomee/tomee-embedded/pom.xml
----------------------------------------------------------------------
diff --git a/tomee/tomee-embedded/pom.xml b/tomee/tomee-embedded/pom.xml
index 19fd351..81e4cfe 100644
--- a/tomee/tomee-embedded/pom.xml
+++ b/tomee/tomee-embedded/pom.xml
@@ -212,11 +212,12 @@
             <configuration>
               <excludes>
                 <exclude>**/SingleInstanceRunnerTest*</exclude>
+                <exclude>**/NoScannerSingleRunnerTest*</exclude>
               </excludes>
             </configuration>
           </execution>
           <execution>
-            <id>single-server</id>
+            <id>single-server-default-scanning</id>
             <goals>
               <goal>test</goal>
             </goals>
@@ -224,6 +225,23 @@
               <includes>
                 <include>**/SingleInstanceRunnerTest*</include>
               </includes>
+              <systemPropertyVariables>
+                <tomee.application-composer.application>org.apache.tomee.embedded.SingleInstanceRunnerTest$TheApp</tomee.application-composer.application>
+              </systemPropertyVariables>
+            </configuration>
+          </execution>
+          <execution>
+            <id>single-server-custom-scanning</id>
+            <goals>
+              <goal>test</goal>
+            </goals>
+            <configuration>
+              <includes>
+                <include>**/NoScannerSingleRunnerTest*</include>
+              </includes>
+              <systemPropertyVariables>
+                <tomee.application-composer.application>org.apache.tomee.embedded.NoScannerSingleRunnerTest$ScanApp</tomee.application-composer.application>
+              </systemPropertyVariables>
             </configuration>
           </execution>
         </executions>

http://git-wip-us.apache.org/repos/asf/tomee/blob/a7dacaf2/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/Configuration.java
----------------------------------------------------------------------
diff --git a/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/Configuration.java b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/Configuration.java
index e5b62fe..c005121 100644
--- a/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/Configuration.java
+++ b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/Configuration.java
@@ -90,156 +90,160 @@ public class Configuration {
                 throw new IllegalArgumentException(resource + " not found");
             }
             final Properties config = IO.readProperties(is, new Properties());
-            final String http = config.getProperty("http");
-            if (http != null) {
-                setHttpPort(Integer.parseInt(http));
-            }
-            final String https = config.getProperty("https");
-            if (https != null) {
-                setHttpsPort(Integer.parseInt(https));
-            }
-            final String stop = config.getProperty("stop");
-            if (stop != null) {
-                setStopPort(Integer.parseInt(stop));
-            }
-            final String host = config.getProperty("host");
-            if (host != null) {
-                setHost(host);
-            }
-            final String dir = config.getProperty("dir");
-            if (dir != null) {
-                setDir(dir);
-            }
-            final String serverXml = config.getProperty("serverXml");
-            if (serverXml != null) {
-                setServerXml(serverXml);
-            }
-            final String keepServerXmlAsThis = config.getProperty("keepServerXmlAsThis");
-            if (keepServerXmlAsThis != null) {
-                setKeepServerXmlAsThis(Boolean.parseBoolean(keepServerXmlAsThis));
-            }
-            final String quickSession = config.getProperty("quickSession");
-            if (quickSession != null) {
-                setQuickSession(Boolean.parseBoolean(quickSession));
-            }
-            final String skipHttp = config.getProperty("skipHttp");
-            if (skipHttp != null) {
-                setSkipHttp(Boolean.parseBoolean(skipHttp));
-            }
-            final String ssl = config.getProperty("ssl");
-            if (ssl != null) {
-                setSsl(Boolean.parseBoolean(ssl));
-            }
-            final String http2 = config.getProperty("http2");
-            if (http2 != null) {
-                setHttp2(Boolean.parseBoolean(http2));
-            }
-            final String webResourceCached = config.getProperty("webResourceCached");
-            if (webResourceCached != null) {
-                setWebResourceCached(Boolean.parseBoolean(webResourceCached));
-            }
-            final String withEjbRemote = config.getProperty("withEjbRemote");
-            if (withEjbRemote != null) {
-                setWithEjbRemote(Boolean.parseBoolean(withEjbRemote));
-            }
-            final String deployOpenEjbApp = config.getProperty("deployOpenEjbApp");
-            if (deployOpenEjbApp != null) {
-                setDeployOpenEjbApp(Boolean.parseBoolean(deployOpenEjbApp));
-            }
-            final String keystoreFile = config.getProperty("keystoreFile");
-            if (keystoreFile != null) {
-                setKeystoreFile(keystoreFile);
-            }
-            final String keystorePass = config.getProperty("keystorePass");
-            if (keystorePass != null) {
-                setKeystorePass(keystorePass);
-            }
-            final String keystoreType = config.getProperty("keystoreType");
-            if (keystoreType != null) {
-                setKeystoreType(keystoreType);
-            }
-            final String clientAuth = config.getProperty("clientAuth");
-            if (clientAuth != null) {
-                setClientAuth(clientAuth);
-            }
-            final String keyAlias = config.getProperty("keyAlias");
-            if (keyAlias != null) {
-                setKeyAlias(keyAlias);
-            }
-            final String sslProtocol = config.getProperty("sslProtocol");
-            if (sslProtocol != null) {
-                setSslProtocol(sslProtocol);
-            }
-            final String webXml = config.getProperty("webXml");
-            if (webXml != null) {
-                setWebXml(webXml);
-            }
-            final String tempDir = config.getProperty("tempDir");
-            if (tempDir != null) {
-                setTempDir(tempDir);
-            }
-            final String classesFilterType = config.getProperty("classesFilter");
-            if (classesFilterType != null) {
-                try {
-                    setClassesFilter(Filter.class.cast(Thread.currentThread().getContextClassLoader().loadClass(classesFilterType).newInstance()));
-                } catch (final InstantiationException | IllegalAccessException | ClassNotFoundException e) {
-                    throw new IllegalArgumentException(e);
-                }
-            }
-            final String conf = config.getProperty("conf");
-            if (conf != null) {
-                setConf(conf);
+            loadFromProperties(config);
+            return this;
+        } catch (final IOException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    public void loadFromProperties(final Properties config) {
+        final String http = config.getProperty("http");
+        if (http != null) {
+            setHttpPort(Integer.parseInt(http));
+        }
+        final String https = config.getProperty("https");
+        if (https != null) {
+            setHttpsPort(Integer.parseInt(https));
+        }
+        final String stop = config.getProperty("stop");
+        if (stop != null) {
+            setStopPort(Integer.parseInt(stop));
+        }
+        final String host = config.getProperty("host");
+        if (host != null) {
+            setHost(host);
+        }
+        final String dir = config.getProperty("dir");
+        if (dir != null) {
+            setDir(dir);
+        }
+        final String serverXml = config.getProperty("serverXml");
+        if (serverXml != null) {
+            setServerXml(serverXml);
+        }
+        final String keepServerXmlAsThis = config.getProperty("keepServerXmlAsThis");
+        if (keepServerXmlAsThis != null) {
+            setKeepServerXmlAsThis(Boolean.parseBoolean(keepServerXmlAsThis));
+        }
+        final String quickSession = config.getProperty("quickSession");
+        if (quickSession != null) {
+            setQuickSession(Boolean.parseBoolean(quickSession));
+        }
+        final String skipHttp = config.getProperty("skipHttp");
+        if (skipHttp != null) {
+            setSkipHttp(Boolean.parseBoolean(skipHttp));
+        }
+        final String ssl = config.getProperty("ssl");
+        if (ssl != null) {
+            setSsl(Boolean.parseBoolean(ssl));
+        }
+        final String http2 = config.getProperty("http2");
+        if (http2 != null) {
+            setHttp2(Boolean.parseBoolean(http2));
+        }
+        final String webResourceCached = config.getProperty("webResourceCached");
+        if (webResourceCached != null) {
+            setWebResourceCached(Boolean.parseBoolean(webResourceCached));
+        }
+        final String withEjbRemote = config.getProperty("withEjbRemote");
+        if (withEjbRemote != null) {
+            setWithEjbRemote(Boolean.parseBoolean(withEjbRemote));
+        }
+        final String deployOpenEjbApp = config.getProperty("deployOpenEjbApp");
+        if (deployOpenEjbApp != null) {
+            setDeployOpenEjbApp(Boolean.parseBoolean(deployOpenEjbApp));
+        }
+        final String keystoreFile = config.getProperty("keystoreFile");
+        if (keystoreFile != null) {
+            setKeystoreFile(keystoreFile);
+        }
+        final String keystorePass = config.getProperty("keystorePass");
+        if (keystorePass != null) {
+            setKeystorePass(keystorePass);
+        }
+        final String keystoreType = config.getProperty("keystoreType");
+        if (keystoreType != null) {
+            setKeystoreType(keystoreType);
+        }
+        final String clientAuth = config.getProperty("clientAuth");
+        if (clientAuth != null) {
+            setClientAuth(clientAuth);
+        }
+        final String keyAlias = config.getProperty("keyAlias");
+        if (keyAlias != null) {
+            setKeyAlias(keyAlias);
+        }
+        final String sslProtocol = config.getProperty("sslProtocol");
+        if (sslProtocol != null) {
+            setSslProtocol(sslProtocol);
+        }
+        final String webXml = config.getProperty("webXml");
+        if (webXml != null) {
+            setWebXml(webXml);
+        }
+        final String tempDir = config.getProperty("tempDir");
+        if (tempDir != null) {
+            setTempDir(tempDir);
+        }
+        final String classesFilterType = config.getProperty("classesFilter");
+        if (classesFilterType != null) {
+            try {
+                setClassesFilter(Filter.class.cast(Thread.currentThread().getContextClassLoader().loadClass(classesFilterType).newInstance()));
+            } catch (final InstantiationException | IllegalAccessException | ClassNotFoundException e) {
+                throw new IllegalArgumentException(e);
             }
-            for (final String prop : config.stringPropertyNames()) {
-                if (prop.startsWith("properties.")) {
-                    property(prop.substring("properties.".length()), config.getProperty(prop));
-                } else if (prop.startsWith("users.")) {
-                    user(prop.substring("users.".length()), config.getProperty(prop));
-                } else if (prop.startsWith("roles.")) {
-                    role(prop.substring("roles.".length()), config.getProperty(prop));
-                } else if (prop.startsWith("connector.")) { // created in container
-                    property(prop, config.getProperty(prop));
-                } else if (prop.equals("realm")) {
-                    final ObjectRecipe recipe = new ObjectRecipe(config.getProperty(prop));
-                    for (final String realmConfig : config.stringPropertyNames()) {
-                        if (realmConfig.startsWith("realm.")) {
-                            recipe.setProperty(realmConfig.substring("realm.".length()), config.getProperty(realmConfig));
-                        }
-                    }
-                    setRealm(Realm.class.cast(recipe.create()));
-                } else if (prop.equals("login")) {
-                    final ObjectRecipe recipe = new ObjectRecipe(LoginConfigBuilder.class.getName());
-                    for (final String nestedConfig : config.stringPropertyNames()) {
-                        if (nestedConfig.startsWith("login.")) {
-                            recipe.setProperty(nestedConfig.substring("login.".length()), config.getProperty(nestedConfig));
-                        }
+        }
+        final String conf = config.getProperty("conf");
+        if (conf != null) {
+            setConf(conf);
+        }
+        for (final String prop : config.stringPropertyNames()) {
+            if (prop.startsWith("properties.")) {
+                property(prop.substring("properties.".length()), config.getProperty(prop));
+            } else if (prop.startsWith("users.")) {
+                user(prop.substring("users.".length()), config.getProperty(prop));
+            } else if (prop.startsWith("roles.")) {
+                role(prop.substring("roles.".length()), config.getProperty(prop));
+            } else if (prop.startsWith("connector.")) { // created in container
+                property(prop, config.getProperty(prop));
+            } else if (prop.equals("realm")) {
+                final ObjectRecipe recipe = new ObjectRecipe(config.getProperty(prop));
+                for (final String realmConfig : config.stringPropertyNames()) {
+                    if (realmConfig.startsWith("realm.")) {
+                        recipe.setProperty(realmConfig.substring("realm.".length()), config.getProperty(realmConfig));
                     }
-                    loginConfig(LoginConfigBuilder.class.cast(recipe.create()));
-                } else if (prop.equals("securityConstraint")) {
-                    final ObjectRecipe recipe = new ObjectRecipe(SecurityConstaintBuilder.class.getName());
-                    for (final String nestedConfig : config.stringPropertyNames()) {
-                        if (nestedConfig.startsWith("securityConstraints.")) {
-                            recipe.setProperty(nestedConfig.substring("securityConstraints.".length()), config.getProperty(nestedConfig));
-                        }
+                }
+                setRealm(Realm.class.cast(recipe.create()));
+            } else if (prop.equals("login")) {
+                final ObjectRecipe recipe = new ObjectRecipe(LoginConfigBuilder.class.getName());
+                for (final String nestedConfig : config.stringPropertyNames()) {
+                    if (nestedConfig.startsWith("login.")) {
+                        recipe.setProperty(nestedConfig.substring("login.".length()), config.getProperty(nestedConfig));
                     }
-                    securityConstaint(SecurityConstaintBuilder.class.cast(recipe.create()));
-                } else if (prop.equals("configurationCustomizer.")) {
-                    final String next = prop.substring("configurationCustomizer.".length());
-                    if (next.contains(".")) {
-                        continue;
+                }
+                loginConfig(LoginConfigBuilder.class.cast(recipe.create()));
+            } else if (prop.equals("securityConstraint")) {
+                final ObjectRecipe recipe = new ObjectRecipe(SecurityConstaintBuilder.class.getName());
+                for (final String nestedConfig : config.stringPropertyNames()) {
+                    if (nestedConfig.startsWith("securityConstraints.")) {
+                        recipe.setProperty(nestedConfig.substring("securityConstraints.".length()), config.getProperty(nestedConfig));
                     }
-                    final ObjectRecipe recipe = new ObjectRecipe(SecurityConstaintBuilder.class.getName());
-                    for (final String nestedConfig : config.stringPropertyNames()) {
-                        if (nestedConfig.startsWith(prop)) {
-                            recipe.setProperty(nestedConfig.substring(prop.length() + 1 /*dot*/), config.getProperty(nestedConfig));
-                        }
+                }
+                securityConstaint(SecurityConstaintBuilder.class.cast(recipe.create()));
+            } else if (prop.equals("configurationCustomizer.")) {
+                final String next = prop.substring("configurationCustomizer.".length());
+                if (next.contains(".")) {
+                    continue;
+                }
+                final ObjectRecipe recipe = new ObjectRecipe(SecurityConstaintBuilder.class.getName());
+                for (final String nestedConfig : config.stringPropertyNames()) {
+                    if (nestedConfig.startsWith(prop)) {
+                        recipe.setProperty(nestedConfig.substring(prop.length() + 1 /*dot*/), config.getProperty(nestedConfig));
                     }
-                    addCustomizer(ConfigurationCustomizer.class.cast(recipe.create()));
                 }
+                addCustomizer(ConfigurationCustomizer.class.cast(recipe.create()));
             }
-            return this;
-        } catch (final IOException e) {
-            throw new IllegalStateException(e);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/tomee/blob/a7dacaf2/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/Container.java
----------------------------------------------------------------------
diff --git a/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/Container.java b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/Container.java
index d9cd483..600a4d8 100644
--- a/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/Container.java
+++ b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/Container.java
@@ -85,6 +85,7 @@ import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
 import org.apache.xbean.finder.AnnotationFinder;
 import org.apache.xbean.finder.ResourceFinder;
 import org.apache.xbean.finder.UrlSet;
+import org.apache.xbean.finder.archive.Archive;
 import org.apache.xbean.finder.filter.Filter;
 import org.apache.xbean.finder.filter.Filters;
 import org.apache.xbean.recipe.ObjectRecipe;
@@ -111,6 +112,7 @@ import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
 import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
 
 /**
  * @version $Rev$ $Date$
@@ -209,7 +211,7 @@ public class Container implements AutoCloseable {
 
             final List<URL> urls = new ArrayList<>(jarList.length);
             for (final File jar : jarList) {
-                urls.addAll(asList(jar.toURI().toURL()));
+                urls.addAll(singletonList(jar.toURI().toURL()));
             }
             return deployPathsAsWebapp(null, urls, null);
         } catch (final MalformedURLException e) {
@@ -223,43 +225,54 @@ public class Container implements AutoCloseable {
 
     public Container deployPathsAsWebapp(final String context, final List<URL> jarList, final File docBase,
                                          final boolean keepClassloader, final String... additionalCallers) {
+        return deploy(new DeploymentRequest(context, jarList, docBase, keepClassloader, additionalCallers, null));
+    }
+
+    public Container deploy(final DeploymentRequest request) {
         final ClassLoader loader = Thread.currentThread().getContextClassLoader();
         final SystemInstance systemInstance = SystemInstance.get();
 
-        String contextRoot = context == null ? "" : context;
+        String contextRoot = request.context == null ? "" : request.context;
         if (!contextRoot.isEmpty() && !contextRoot.startsWith("/")) {
-            contextRoot = "/" + context;
+            contextRoot = "/" + request.context;
         }
 
-        File jarLocation = docBase == null || !docBase.isDirectory() ? fakeRootDir() : docBase;
+        File jarLocation = request.docBase == null || !request.docBase.isDirectory() ? fakeRootDir() : request.docBase;
         final WebModule webModule = new WebModule(new WebApp(), contextRoot, loader, jarLocation.getAbsolutePath(), contextRoot.replace("/", ""));
-        if (docBase == null) {
+        if (request.docBase == null) {
             webModule.getProperties().put("fakeJarLocation", "true");
         }
-        webModule.setUrls(jarList);
+        webModule.setUrls(request.jarList);
         webModule.setAddedUrls(Collections.<URL>emptyList());
         webModule.setRarUrls(Collections.<URL>emptyList());
-        webModule.setScannableUrls(jarList);
+        webModule.setScannableUrls(request.jarList);
         final AnnotationFinder finder;
         try {
-            final String filterContainerClasses = SystemInstance.get().getProperty("tomee.embedded.filter-container-classes");
             Filter filter = configuration.getClassesFilter();
-            if (filter == null && (jarList.size() <= 4 || "true".equalsIgnoreCase(filterContainerClasses))) {
+            if (filter == null &&
+                    (request.jarList.size() <= 4 || "true".equalsIgnoreCase(SystemInstance.get().getProperty("tomee.embedded.filter-container-classes")))) {
                 filter = new ContainerClassesFilter(configuration.getProperties());
             }
-            finder = new FinderFactory.OpenEJBAnnotationFinder(
-                    // skip container classes in scanning for shades
-                    new WebappAggregatedArchive(webModule, jarList,
-                            // see org.apache.openejb.config.DeploymentsResolver.ClasspathSearcher.cleanUpUrlSet()
-                            filter))
-                    .link();
+
+            final Archive archive;
+            if (request.archive == null) {
+                archive = new WebappAggregatedArchive(webModule, request.jarList,
+                        // see org.apache.openejb.config.DeploymentsResolver.ClasspathSearcher.cleanUpUrlSet()
+                        filter);
+            } else if (WebappAggregatedArchive.class.isInstance(request.archive)) {
+                archive = request.archive;
+            } else {
+                archive = new WebappAggregatedArchive(request.archive, request.jarList);
+            }
+
+            finder = new FinderFactory.OpenEJBAnnotationFinder(archive).link();
             SystemInstance.get().fireEvent(new TomEEEmbeddedScannerCreated(finder));
             webModule.setFinder(finder);
         } catch (final Exception e) {
             throw new IllegalArgumentException(e);
         }
 
-        final File beansXml = new File(docBase, "WEB-INF/beans.xml");
+        final File beansXml = new File(request.docBase, "WEB-INF/beans.xml");
         if (beansXml.exists()) { // add it since it is not in the scanned path by default
             try {
                 webModule.getAltDDs().put("beans.xml", beansXml.toURI().toURL());
@@ -284,16 +297,16 @@ public class Container implements AutoCloseable {
         }
 
         if (!SystemInstance.isInitialized() || Boolean.parseBoolean(SystemInstance.get().getProperty("tomee.embedded.add-callers", "true"))) {
-            addCallersAsEjbModule(loader, app, additionalCallers);
+            addCallersAsEjbModule(loader, app, request.additionalCallers);
         }
 
-        systemInstance.addObserver(new StandardContextCustomizer(configuration, webModule, keepClassloader));
+        systemInstance.addObserver(new StandardContextCustomizer(configuration, webModule, request.keepClassloader));
         if (systemInstance.getComponent(AnnotationDeployer.FolderDDMapper.class) == null) {
             systemInstance.setComponent(AnnotationDeployer.FolderDDMapper.class, new AnnotationDeployer.FolderDDMapper() {
                 @Override
                 public File getDDFolder(final File dir) {
                     try {
-                        return isMaven(dir) || isGradle(dir) ? new File(docBase, "WEB-INF") : null;
+                        return isMaven(dir) || isGradle(dir) ? new File(request.docBase, "WEB-INF") : null;
                     } catch (final RuntimeException re) { // folder doesn't exist -> test is stopped which is expected
                         return null;
                     }
@@ -1093,4 +1106,25 @@ public class Container implements AutoCloseable {
             super.start();
         }
     }
+
+    // there to allow to add params without breaking signature each time
+    public static class DeploymentRequest {
+        private final String context;
+        private final List<URL> jarList;
+        private final File docBase;
+        private final boolean keepClassloader;
+        private final String[] additionalCallers;
+        private final Archive archive;
+
+        public DeploymentRequest(final String context, final List<URL> jarList, final File docBase,
+                                 final boolean keepClassloader, final String[] additionalCallers,
+                                 final Archive archive) {
+            this.context = context;
+            this.jarList = jarList == null ? Collections.<URL>emptyList() : jarList;
+            this.docBase = docBase;
+            this.keepClassloader = keepClassloader;
+            this.additionalCallers = additionalCallers;
+            this.archive = archive;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/tomee/blob/a7dacaf2/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/TomEEEmbeddedApplicationRunner.java
----------------------------------------------------------------------
diff --git a/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/TomEEEmbeddedApplicationRunner.java b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/TomEEEmbeddedApplicationRunner.java
new file mode 100644
index 0000000..9387822
--- /dev/null
+++ b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/TomEEEmbeddedApplicationRunner.java
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.embedded;
+
+import org.apache.openejb.testing.Application;
+import org.apache.openejb.testing.ApplicationComposers;
+import org.apache.openejb.testing.Classes;
+import org.apache.openejb.testing.ContainerProperties;
+import org.apache.openejb.testing.Jars;
+import org.apache.openejb.testing.RandomPort;
+import org.apache.openejb.testing.WebResource;
+import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.inject.OWBInjector;
+import org.apache.xbean.finder.AnnotationFinder;
+import org.apache.xbean.finder.archive.Archive;
+import org.apache.xbean.finder.archive.ClassesArchive;
+import org.apache.xbean.finder.archive.FileArchive;
+
+import javax.enterprise.inject.Vetoed;
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.apache.openejb.loader.JarLocation.jarLocation;
+import static org.apache.openejb.util.Classes.ancestors;
+
+@Vetoed
+public class TomEEEmbeddedApplicationRunner implements AutoCloseable {
+    private volatile boolean started = false;
+    private volatile Object app;
+    private volatile Thread hook;
+
+    public void run(final Object app, final String... args) {
+        start(app, args);
+        try {
+            new CountDownLatch(1).await();
+        } catch (final InterruptedException e) {
+            Thread.interrupted();
+            close();
+        }
+    }
+
+    public AutoCloseable start(final Object app, final String... args) {
+        setApp(app);
+        final Properties overrides = args == null || args.length == 0 ? null : new Properties();
+        if (overrides != null) {
+            for (final String prop : args) {
+                final String[] seg = prop.split("=");
+                if (seg[0].startsWith("--")) {
+                    seg[0] = seg[0].substring("--".length());
+                }
+                overrides.put(seg[0], seg[1]);
+            }
+        }
+        try {
+            start(app.getClass(), overrides);
+            return this;
+        } catch (final Exception e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    public void setApp(final Object app) {
+        this.app = app;
+    }
+
+    public Object getApp() {
+        return app;
+    }
+
+    public synchronized void start(final Class<?> marker, final Properties config) throws Exception {
+        if (started) {
+            return;
+        }
+
+        ensureAppInit(marker);
+        started = true;
+
+        final Class<?> appClass = app.getClass();
+        final AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(ancestors(appClass)));
+
+        // setup the container config reading class annotation, using a randome http port and deploying the classpath
+        final Configuration configuration = new Configuration();
+        final ContainerProperties props = appClass.getAnnotation(ContainerProperties.class);
+        if (props != null) {
+            for (final ContainerProperties.Property p : props.value()) {
+                configuration.property(p.name(), p.value());
+            }
+        }
+
+        final List<Method> annotatedMethods = finder.findAnnotatedMethods(org.apache.openejb.testing.Configuration.class);
+        if (annotatedMethods.size() > 1) {
+            throw new IllegalArgumentException("Only one @Configuration is supported: " + annotatedMethods);
+        }
+        for (final Method m : annotatedMethods) {
+            final Object o = m.invoke(app);
+            if (Properties.class.isInstance(o)) {
+                final Properties properties = Properties.class.cast(o);
+                if (configuration.getProperties() == null) {
+                    configuration.setProperties(new Properties());
+                }
+                configuration.getProperties().putAll(properties);
+            } else {
+                throw new IllegalArgumentException("Unsupported " + o + " for @Configuration");
+            }
+        }
+
+        final Collection<Closeable> postTasks = new ArrayList<>();
+        final LifecycleTasks tasks = appClass.getAnnotation(LifecycleTasks.class);
+        if (tasks != null) {
+            for (final Class<? extends org.apache.tomee.embedded.LifecycleTask> type : tasks.value()) {
+                postTasks.add(type.newInstance().beforeContainerStartup());
+            }
+        }
+
+        final Map<String, Field> ports = new HashMap<>();
+        {
+            Class<?> type = appClass;
+            while (type != null && type != Object.class) {
+                for (final Field f : type.getDeclaredFields()) {
+                    final RandomPort annotation = f.getAnnotation(RandomPort.class);
+                    final String value = annotation == null ? null : annotation.value();
+                    if (value != null && value.startsWith("http")) {
+                        f.setAccessible(true);
+                        ports.put(value, f);
+                    }
+                }
+                type = type.getSuperclass();
+            }
+        }
+
+        if (ports.containsKey("http")) {
+            configuration.randomHttpPort();
+        }
+
+        // at least after LifecycleTasks to inherit from potential states (system properties to get a port etc...)
+        final Configurers configurers = appClass.getAnnotation(Configurers.class);
+        if (tasks != null) {
+            for (final Class<? extends Configurer> type : configurers.value()) {
+                type.newInstance().configure(configuration);
+            }
+        }
+
+        final Classes classes = appClass.getAnnotation(Classes.class);
+        String context = classes != null ? classes.context() : "";
+        context = !context.isEmpty() && context.startsWith("/") ? context.substring(1) : context;
+
+        Archive archive = null;
+        if (classes != null && classes.value().length > 0) {
+            archive = new ClassesArchive(classes.value());
+        }
+
+        final Jars jars = appClass.getAnnotation(Jars.class);
+        List<URL> urls = null;
+        if (jars != null) {
+            final Collection<File> files = ApplicationComposers.findFiles(jars);
+            urls = new ArrayList<>(files.size());
+            for (final File f : files) {
+                urls.add(f.toURI().toURL());
+            }
+        }
+
+        final WebResource resources = appClass.getAnnotation(WebResource.class);
+        if (resources != null && resources.value().length > 1) {
+            throw new IllegalArgumentException("Only one docBase is supported for now using @WebResource");
+        }
+
+        String webResource = null;
+        if (resources != null && resources.value().length > 0) {
+            webResource = resources.value()[0];
+        } else {
+            final File webapp = new File("src/main/webapp");
+            if (webapp.isDirectory()) {
+                webResource = "src/main/webapp";
+            }
+        }
+
+        if (config != null) { // override other config from annotations
+            configuration.loadFromProperties(config);
+        }
+
+        final Container container = new Container(configuration)
+                .deploy(new Container.DeploymentRequest(
+                        context,
+                        urls,
+                        webResource != null ? new File(webResource) : null,
+                        true,
+                        null,
+                        archive));
+
+        for (final Map.Entry<String, Field> f : ports.entrySet()) {
+            switch (f.getKey()) {
+                case "http":
+                    setPortField(f.getKey(), f.getValue(), configuration, context, app);
+                    break;
+                case "https":
+                    break;
+                default:
+                    throw new IllegalArgumentException("port " + f.getKey() + " not yet supported");
+            }
+        }
+
+        composerInject(app);
+
+        Runtime.getRuntime().addShutdownHook(hook = new Thread() {
+            @Override
+            public void run() { // ensure to log errors but not fail there
+                try {
+                    if (container != null) {
+                        container.close();
+                    }
+                } catch (final Exception e) {
+                    e.printStackTrace();
+                }
+                for (final Closeable c : postTasks) {
+                    try {
+                        c.close();
+                    } catch (final IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+                postTasks.clear();
+                app = null;
+                try {
+                    Runtime.getRuntime().removeShutdownHook(this);
+                } catch (final Exception e) {
+                    // no-op: that's ok at that moment if not called manually
+                }
+            }
+        });
+    }
+
+    // if app is not set then we'll check if -Dtomee.application-composer.application is set otherwise
+    // we'll try to find a single @Application class in the jar containing marker (case for tests).
+    private void ensureAppInit(final Class<?> marker) {
+        if (app != null) {
+            return;
+        }
+
+        final Class<?> type;
+        final String typeStr = System.getProperty("tomee.application-composer.application");
+        if (typeStr != null) {
+            try {
+                type = Thread.currentThread().getContextClassLoader().loadClass(typeStr);
+            } catch (final ClassNotFoundException e) {
+                throw new IllegalArgumentException(e);
+            }
+        } else if (marker == null) {
+            throw new IllegalArgumentException("set tomee.application-composer.application system property or add a marker to the rule or runner");
+        } else {
+            final Iterator<Class<?>> descriptors =
+                    new AnnotationFinder(new FileArchive(Thread.currentThread().getContextClassLoader(), jarLocation(marker)), false)
+                            .findAnnotatedClasses(Application.class).iterator();
+            if (!descriptors.hasNext()) {
+                throw new IllegalArgumentException("No descriptor class using @Application");
+            }
+            type = descriptors.next();
+            if (descriptors.hasNext()) {
+                throw new IllegalArgumentException("Ambiguous @Application: " + type + ", " + descriptors.next());
+            }
+        }
+        try {
+            app = type.newInstance();
+        } catch (final InstantiationException | IllegalAccessException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    @Override
+    public synchronized void close() {
+        if (hook != null) {
+            hook.run();
+            Runtime.getRuntime().removeShutdownHook(hook);
+            hook = null;
+            app = null;
+        }
+    }
+
+    private static void setPortField(final String key, final Field value, final Configuration configuration, final String ctx,
+                                     final Object instance) {
+        final int port = "http".equals(key) ? configuration.getHttpPort() : configuration.getHttpsPort();
+        if (value.getType() == URL.class) {
+            try {
+                value.set(instance, new URL(key + "://localhost:" + port + "/" + ctx));
+            } catch (final Exception e) {
+                throw new IllegalArgumentException(e);
+            }
+        } else if (value.getType() == int.class) {
+            try {
+                value.set(instance, port);
+            } catch (final Exception e) {
+                throw new IllegalArgumentException(e);
+            }
+        } else {
+            throw new IllegalArgumentException("Unsupported " + key);
+        }
+    }
+
+    public void composerInject(final Object target) throws IllegalAccessException {
+        WebBeansContext webBeansContext = null;
+        try {
+            webBeansContext = WebBeansContext.currentInstance();
+        } catch (final IllegalStateException ise) {
+            // no-op
+        }
+        if (webBeansContext != null) {
+            OWBInjector.inject(webBeansContext.getBeanManagerImpl(), target, null);
+        }
+
+        final Class<?> aClass = target.getClass();
+        for (final Field f : aClass.getDeclaredFields()) {
+            final RandomPort randomPort = f.getAnnotation(RandomPort.class);
+            if (randomPort != null) {
+                for (final Field field : app.getClass().getDeclaredFields()) {
+                    final RandomPort appPort = field.getAnnotation(RandomPort.class);
+                    if (field.getType() == f.getType() && appPort != null && appPort.value().equals(randomPort.value())) {
+                        if (!field.isAccessible()) {
+                            field.setAccessible(true);
+                        }
+                        if (!f.isAccessible()) {
+                            f.setAccessible(true);
+                        }
+
+                        final Object value = field.get(app);
+                        f.set(target, value);
+                        break;
+                    }
+                }
+            } else if (f.isAnnotationPresent(Application.class)) {
+                if (!f.isAccessible()) {
+                    f.setAccessible(true);
+                }
+                f.set(target, app);
+            }
+        }
+        final Class<?> superclass = aClass.getSuperclass();
+        if (superclass != Object.class) {
+            composerInject(superclass);
+        }
+    }
+
+    @Retention(RUNTIME)
+    @Target(TYPE)
+    public @interface LifecycleTasks {
+        Class<? extends org.apache.tomee.embedded.LifecycleTask>[] value();
+    }
+
+    @Retention(RUNTIME)
+    @Target(TYPE)
+    public @interface Configurers {
+        Class<? extends Configurer>[] value();
+    }
+
+    public interface Configurer {
+        void configure(Configuration configuration);
+    }
+}

http://git-wip-us.apache.org/repos/asf/tomee/blob/a7dacaf2/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/TomEEEmbeddedSingleRunner.java
----------------------------------------------------------------------
diff --git a/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/TomEEEmbeddedSingleRunner.java b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/TomEEEmbeddedSingleRunner.java
index ecf480d..6151a12 100644
--- a/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/TomEEEmbeddedSingleRunner.java
+++ b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/TomEEEmbeddedSingleRunner.java
@@ -16,18 +16,7 @@
  */
 package org.apache.tomee.embedded.junit;
 
-import org.apache.openejb.testing.Application;
-import org.apache.openejb.testing.Classes;
-import org.apache.openejb.testing.ContainerProperties;
-import org.apache.openejb.testing.RandomPort;
-import org.apache.openejb.testing.WebResource;
-import org.apache.tomee.embedded.Configuration;
-import org.apache.tomee.embedded.Container;
-import org.apache.webbeans.config.WebBeansContext;
-import org.apache.webbeans.inject.OWBInjector;
-import org.apache.xbean.finder.AnnotationFinder;
-import org.apache.xbean.finder.archive.ClassesArchive;
-import org.apache.xbean.finder.archive.FileArchive;
+import org.apache.tomee.embedded.TomEEEmbeddedApplicationRunner;
 import org.junit.rules.MethodRule;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -38,28 +27,10 @@ import org.junit.runners.model.InitializationError;
 import org.junit.runners.model.Statement;
 
 import javax.enterprise.inject.Vetoed;
-import java.io.Closeable;
-import java.io.File;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.atomic.AtomicReference;
 
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-import static org.apache.openejb.loader.JarLocation.jarLocation;
-import static org.apache.openejb.util.Classes.ancestors;
-
 /**
  * see org.apache.tomee.embedded.SingleInstanceRunnerTest for a sample.
  * idea is to reuse some part of ApplicationComposer API to get a single container for all tests in embedded mode.
@@ -81,9 +52,7 @@ import static org.apache.openejb.util.Classes.ancestors;
  */
 @Vetoed
 public class TomEEEmbeddedSingleRunner extends BlockJUnit4ClassRunner {
-    private static volatile boolean started = false;
-    private static final AtomicReference<Object> APP = new AtomicReference<>();
-    private static final AtomicReference<Thread> HOOK = new AtomicReference<>();
+    private static final AtomicReference<TomEEEmbeddedApplicationRunner> RUNNER = new AtomicReference<>();
 
     // use when you use another runner like Parameterized of JUnit
     public static class Rule implements TestRule {
@@ -98,8 +67,8 @@ public class TomEEEmbeddedSingleRunner extends BlockJUnit4ClassRunner {
             return new Statement() {
                 @Override
                 public void evaluate() throws Throwable {
-                    start(test.getClass());
-                    composerInject(test);
+                    start(test);
+                    RUNNER.get().composerInject(test);
                     base.evaluate();
                 }
             };
@@ -113,17 +82,26 @@ public class TomEEEmbeddedSingleRunner extends BlockJUnit4ClassRunner {
         }
     }
 
+    private static void start(final Object marker) throws Exception {
+        getRunner().start(marker.getClass(), (Properties) null);
+    }
+
     public static void setApp(final Object o) {
-        APP.set(o);
+        getRunner().setApp(o);
+    }
+
+    private static TomEEEmbeddedApplicationRunner getRunner() {
+        final TomEEEmbeddedApplicationRunner runner = RUNNER.get();
+        if (runner == null) {
+            RUNNER.compareAndSet(null, new TomEEEmbeddedApplicationRunner());
+        }
+        return RUNNER.get();
     }
 
     public static void close() {
-        final Thread hook = HOOK.get();
-        if (hook != null) {
-            hook.run();
-            Runtime.getRuntime().removeShutdownHook(hook);
-            HOOK.compareAndSet(hook, null);
-            APP.set(null);
+        final TomEEEmbeddedApplicationRunner runner = RUNNER.get();
+        if (runner != null) {
+            runner.close();
         }
     }
 
@@ -140,8 +118,8 @@ public class TomEEEmbeddedSingleRunner extends BlockJUnit4ClassRunner {
                 return new Statement() {
                     @Override
                     public void evaluate() throws Throwable {
-                        start(getTestClass().getJavaClass());
-                        composerInject(target);
+                        start(test);
+                        getRunner().composerInject(target);
                         base.evaluate();
                     }
                 };
@@ -149,254 +127,4 @@ public class TomEEEmbeddedSingleRunner extends BlockJUnit4ClassRunner {
         });
         return rules;
     }
-
-    private static void start(final Class<?> marker) throws Exception {
-        if (APP.get() == null) {
-            final Class<?> type;
-            final String typeStr = System.getProperty("tomee.application-composer.application");
-            if (typeStr != null) {
-                try {
-                    type = Thread.currentThread().getContextClassLoader().loadClass(typeStr);
-                } catch (final ClassNotFoundException e) {
-                    throw new IllegalArgumentException(e);
-                }
-            } else if (marker == null) {
-                throw new IllegalArgumentException("set tomee.application-composer.application system property or add a marker to the rule or runner");
-            } else {
-                final Iterator<Class<?>> descriptors =
-                        new AnnotationFinder(new FileArchive(Thread.currentThread().getContextClassLoader(), jarLocation(marker)), false)
-                                .findAnnotatedClasses(Application.class).iterator();
-                if (!descriptors.hasNext()) {
-                    throw new IllegalArgumentException("No descriptor class using @Application");
-                }
-                type = descriptors.next();
-                if (descriptors.hasNext()) {
-                    throw new IllegalArgumentException("Ambiguous @Application: " + type + ", " + descriptors.next());
-                }
-            }
-            try {
-                APP.compareAndSet(null, type.newInstance());
-            } catch (final InstantiationException | IllegalAccessException e) {
-                throw new IllegalStateException(e);
-            }
-        }
-
-        if (!started) {
-            synchronized (TomEEEmbeddedSingleRunner.class) {
-                started = true;
-
-                final Class<?> appClass = APP.get().getClass();
-                final AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(ancestors(appClass)));
-
-                // setup the container config reading class annotation, using a randome http port and deploying the classpath
-                final Configuration configuration = new Configuration();
-                final ContainerProperties props = appClass.getAnnotation(ContainerProperties.class);
-                if (props != null) {
-                    for (final ContainerProperties.Property p : props.value()) {
-                        configuration.property(p.name(), p.value());
-                    }
-                }
-
-                final List<Method> annotatedMethods = finder.findAnnotatedMethods(org.apache.openejb.testing.Configuration.class);
-                if (annotatedMethods.size() > 1) {
-                    throw new IllegalArgumentException("Only one @Configuration is supported: " + annotatedMethods);
-                }
-                for (final Method m : annotatedMethods) {
-                    final Object o = m.invoke(APP.get());
-                    if (Properties.class.isInstance(o)) {
-                        final Properties properties = Properties.class.cast(o);
-                        if (configuration.getProperties() == null) {
-                            configuration.setProperties(new Properties());
-                        }
-                        configuration.getProperties().putAll(properties);
-                    } else {
-                        throw new IllegalArgumentException("Unsupported " + o + " for @Configuration");
-                    }
-                }
-
-                final Collection<Closeable> postTasks = new ArrayList<>();
-                final LifecycleTasks tasks = appClass.getAnnotation(LifecycleTasks.class);
-                if (tasks != null) {
-                    for (final Class<? extends org.apache.tomee.embedded.LifecycleTask> type : tasks.value()) {
-                        postTasks.add(type.newInstance().beforeContainerStartup());
-                    }
-                }
-
-                final Map<String, Field> ports = new HashMap<>();
-                {
-                    Class<?> type = appClass;
-                    while (type != null && type != Object.class) {
-                        for (final Field f : type.getDeclaredFields()) {
-                            final RandomPort annotation = f.getAnnotation(RandomPort.class);
-                            final String value = annotation == null ? null : annotation.value();
-                            if (value != null && value.startsWith("http")) {
-                                f.setAccessible(true);
-                                ports.put(value, f);
-                            }
-                        }
-                        type = type.getSuperclass();
-                    }
-                }
-
-                if (ports.containsKey("http")) {
-                    configuration.randomHttpPort();
-                }
-
-                // at least after LifecycleTasks to inherit from potential states (system properties to get a port etc...)
-                final Configurers configurers = appClass.getAnnotation(Configurers.class);
-                if (tasks != null) {
-                    for (final Class<? extends Configurer> type : configurers.value()) {
-                        type.newInstance().configure(configuration);
-                    }
-                }
-
-                final Classes classes = appClass.getAnnotation(Classes.class);
-                String context = classes != null ? classes.context() : "";
-                context = !context.isEmpty() && context.startsWith("/") ? context.substring(1) : context;
-                // TODO: potentially respect classes() giving to deployClasspath a built Finder
-
-                final WebResource resources = appClass.getAnnotation(WebResource.class);
-                if (resources != null && resources.value().length > 1) {
-                    throw new IllegalArgumentException("Only one docBase is supported for now using @WebResource");
-                }
-
-                String webResource = null;
-                if (resources != null && resources.value().length > 0) {
-                    webResource = resources.value()[0];
-                } else {
-                    final File webapp = new File("src/main/webapp");
-                    if (webapp.isDirectory()) {
-                        webResource = "src/main/webapp";
-                    }
-                }
-
-                final Container container = new Container(configuration)
-                        .deployClasspathAsWebApp(context, webResource != null ? new File(webResource) : null);
-
-                for (final Map.Entry<String, Field> f : ports.entrySet()) {
-                    switch (f.getKey()) {
-                        case "http":
-                            setPortField(f.getKey(), f.getValue(), configuration, context, APP.get());
-                            break;
-                        case "https":
-                            break;
-                        default:
-                            throw new IllegalArgumentException("port " + f.getKey() + " not yet supported");
-                    }
-                }
-
-                composerInject(APP.get());
-
-                Runtime.getRuntime().addShutdownHook(new Thread() {
-                    @Override
-                    public void run() { // ensure to log errors but not fail there
-                        try {
-                            if (container != null) {
-                                container.close();
-                            }
-                        } catch (final Exception e) {
-                            e.printStackTrace();
-                        }
-                        for (final Closeable c : postTasks) {
-                            try {
-                                c.close();
-                            } catch (final IOException e) {
-                                e.printStackTrace();
-                            }
-                        }
-                        postTasks.clear();
-                        APP.set(null);
-                        try {
-                            Runtime.getRuntime().removeShutdownHook(this);
-                        } catch (final Exception e) {
-                            // no-op: that's ok at that moment if not called manually
-                        }
-                    }
-                });
-            }
-        }
-    }
-
-    private static void setPortField(final String key, final Field value, final Configuration configuration, final String ctx,
-                                     final Object instance) {
-        final int port = "http".equals(key) ? configuration.getHttpPort() : configuration.getHttpsPort();
-        if (value.getType() == URL.class) {
-            try {
-                value.set(instance, new URL(key + "://localhost:" + port + "/" + ctx));
-            } catch (final Exception e) {
-                throw new IllegalArgumentException(e);
-            }
-        } else if (value.getType() == int.class) {
-            try {
-                value.set(instance, port);
-            } catch (final Exception e) {
-                throw new IllegalArgumentException(e);
-            }
-        } else {
-            throw new IllegalArgumentException("Unsupported " + key);
-        }
-    }
-
-    private static void composerInject(final Object target) throws IllegalAccessException {
-        WebBeansContext webBeansContext = null;
-        try {
-            webBeansContext = WebBeansContext.currentInstance();
-        } catch (final IllegalStateException ise) {
-            // no-op
-        }
-        if (webBeansContext != null) {
-            OWBInjector.inject(webBeansContext.getBeanManagerImpl(), target, null);
-        }
-
-        final Object app = APP.get();
-        final Class<?> aClass = target.getClass();
-        for (final Field f : aClass.getDeclaredFields()) {
-            final RandomPort randomPort = f.getAnnotation(RandomPort.class);
-            if (randomPort != null) {
-                for (final Field field : app.getClass().getDeclaredFields()) {
-                    final RandomPort appPort = field.getAnnotation(RandomPort.class);
-                    if (field.getType() == f.getType() && appPort != null && appPort.value().equals(randomPort.value())) {
-                        if (!field.isAccessible()) {
-                            field.setAccessible(true);
-                        }
-                        if (!f.isAccessible()) {
-                            f.setAccessible(true);
-                        }
-
-                        final Object value = field.get(app);
-                        f.set(target, value);
-                        break;
-                    }
-                }
-            } else if (f.isAnnotationPresent(Application.class)) {
-                if (!f.isAccessible()) {
-                    f.setAccessible(true);
-                }
-                f.set(target, app);
-            }
-        }
-        final Class<?> superclass = aClass.getSuperclass();
-        if (superclass != Object.class) {
-            composerInject(superclass);
-        }
-    }
-
-    public interface LifecycleTask extends org.apache.tomee.embedded.LifecycleTask {
-    }
-
-    @Retention(RUNTIME)
-    @Target(TYPE)
-    public @interface LifecycleTasks {
-        Class<? extends org.apache.tomee.embedded.LifecycleTask>[] value();
-    }
-
-    @Retention(RUNTIME)
-    @Target(TYPE)
-    public @interface Configurers {
-        Class<? extends Configurer>[] value();
-    }
-
-    public interface Configurer {
-        void configure(Configuration configuration);
-    }
 }

http://git-wip-us.apache.org/repos/asf/tomee/blob/a7dacaf2/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/NoScannerSingleRunnerTest.java
----------------------------------------------------------------------
diff --git a/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/NoScannerSingleRunnerTest.java b/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/NoScannerSingleRunnerTest.java
new file mode 100644
index 0000000..570f88a
--- /dev/null
+++ b/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/NoScannerSingleRunnerTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.embedded;
+
+import org.apache.openejb.testing.Application;
+import org.apache.openejb.testing.Classes;
+import org.apache.tomee.embedded.junit.TomEEEmbeddedSingleRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.Instance;
+import javax.inject.Inject;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+// just a manual test to check it works, can't be executed with the rest of the suite,
+// we could use a different surefire execution if we want to add it to the default run
+//@Ignore("can't run with by test containers")
+@RunWith(TomEEEmbeddedSingleRunner.class)
+public class NoScannerSingleRunnerTest {
+    @Application // app can have several injections/helpers
+    private ScanApp app;
+
+    @Test
+    public void run() {
+        app.check();
+    }
+
+    @Application
+    @Classes(value = ScanMe.class)
+    public static class ScanApp {
+        @Inject
+        private ScanMe ok;
+
+        @Inject
+        private Instance<NotScanned> ko;
+
+        public void check() {
+            assertNotNull(ok);
+            assertTrue(ko.isUnsatisfied());
+        }
+    }
+
+    @ApplicationScoped
+    public static class ScanMe {
+    }
+
+    @ApplicationScoped
+    public static class NotScanned {
+    }
+}

http://git-wip-us.apache.org/repos/asf/tomee/blob/a7dacaf2/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/SingleInstanceRunnerTest.java
----------------------------------------------------------------------
diff --git a/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/SingleInstanceRunnerTest.java b/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/SingleInstanceRunnerTest.java
index 94031ea..cf2d039 100644
--- a/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/SingleInstanceRunnerTest.java
+++ b/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/SingleInstanceRunnerTest.java
@@ -24,7 +24,6 @@ import org.apache.openejb.testing.ContainerProperties;
 import org.apache.openejb.testing.RandomPort;
 import org.apache.openejb.testng.PropertiesBuilder;
 import org.apache.tomee.embedded.junit.TomEEEmbeddedSingleRunner;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -64,8 +63,8 @@ public class SingleInstanceRunnerTest {
     @Application
     @Classes(context = "app")
     @ContainerProperties(@ContainerProperties.Property(name = "t", value = "set"))
-    @TomEEEmbeddedSingleRunner.LifecycleTasks(MyTask.class) // can start a ftp/sftp/elasticsearch/mongo/... server before tomee
-    @TomEEEmbeddedSingleRunner.Configurers(SetMyProperty.class)
+    @TomEEEmbeddedApplicationRunner.LifecycleTasks(MyTask.class) // can start a ftp/sftp/elasticsearch/mongo/... server before tomee
+    @TomEEEmbeddedApplicationRunner.Configurers(SetMyProperty.class)
     public static class TheApp {
         @RandomPort("http")
         private int port;
@@ -79,7 +78,7 @@ public class SingleInstanceRunnerTest {
         }
     }
 
-    public static class MyTask implements TomEEEmbeddedSingleRunner.LifecycleTask {
+    public static class MyTask implements LifecycleTask {
         @Override
         public Closeable beforeContainerStartup() {
             System.out.println(">>> start");
@@ -93,7 +92,7 @@ public class SingleInstanceRunnerTest {
         }
     }
 
-    public static class SetMyProperty implements TomEEEmbeddedSingleRunner.Configurer {
+    public static class SetMyProperty implements TomEEEmbeddedApplicationRunner.Configurer {
         @Override
         public void configure(final Configuration configuration) {
             configuration.property("configurer", "true");