You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwebbeans.apache.org by rm...@apache.org on 2016/12/04 19:25:53 UTC

svn commit: r1772568 - in /openwebbeans/meecrowave/trunk: meecrowave-core/src/main/java/org/apache/meecrowave/openwebbeans/ meecrowave-doc/src/main/jbake/content/testing/ meecrowave-jpa/ meecrowave-junit/ meecrowave-junit/src/main/java/org/apache/meecr...

Author: rmannibucau
Date: Sun Dec  4 19:25:53 2016
New Revision: 1772568

URL: http://svn.apache.org/viewvc?rev=1772568&view=rev
Log:
junit 5 integration

Added:
    openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/
    openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveConfig.java
    openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveExtension.java
    openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MonoMeecrowaveConfig.java
    openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MonoMeecrowaveExtension.java
    openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/
    openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/ConfigurationInject.java
    openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/Injector.java
    openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/MonoBase.java
    openwebbeans/meecrowave/trunk/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/
    openwebbeans/meecrowave/trunk/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/MeecrowaveConfigTest.java
Modified:
    openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/openwebbeans/OWBTomcatWebScannerService.java
    openwebbeans/meecrowave/trunk/meecrowave-doc/src/main/jbake/content/testing/index.adoc
    openwebbeans/meecrowave/trunk/meecrowave-jpa/pom.xml
    openwebbeans/meecrowave/trunk/meecrowave-junit/pom.xml
    openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit/MonoMeecrowave.java
    openwebbeans/meecrowave/trunk/meecrowave-junit/src/test/java/org/apache/meecrowave/junit/MonoMeecrowaveRuleTest.java

Modified: openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/openwebbeans/OWBTomcatWebScannerService.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/openwebbeans/OWBTomcatWebScannerService.java?rev=1772568&r1=1772567&r2=1772568&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/openwebbeans/OWBTomcatWebScannerService.java (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/openwebbeans/OWBTomcatWebScannerService.java Sun Dec  4 19:25:53 2016
@@ -107,6 +107,15 @@ public class OWBTomcatWebScannerService
             return jreBase.length();
         }
 
+        // jar:file:spring-boot-cdi-launcher-1.0-SNAPSHOT.jar!/BOOT-INF/lib/x.jar!/
+        if (path.startsWith("jar:file:") && path.endsWith(".jar!/")) {
+            final int lastSep = path.substring(0, path.length() - 2).lastIndexOf('/');
+            if (lastSep > 0) {
+                return filter.check(PLUGGABILITY, path.substring(lastSep + 1, path.length() - 2)) ?
+                        -1 : (path.indexOf(".jar") - 1 /*should be lastIndexOf but filterExcludedJar logic would be broken*/);
+            }
+        }
+
         final int filenameIdx = path.replace(File.separatorChar, '/').replace("!/", "").lastIndexOf('/') + 1;
         if (filenameIdx < 0 || filenameIdx >= path.length()) { // unlikely
             return -1;

Modified: openwebbeans/meecrowave/trunk/meecrowave-doc/src/main/jbake/content/testing/index.adoc
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-doc/src/main/jbake/content/testing/index.adoc?rev=1772568&r1=1772567&r2=1772568&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-doc/src/main/jbake/content/testing/index.adoc (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-doc/src/main/jbake/content/testing/index.adoc Sun Dec  4 19:25:53 2016
@@ -7,7 +7,7 @@
 :jbake-meecrowavecolor: body-pink
 :icons: font
 
-== JUnit: rules and runners
+== JUnit
 
 Coordinates:
 
@@ -20,6 +20,8 @@ Coordinates:
 </dependency>
 ----
 
+=== Rules and Runners
+
 Meecrowave provides two flavors of JUnit integration: mono or nor runners/rules. The mono one will
 ensure there is a single container for the whole JVM where the other ones will follow JUnit lifecycle (per class or test).
 
@@ -65,6 +67,38 @@ access the configuration and random HTTP
 For the configuration, mono rule will use a global configuration shared by all tests. To load it
 it will use a standard `ServiceLoader` on type `org.apache.meecrowave.Meecrowave$ConfigurationCustomizer`.
 
+=== JUnit 5
+
+JUnit 5 integrates a new `Extension` system. It is not yet very well supported by IDEs but you can already use it with
+Gradle and Maven (see http://junit.org/junit5/docs/current/user-guide/#running-tests).
+
+The usage has two annotations: `@MeecrowaveConfig` which remaps most of the configuration of Meecrowave and `@MonoMeecrowaveConfig`
+which is close to `MonoMeecrowave.Runner` in term of usage.
+
+[source,java]
+----
+@MeecrowaveConfig /*(some config)*/
+public class MeecrowaveConfigTest {
+    @ConfigurationInject
+    private Meecrowave.Builder config;
+
+    @Test
+    public void run() throws MalformedURLException {
+        final String base = "http://localhost:" + config.getHttpPort();
+        // asserts
+    }
+}
+----
+
+Or
+
+[source,java]
+----
+@MonoMeecrowaveConfig
+public class MeecrowaveConfigTest {
+    // ...
+}
+----
 
 == Arquillian Container
 

Modified: openwebbeans/meecrowave/trunk/meecrowave-jpa/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-jpa/pom.xml?rev=1772568&r1=1772567&r2=1772568&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-jpa/pom.xml (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-jpa/pom.xml Sun Dec  4 19:25:53 2016
@@ -68,5 +68,11 @@
       <version>2.1</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.12</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>
\ No newline at end of file

Modified: openwebbeans/meecrowave/trunk/meecrowave-junit/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-junit/pom.xml?rev=1772568&r1=1772567&r2=1772568&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-junit/pom.xml (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-junit/pom.xml Sun Dec  4 19:25:53 2016
@@ -37,14 +37,33 @@
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>${junit.version}</version>
+      <scope>provided</scope>
     </dependency>
     <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-api</artifactId>
+      <version>${junit5.version}</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-engine</artifactId>
+      <version>${junit5.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
       <groupId>org.apache.meecrowave</groupId>
       <artifactId>meecrowave-core</artifactId>
       <version>${project.version}</version>
     </dependency>
   </dependencies>
 
+  <properties>
+    <junit5.version>5.0.0-M3</junit5.version>
+  </properties>
+
   <build>
     <plugins>
       <plugin>
@@ -54,6 +73,23 @@
           <!-- cause of mono runner/rule -->
           <reuseForks>false</reuseForks>
         </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-surefire-provider</artifactId>
+            <version>1.0.0-M3</version>
+          </dependency>
+          <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>${junit5.version}</version>
+          </dependency>
+          <dependency>
+            <groupId>org.junit.vintage</groupId>
+            <artifactId>junit-vintage-engine</artifactId>
+            <version>4.12.0-M3</version>
+          </dependency>
+        </dependencies>
       </plugin>
     </plugins>
   </build>

Modified: openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit/MonoMeecrowave.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit/MonoMeecrowave.java?rev=1772568&r1=1772567&r2=1772568&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit/MonoMeecrowave.java (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit/MonoMeecrowave.java Sun Dec  4 19:25:53 2016
@@ -19,27 +19,19 @@
 package org.apache.meecrowave.junit;
 
 import org.apache.meecrowave.Meecrowave;
+import org.apache.meecrowave.testing.Injector;
+import org.apache.meecrowave.testing.MonoBase;
 import org.junit.rules.MethodRule;
 import org.junit.runners.BlockJUnit4ClassRunner;
 import org.junit.runners.model.InitializationError;
 import org.junit.runners.model.Statement;
 
-import java.io.File;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
+import javax.enterprise.context.spi.CreationalContext;
 import java.util.List;
-import java.util.ServiceLoader;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 // a MeecrowaveRule starting a single container, very awesome for forkCount=1, reuseForks=true
 public class MonoMeecrowave {
-    private static final AtomicReference<AutoCloseable> CONTAINER = new AtomicReference<>();
-    private static final AtomicReference<Meecrowave.Builder> CONFIGURATION = new AtomicReference<>();
+    private static final MonoBase BASE = new MonoBase();
     private static final AutoCloseable NOOP_CLOSEABLE = () -> {
     };
 
@@ -54,24 +46,18 @@ public class MonoMeecrowave {
             rules.add((base, method, target) -> new Statement() {
                 @Override
                 public void evaluate() throws Throwable {
-                    doBoot();
+                    BASE.startIfNeeded();
                     configInjection(test.getClass(), test);
-                    base.evaluate();
+                    final CreationalContext<?> creationalContext = Injector.inject(true);
+                    try {
+                        base.evaluate();
+                    } finally {
+                        creationalContext.release();
+                    }
                 }
 
                 private void configInjection(final Class<?> aClass, final Object test) {
-                    Stream.of(aClass.getDeclaredFields())
-                            .filter(f -> f.isAnnotationPresent(ConfigurationInject.class))
-                            .forEach(f -> {
-                                if (!f.isAccessible()) {
-                                    f.setAccessible(true);
-                                }
-                                try {
-                                    f.set(test, CONFIGURATION.get());
-                                } catch (final IllegalAccessException e) {
-                                    throw new IllegalStateException(e);
-                                }
-                            });
+                    Injector.injectConfig(BASE.getConfiguration(), test);
                     final Class<?> parent = aClass.getSuperclass();
                     if (parent != null && parent != Object.class) {
                         configInjection(parent, true);
@@ -80,74 +66,18 @@ public class MonoMeecrowave {
             });
             return rules;
         }
-
-        /**
-         * Only working with the runner
-         */
-        @Target(FIELD)
-        @Retention(RUNTIME)
-        public @interface ConfigurationInject {
-        }
     }
 
     public static class Rule extends MeecrowaveRuleBase<Rule> {
         @Override
         public Meecrowave.Builder getConfiguration() {
-            return CONFIGURATION.get();
+            return BASE.getConfiguration();
         }
 
         @Override
         protected AutoCloseable onStart() {
-            if (CONTAINER.get() == null) { // yes synchro could be simpler but it does the job, feel free to rewrite it
-                synchronized (CONTAINER) {
-                    if (CONTAINER.get() == null) {
-                        doBoot();
-                    }
-                }
-            }
+            BASE.startIfNeeded();
             return NOOP_CLOSEABLE;
         }
     }
-
-    private static void doBoot() {
-        final Meecrowave.Builder configuration = new Meecrowave.Builder().randomHttpPort().noShutdownHook(/*the rule does*/);
-        StreamSupport.stream(ServiceLoader.load(Meecrowave.ConfigurationCustomizer.class).spliterator(), false)
-                .forEach(c -> c.accept(configuration));
-        CONFIGURATION.compareAndSet(null, configuration);
-
-        final Meecrowave meecrowave = new Meecrowave(CONFIGURATION.get());
-        if (CONTAINER.compareAndSet(null, meecrowave)) {
-            final Configuration runnerConfig = StreamSupport.stream(ServiceLoader.load(Configuration.class).spliterator(), false)
-                    .findAny()
-                    .orElseGet(() -> new Configuration() {
-                    });
-
-            final File war = runnerConfig.application();
-            if (war == null) {
-                meecrowave.bake(runnerConfig.context());
-            } else {
-                meecrowave.deployWebapp(runnerConfig.context(), runnerConfig.application());
-            }
-            Runtime.getRuntime().addShutdownHook(new Thread() {
-                {
-                    setName("Meecrowave-mono-rue-stopping");
-                }
-
-                @Override
-                public void run() {
-                    meecrowave.close();
-                }
-            });
-        }
-    }
-
-    public interface Configuration {
-        default String context() {
-            return "";
-        }
-
-        default File application() {
-            return null;
-        }
-    }
 }

Added: openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveConfig.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveConfig.java?rev=1772568&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveConfig.java (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveConfig.java Sun Dec  4 19:25:53 2016
@@ -0,0 +1,86 @@
+/*
+ * 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.meecrowave.junit5;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target(TYPE)
+@Retention(RUNTIME)
+@ExtendWith(MeecrowaveExtension.class)
+public @interface MeecrowaveConfig {
+    // deployment related config
+    String context() default "";
+
+    // container config (note: ensure to keep type and default matching, see MeecrowaveExtension impl)
+    int httpPort() default  -1;
+    int httpsPort() default -1;
+    int stopPort() default -1;
+    String host() default "localhost";
+    String dir() default "";
+    String serverXml() default "";
+    boolean keepServerXmlAsThis() default false;
+    boolean quickSession() default true;
+    boolean skipHttp() default false;
+    boolean ssl() default false;
+    String keystoreFile() default "";
+    String keystorePass() default "";
+    String keystoreType() default "JKS";
+    String clientAuth() default "";
+    String keyAlias() default "";
+    String sslProtocol() default "";
+    String webXml() default "";
+    boolean http2() default false;
+    String tempDir() default "";
+    boolean webResourceCached() default false;
+    String conf() default "";
+    boolean deleteBaseOnStartup() default true;
+    String jaxrsMapping() default "/*";
+    boolean cdiConversation() default false;
+    boolean jaxrsProviderSetup() default true;
+    String jaxrsDefaultProviders() default "";
+    boolean jaxrsLogProviders() default false;
+    String jsonpBufferStrategy() default "QUEUE";
+    int jsonpMaxStringLen() default 10 * 1024 * 1024;
+    int jsonpMaxReadBufferLen() default 64 * 1024;
+    int jsonpMaxWriteBufferLen() default 64 * 1024;
+    boolean jsonpSupportsComment() default false;
+    boolean jsonpPrettify() default false;
+    String jsonbEncoding() default "UTF-8";
+    boolean jsonbNulls() default false;
+    boolean jsonbIJson() default false;
+    boolean jsonbPrettify() default false;
+    String jsonbBinaryStrategy() default "";
+    String jsonbNamingStrategy() default "";
+    String jsonbOrderStrategy() default "";
+    boolean loggingGlobalSetup() default true;
+    boolean tomcatScanning() default true;
+    boolean tomcatAutoSetup() default true;
+    boolean useShutdownHook() default true;
+    String tomcatFilter() default "";
+    boolean useTomcatDefaults() default true;
+    boolean tomcatWrapLoader() default false;
+    String sharedLibraries() default "";
+    boolean useLog4j2JulLogManager() default false;
+}

Added: openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveExtension.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveExtension.java?rev=1772568&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveExtension.java (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveExtension.java Sun Dec  4 19:25:53 2016
@@ -0,0 +1,102 @@
+/*
+ * 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.meecrowave.junit5;
+
+import org.apache.meecrowave.Meecrowave;
+import org.apache.meecrowave.testing.Injector;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ContainerExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.TestExtensionContext;
+
+import javax.enterprise.context.spi.CreationalContext;
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Optional;
+
+public class MeecrowaveExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback {
+    private static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace.create(MeecrowaveExtension.class.getName());
+
+    @Override
+    public void beforeAll(final ContainerExtensionContext context) throws Exception {
+        final Meecrowave.Builder builder = new Meecrowave.Builder();
+        final Optional<MeecrowaveConfig> meecrowaveConfig = context.getElement().map(e -> e.getAnnotation(MeecrowaveConfig.class));
+        final String ctx;
+        if (meecrowaveConfig.isPresent()) {
+            final MeecrowaveConfig config = meecrowaveConfig.get();
+            ctx = config.context();
+
+            for (final Method method : MeecrowaveConfig.class.getMethods()) {
+                if (MeecrowaveConfig.class != method.getDeclaringClass()) {
+                    continue;
+                }
+
+                try {
+                    final Object value = method.invoke(config);
+
+                    final Field configField = Meecrowave.Builder.class.getDeclaredField(method.getName());
+                    if (!configField.isAccessible()) {
+                        configField.setAccessible(true);
+                    }
+
+                    if (value != null && (!String.class.isInstance(value) || !value.toString().isEmpty())) {
+                        if (!configField.isAccessible()) {
+                            configField.setAccessible(true);
+                        }
+                        configField.set(builder, File.class == configField.getType() ? /*we use string instead */new File(value.toString()) : value);
+                    }
+                } catch (final NoSuchFieldException nsfe) {
+                    // ignored
+                } catch (final Exception e) {
+                    throw new IllegalStateException(e);
+                }
+            }
+
+            if (builder.getHttpPort() < 0) {
+                builder.randomHttpPort();
+            }
+        } else {
+            ctx = "";
+        }
+        final Meecrowave meecrowave = new Meecrowave(builder);
+        context.getStore(NAMESPACE).put(Meecrowave.class.getName(), meecrowave);
+        context.getStore(NAMESPACE).put(Meecrowave.Builder.class.getName(), builder);
+        meecrowave.bake(ctx);
+    }
+
+    @Override
+    public void afterAll(final ContainerExtensionContext context) throws Exception {
+        Meecrowave.class.cast(context.getStore(NAMESPACE).get(Meecrowave.class.getName())).close();
+    }
+
+    @Override
+    public void beforeEach(final TestExtensionContext context) throws Exception {
+        context.getStore(NAMESPACE).put(CreationalContext.class.getName(), Injector.inject(context.getTestInstance()));
+        Injector.injectConfig(Meecrowave.Builder.class.cast(context.getStore(NAMESPACE).get(Meecrowave.Builder.class.getName())), context.getTestInstance());
+    }
+
+    @Override
+    public void afterEach(final TestExtensionContext context) throws Exception {
+        CreationalContext.class.cast(context.getStore(NAMESPACE).get(CreationalContext.class.getName())).release();
+    }
+}

Added: openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MonoMeecrowaveConfig.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MonoMeecrowaveConfig.java?rev=1772568&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MonoMeecrowaveConfig.java (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MonoMeecrowaveConfig.java Sun Dec  4 19:25:53 2016
@@ -0,0 +1,34 @@
+/*
+ * 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.meecrowave.junit5;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target(TYPE)
+@Retention(RUNTIME)
+@ExtendWith(MonoMeecrowaveExtension.class)
+public @interface MonoMeecrowaveConfig {
+    // no config since it is shared
+}

Added: openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MonoMeecrowaveExtension.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MonoMeecrowaveExtension.java?rev=1772568&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MonoMeecrowaveExtension.java (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MonoMeecrowaveExtension.java Sun Dec  4 19:25:53 2016
@@ -0,0 +1,52 @@
+/*
+ * 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.meecrowave.junit5;
+
+import org.apache.meecrowave.Meecrowave;
+import org.apache.meecrowave.testing.Injector;
+import org.apache.meecrowave.testing.MonoBase;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ContainerExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.TestExtensionContext;
+
+import javax.enterprise.context.spi.CreationalContext;
+
+public class MonoMeecrowaveExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback {
+    private static final MonoBase BASE = new MonoBase();
+    private static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace.create(MonoMeecrowaveExtension.class.getName());
+
+    @Override
+    public void beforeAll(final ContainerExtensionContext context) throws Exception {
+        context.getStore(NAMESPACE).put(Meecrowave.Builder.class.getName(), BASE.startIfNeeded());
+    }
+
+    @Override
+    public void beforeEach(final TestExtensionContext context) throws Exception {
+        context.getStore(NAMESPACE).put(CreationalContext.class.getName(), Injector.inject(context.getTestInstance()));
+        Injector.injectConfig(Meecrowave.Builder.class.cast(context.getStore(NAMESPACE).get(Meecrowave.Builder.class.getName())), context.getTestInstance());
+    }
+
+    @Override
+    public void afterEach(final TestExtensionContext context) throws Exception {
+        CreationalContext.class.cast(context.getStore(NAMESPACE).get(CreationalContext.class.getName())).release();
+    }
+}

Added: openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/ConfigurationInject.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/ConfigurationInject.java?rev=1772568&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/ConfigurationInject.java (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/ConfigurationInject.java Sun Dec  4 19:25:53 2016
@@ -0,0 +1,30 @@
+/*
+ * 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.meecrowave.testing;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target(FIELD)
+@Retention(RUNTIME)
+public @interface ConfigurationInject {
+}

Added: openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/Injector.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/Injector.java?rev=1772568&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/Injector.java (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/Injector.java Sun Dec  4 19:25:53 2016
@@ -0,0 +1,59 @@
+/*
+ * 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.meecrowave.testing;
+
+import org.apache.meecrowave.Meecrowave;
+
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.AnnotatedType;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.CDI;
+import javax.enterprise.inject.spi.InjectionTarget;
+import java.util.stream.Stream;
+
+public final class Injector {
+    private Injector() {
+        // no-op
+    }
+
+    public static CreationalContext<?> inject(final Object testInstance) {
+        final BeanManager bm = CDI.current().getBeanManager();
+        final AnnotatedType<?> annotatedType = bm.createAnnotatedType(testInstance.getClass());
+        final InjectionTarget injectionTarget = bm.createInjectionTarget(annotatedType);
+        final CreationalContext<?> creationalContext = bm.createCreationalContext(null);
+        injectionTarget.inject(testInstance, creationalContext);
+        return creationalContext;
+    }
+
+    public static void injectConfig(final Meecrowave.Builder config, final Object test) {
+        final Class<?> aClass = test.getClass();
+        Stream.of(aClass.getDeclaredFields())
+                .filter(f -> f.isAnnotationPresent(ConfigurationInject.class))
+                .forEach(f -> {
+                    if (!f.isAccessible()) {
+                        f.setAccessible(true);
+                    }
+                    try {
+                        f.set(test, config);
+                    } catch (final IllegalAccessException e) {
+                        throw new IllegalStateException(e);
+                    }
+                });
+    }
+}

Added: openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/MonoBase.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/MonoBase.java?rev=1772568&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/MonoBase.java (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-junit/src/main/java/org/apache/meecrowave/testing/MonoBase.java Sun Dec  4 19:25:53 2016
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.meecrowave.testing;
+
+import org.apache.meecrowave.Meecrowave;
+
+import java.io.File;
+import java.util.ServiceLoader;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.StreamSupport;
+
+public class MonoBase {
+    private static final AtomicReference<AutoCloseable> CONTAINER = new AtomicReference<>();
+    private static final AtomicReference<Meecrowave.Builder> CONFIGURATION = new AtomicReference<>();
+
+    public Meecrowave.Builder doBoot() {
+        final Meecrowave.Builder configuration = new Meecrowave.Builder().randomHttpPort().noShutdownHook(/*the rule does*/);
+        StreamSupport.stream(ServiceLoader.load(Meecrowave.ConfigurationCustomizer.class).spliterator(), false)
+                .forEach(c -> c.accept(configuration));
+        CONFIGURATION.compareAndSet(null, configuration);
+
+        final Meecrowave meecrowave = new Meecrowave(CONFIGURATION.get());
+        if (CONTAINER.compareAndSet(null, meecrowave)) {
+            final Configuration runnerConfig = StreamSupport.stream(ServiceLoader.load(Configuration.class).spliterator(), false)
+                    .findAny()
+                    .orElseGet(() -> new Configuration() {
+                    });
+
+            final File war = runnerConfig.application();
+            if (war == null) {
+                meecrowave.bake(runnerConfig.context());
+            } else {
+                meecrowave.deployWebapp(runnerConfig.context(), runnerConfig.application());
+            }
+            Runtime.getRuntime().addShutdownHook(new Thread() {
+                {
+                    setName("Meecrowave-mono-rue-stopping");
+                }
+
+                @Override
+                public void run() {
+                    meecrowave.close();
+                }
+            });
+        }
+        return getConfiguration();
+    }
+
+    public Meecrowave.Builder getConfiguration() {
+        return CONFIGURATION.get();
+    }
+
+    public Meecrowave.Builder startIfNeeded() {
+        if (CONTAINER.get() == null) { // yes synchro could be simpler but it does the job, feel free to rewrite it
+            synchronized (CONTAINER) {
+                if (CONTAINER.get() == null) {
+                    doBoot();
+                }
+            }
+        }
+        return getConfiguration();
+    }
+
+    public interface Configuration {
+        default String context() {
+            return "";
+        }
+
+        default File application() {
+            return null;
+        }
+    }
+}

Modified: openwebbeans/meecrowave/trunk/meecrowave-junit/src/test/java/org/apache/meecrowave/junit/MonoMeecrowaveRuleTest.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-junit/src/test/java/org/apache/meecrowave/junit/MonoMeecrowaveRuleTest.java?rev=1772568&r1=1772567&r2=1772568&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-junit/src/test/java/org/apache/meecrowave/junit/MonoMeecrowaveRuleTest.java (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-junit/src/test/java/org/apache/meecrowave/junit/MonoMeecrowaveRuleTest.java Sun Dec  4 19:25:53 2016
@@ -20,6 +20,7 @@ package org.apache.meecrowave.junit;
 
 import org.apache.meecrowave.Meecrowave;
 import org.apache.meecrowave.io.IO;
+import org.apache.meecrowave.testing.ConfigurationInject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -37,7 +38,7 @@ public class MonoMeecrowaveRuleTest {
     public static final MonoMeecrowave.Rule RULE = new MonoMeecrowave.Rule();
     */
 
-    @MonoMeecrowave.Runner.ConfigurationInject
+    @ConfigurationInject
     private Meecrowave.Builder config;
 
     @Test

Added: openwebbeans/meecrowave/trunk/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/MeecrowaveConfigTest.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/MeecrowaveConfigTest.java?rev=1772568&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/MeecrowaveConfigTest.java (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/MeecrowaveConfigTest.java Sun Dec  4 19:25:53 2016
@@ -0,0 +1,52 @@
+/*
+ * 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.meecrowave.junit5;
+
+import org.apache.meecrowave.Meecrowave;
+import org.apache.meecrowave.io.IO;
+import org.apache.meecrowave.testing.ConfigurationInject;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+@MeecrowaveConfig
+public class MeecrowaveConfigTest {
+    @ConfigurationInject
+    private Meecrowave.Builder config;
+
+    @Test
+    public void run() throws MalformedURLException {
+        assertEquals("simple", slurp(new URL("http://localhost:" + config.getHttpPort() + "/api/test")));
+    }
+
+    private String slurp(final URL url) {
+        try (final InputStream is = url.openStream()) {
+            return IO.toString(is);
+        } catch (final IOException e) {
+            fail(e.getMessage());
+        }
+        return null;
+    }
+}