Merge branch 'master' of github.com:apache/tomee
diff --git a/container/openejb-core/pom.xml b/container/openejb-core/pom.xml
index 2000d30..d28bcad 100644
--- a/container/openejb-core/pom.xml
+++ b/container/openejb-core/pom.xml
@@ -313,10 +313,10 @@
</execution>
</executions>
</plugin>
+
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
- <version>${surefire.version}</version>
<configuration>
<childDelegation>true</childDelegation>
<reuseForks>false</reuseForks>
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 4207e74..a915654 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
@@ -940,6 +940,11 @@
}
}
+ public void enrich(final Object inputTestInstance) throws org.apache.openejb.OpenEJBException {
+ final BeanContext context = SystemInstance.get().getComponent(ContainerSystem.class).getBeanContext(inputTestInstance.getClass());
+ enrich(inputTestInstance, context);
+ }
+
private void enrich(final Object inputTestInstance, final BeanContext context) throws org.apache.openejb.OpenEJBException {
if (context == null) {
return;
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/testing/SingleApplicationComposerBase.java b/container/openejb-core/src/main/java/org/apache/openejb/testing/SingleApplicationComposerBase.java
new file mode 100644
index 0000000..c54577d
--- /dev/null
+++ b/container/openejb-core/src/main/java/org/apache/openejb/testing/SingleApplicationComposerBase.java
@@ -0,0 +1,159 @@
+/*
+ * 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.openejb.testing;
+
+import org.apache.openejb.core.ThreadContext;
+import org.apache.openejb.util.JavaSecurityManagers;
+import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.inject.OWBInjector;
+import org.apache.xbean.finder.AnnotationFinder;
+import org.apache.xbean.finder.archive.FileArchive;
+
+import java.lang.reflect.Field;
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.openejb.loader.JarLocation.jarLocation;
+
+// goal is to share the same container for all embedded tests and hold the config there
+// only works if all tests use the same config
+public class SingleApplicationComposerBase {
+ private static volatile boolean started = false;
+ private static final AtomicReference<Object> APP = new AtomicReference<>();
+ private static final AtomicReference<Thread> HOOK = new AtomicReference<>();
+
+ public void setApp(final Object o) {
+ APP.set(o);
+ }
+
+ public void close() {
+ final Thread hook = HOOK.get();
+ if (hook != null) {
+ hook.run();
+ Runtime.getRuntime().removeShutdownHook(hook);
+ HOOK.compareAndSet(hook, null);
+ APP.set(null);
+ }
+ }
+
+ public void start(final Class<?> marker) throws Exception {
+ if (APP.get() == null) {
+ final Class<?> type;
+ final String typeStr = JavaSecurityManagers.getSystemProperty("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) {
+ final Object app = APP.get();
+ final ApplicationComposers composers = new ApplicationComposers(app.getClass()) {
+ @Override
+ public void deployApp(final Object inputTestInstance) throws Exception {
+ super.deployApp(inputTestInstance);
+ if (!started) {
+ final ThreadContext previous = ThreadContext.getThreadContext(); // done here for logging
+ final ApplicationComposers comp = this;
+ final Thread hook = new Thread() {
+ @Override
+ public void run() {
+ try {
+ comp.after();
+ } catch (final Exception e) {
+ ThreadContext.exit(previous);
+ throw new IllegalStateException(e);
+ }
+ }
+ };
+ HOOK.set(hook);
+ Runtime.getRuntime().addShutdownHook(hook);
+ started = true;
+ }
+ }
+ };
+ composers.before(app);
+ composers.handleLifecycle(app.getClass(), app);
+ }
+ }
+
+ public void composerInject(final Object target) throws IllegalAccessException {
+ WebBeansContext wbc = null;
+ try {
+ wbc = WebBeansContext.currentInstance();
+ } catch (final IllegalStateException ise) {
+ // no-op
+ }
+ if (wbc != null) {
+ OWBInjector.inject(wbc.getBeanManagerImpl(), target, null);
+ }
+
+ final Object app = APP.get();
+ final Class<?> aClass = target.getClass();
+ for (final Field f : aClass.getDeclaredFields()) {
+ if (f.isAnnotationPresent(RandomPort.class)) {
+ for (final Field field : app.getClass().getDeclaredFields()) {
+ if (field.getType() == f.getType()) {
+ 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 boolean isStarted() {
+ return started;
+ }
+}
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/testing/SingleApplicationComposerRunner.java b/container/openejb-core/src/main/java/org/apache/openejb/testing/SingleApplicationComposerRunner.java
index b35b7f5..1b1eb07 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/testing/SingleApplicationComposerRunner.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/testing/SingleApplicationComposerRunner.java
@@ -16,12 +16,6 @@
*/
package org.apache.openejb.testing;
-import org.apache.openejb.core.ThreadContext;
-import org.apache.openejb.util.JavaSecurityManagers;
-import org.apache.webbeans.config.WebBeansContext;
-import org.apache.webbeans.inject.OWBInjector;
-import org.apache.xbean.finder.AnnotationFinder;
-import org.apache.xbean.finder.archive.FileArchive;
import org.junit.rules.MethodRule;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
@@ -31,19 +25,12 @@
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
-import java.lang.reflect.Field;
-import java.util.Iterator;
import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static org.apache.openejb.loader.JarLocation.jarLocation;
// goal is to share the same container for all embedded tests and hold the config there
// only works if all tests use the same config
public class SingleApplicationComposerRunner 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 SingleApplicationComposerBase BASE = new SingleApplicationComposerBase();
// use when you use another runner like Parameterized of JUnit
public static class Rule implements TestRule {
@@ -58,8 +45,8 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
- start(test.getClass());
- composerInject(test);
+ BASE.start(test.getClass());
+ BASE.composerInject(test);
base.evaluate();
}
};
@@ -69,22 +56,12 @@
public static class Start extends RunListener {
@Override
public void testStarted(final Description description) throws Exception {
- start(null);
+ BASE.start(null);
}
}
public static void setApp(final Object o) {
- APP.set(o);
- }
-
- 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);
- }
+ BASE.setApp(o);
}
public SingleApplicationComposerRunner(final Class<?> klass) throws InitializationError {
@@ -100,8 +77,8 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
- start(getTestClass().getJavaClass());
- composerInject(target);
+ BASE.start(getTestClass().getJavaClass());
+ BASE.composerInject(target);
base.evaluate();
}
};
@@ -109,107 +86,4 @@
});
return rules;
}
-
- private static void start(final Class<?> marker) throws Exception {
- if (APP.get() == null) {
- final Class<?> type;
- final String typeStr = JavaSecurityManagers.getSystemProperty("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) {
- final Object app = APP.get();
- final ApplicationComposers composers = new ApplicationComposers(app.getClass()) {
- @Override
- public void deployApp(final Object inputTestInstance) throws Exception {
- super.deployApp(inputTestInstance);
- if (!started) {
- final ThreadContext previous = ThreadContext.getThreadContext(); // done here for logging
- final ApplicationComposers comp = this;
- final Thread hook = new Thread() {
- @Override
- public void run() {
- try {
- comp.after();
- } catch (final Exception e) {
- ThreadContext.exit(previous);
- throw new IllegalStateException(e);
- }
- }
- };
- HOOK.set(hook);
- Runtime.getRuntime().addShutdownHook(hook);
- started = true;
- }
- }
- };
- composers.before(app);
- composers.handleLifecycle(app.getClass(), app);
- }
- }
-
- private static void composerInject(final Object target) throws IllegalAccessException {
- WebBeansContext wbc = null;
- try {
- wbc = WebBeansContext.currentInstance();
- } catch (final IllegalStateException ise) {
- // no-op
- }
- if (wbc != null) {
- OWBInjector.inject(wbc.getBeanManagerImpl(), target, null);
- }
-
- final Object app = APP.get();
- final Class<?> aClass = target.getClass();
- for (final Field f : aClass.getDeclaredFields()) {
- if (f.isAnnotationPresent(RandomPort.class)) {
- for (final Field field : app.getClass().getDeclaredFields()) {
- if (field.getType() == f.getType()) {
- 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);
- }
- }
}
diff --git a/container/openejb-junit5-backward/pom.xml b/container/openejb-junit5-backward/pom.xml
index bdd326a..247cdd5 100644
--- a/container/openejb-junit5-backward/pom.xml
+++ b/container/openejb-junit5-backward/pom.xml
@@ -34,13 +34,11 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
- <version>${junit.jupiter.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
- <version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -62,7 +60,7 @@
</dependency>
</dependencies>
<properties>
- <tomee.build.name>${project.groupId}.container.junit5</tomee.build.name>
+ <tomee.build.name>${project.groupId}.container.junit5.backward</tomee.build.name>
<netbeans.hint.license>openejb</netbeans.hint.license>
</properties>
@@ -71,7 +69,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
- <version>${surefire.junit5.version}</version>
</plugin>
</plugins>
</build>
diff --git a/container/openejb-junit5-backward/src/main/java/org/apache/openejb/junit5/jee/EjbContainerExtension.java b/container/openejb-junit5-backward/src/main/java/org/apache/openejb/junit5/jee/EjbContainerExtension.java
index 13bd310..f99d4ec 100644
--- a/container/openejb-junit5-backward/src/main/java/org/apache/openejb/junit5/jee/EjbContainerExtension.java
+++ b/container/openejb-junit5-backward/src/main/java/org/apache/openejb/junit5/jee/EjbContainerExtension.java
@@ -18,6 +18,7 @@
import org.apache.openejb.Injector;
import org.apache.openejb.OpenEJBException;
+import org.apache.openejb.OpenEJBRuntimeException;
import org.apache.openejb.OpenEjbContainer;
import org.apache.openejb.injection.FallbackPropertyInjector;
import org.apache.openejb.junit.jee.config.Properties;
@@ -48,12 +49,8 @@
@Override
public void beforeAll(ExtensionContext extensionContext) throws Exception {
- Optional<Class<?>> oClazz = extensionContext.getTestClass();
-
- if (!oClazz.isPresent()) {
- throw new RuntimeException("Could not get class from extension context");
- }
- Class<?> clazz = oClazz.get();
+ Class<?> clazz = extensionContext.getTestClass()
+ .orElseThrow(() -> new OpenEJBRuntimeException("Could not get class from extension context"));
properties = new java.util.Properties();
@@ -119,8 +116,8 @@
@Override
public void beforeEach(ExtensionContext extensionContext) throws Exception {
- Class<?> clazz = extensionContext.getTestClass().isPresent() ? extensionContext.getTestClass().get() : null;
- Object test = extensionContext.getTestInstance().isPresent() ? extensionContext.getTestInstance().get() : null;
+ Class<?> clazz = extensionContext.getTestClass().orElse(null);
+ Object test = extensionContext.getTestInstance().orElse(null);
if (clazz != null){
diff --git a/container/openejb-junit5-backward/src/main/java/org/apache/openejb/junit5/jee/transaction/TransactionExtension.java b/container/openejb-junit5-backward/src/main/java/org/apache/openejb/junit5/jee/transaction/TransactionExtension.java
index 369f176..fe52498 100644
--- a/container/openejb-junit5-backward/src/main/java/org/apache/openejb/junit5/jee/transaction/TransactionExtension.java
+++ b/container/openejb-junit5-backward/src/main/java/org/apache/openejb/junit5/jee/transaction/TransactionExtension.java
@@ -16,6 +16,7 @@
*/
package org.apache.openejb.junit5.jee.transaction;
+import org.apache.openejb.OpenEJBRuntimeException;
import org.apache.openejb.core.transaction.JtaTransactionPolicyFactory;
import org.apache.openejb.core.transaction.TransactionPolicy;
import org.apache.openejb.core.transaction.TransactionType;
@@ -30,7 +31,8 @@
@Override
public void afterTestExecution(ExtensionContext extensionContext) throws Exception {
- final Method mtd = extensionContext.getTestMethod().get();
+ final Method mtd = extensionContext.getTestMethod()
+ .orElseThrow(() -> new OpenEJBRuntimeException("Could not get test method from extension context."));
final Transaction tx = mtd.getAnnotation(Transaction.class);
if (tx != null) {
final TransactionManager transactionManager = SystemInstance.get().getComponent(TransactionManager.class);
diff --git a/container/openejb-junit5/LICENSE b/container/openejb-junit5/LICENSE
new file mode 100644
index 0000000..10d7a73
--- /dev/null
+++ b/container/openejb-junit5/LICENSE
@@ -0,0 +1,417 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+=================================================================================
+JUnit
+
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
+CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+ a) in the case of the initial Contributor, the initial code and
+ documentation distributed under this Agreement, and
+ b) in the case of each subsequent Contributor:
+
+ i) changes to the Program, and
+
+ ii) additions to the Program;
+
+ where such changes and/or additions to the Program originate from and are
+distributed by that particular Contributor. A Contribution 'originates' from a
+Contributor if it was added to the Program by such Contributor itself or anyone
+acting on such Contributor's behalf. Contributions do not include additions to
+the Program which: (i) are separate modules of software distributed in
+conjunction with the Program under their own license agreement, and (ii) are
+not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor which are
+necessarily infringed by the use or sale of its Contribution alone or when
+combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement,
+including all Contributors.
+
+2. GRANT OF RIGHTS
+
+ a) Subject to the terms of this Agreement, each Contributor hereby grants
+Recipient a non-exclusive, worldwide, royalty-free copyright license to
+reproduce, prepare derivative works of, publicly display, publicly perform,
+distribute and sublicense the Contribution of such Contributor, if any, and
+such derivative works, in source code and object code form.
+
+ b) Subject to the terms of this Agreement, each Contributor hereby grants
+Recipient a non-exclusive, worldwide, royalty-free patent license under
+Licensed Patents to make, use, sell, offer to sell, import and otherwise
+transfer the Contribution of such Contributor, if any, in source code and
+object code form. This patent license shall apply to the combination of the
+Contribution and the Program if, at the time the Contribution is added by the
+Contributor, such addition of the Contribution causes such combination to be
+covered by the Licensed Patents. The patent license shall not apply to any
+other combinations which include the Contribution. No hardware per se is
+licensed hereunder.
+
+ c) Recipient understands that although each Contributor grants the
+licenses to its Contributions set forth herein, no assurances are provided by
+any Contributor that the Program does not infringe the patent or other
+intellectual property rights of any other entity. Each Contributor disclaims
+any liability to Recipient for claims brought by any other entity based on
+infringement of intellectual property rights or otherwise. As a condition to
+exercising the rights and licenses granted hereunder, each Recipient hereby
+assumes sole responsibility to secure any other intellectual property rights
+needed, if any. For example, if a third party patent license is required to
+allow Recipient to distribute the Program, it is Recipient's responsibility to
+acquire that license before distributing the Program.
+
+ d) Each Contributor represents that to its knowledge it has sufficient
+copyright rights in its Contribution, if any, to grant the copyright license
+set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under
+its own license agreement, provided that:
+
+ a) it complies with the terms and conditions of this Agreement; and
+
+ b) its license agreement:
+
+ i) effectively disclaims on behalf of all Contributors all warranties and
+conditions, express and implied, including warranties or conditions of title
+and non-infringement, and implied warranties or conditions of merchantability
+and fitness for a particular purpose;
+
+ ii) effectively excludes on behalf of all Contributors all liability for
+damages, including direct, indirect, special, incidental and consequential
+damages, such as lost profits;
+
+ iii) states that any provisions which differ from this Agreement are
+offered by that Contributor alone and not by any other party; and
+
+ iv) states that source code for the Program is available from such
+Contributor, and informs licensees how to obtain it in a reasonable manner on
+or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+ a) it must be made available under this Agreement; and
+
+ b) a copy of this Agreement must be included with each copy of the
+Program.
+
+Contributors may not remove or alter any copyright notices contained within the
+Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if
+any, in a manner that reasonably allows subsequent Recipients to identify the
+originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with
+respect to end users, business partners and the like. While this license is
+intended to facilitate the commercial use of the Program, the Contributor who
+includes the Program in a commercial product offering should do so in a manner
+which does not create potential liability for other Contributors. Therefore, if
+a Contributor includes the Program in a commercial product offering, such
+Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
+every other Contributor ("Indemnified Contributor") against any losses, damages
+and costs (collectively "Losses") arising from claims, lawsuits and other legal
+actions brought by a third party against the Indemnified Contributor to the
+extent caused by the acts or omissions of such Commercial Contributor in
+connection with its distribution of the Program in a commercial product
+offering. The obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement. In order
+to qualify, an Indemnified Contributor must: a) promptly notify the Commercial
+Contributor in writing of such claim, and b) allow the Commercial Contributor
+to control, and cooperate with the Commercial Contributor in, the defense and
+any related settlement negotiations. The Indemnified Contributor may
+participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product
+offering, Product X. That Contributor is then a Commercial Contributor. If that
+Commercial Contributor then makes performance claims, or offers warranties
+related to Product X, those performance claims and warranties are such
+Commercial Contributor's responsibility alone. Under this section, the
+Commercial Contributor would have to defend claims against the other
+Contributors related to those performance claims and warranties, and if a court
+requires any other Contributor to pay any damages as a result, the Commercial
+Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
+Recipient is solely responsible for determining the appropriateness of using
+and distributing the Program and assumes all risks associated with its exercise
+of rights under this Agreement, including but not limited to the risks and
+costs of program errors, compliance with applicable laws, damage to or loss of
+data, programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
+CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
+PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS
+GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable
+law, it shall not affect the validity or enforceability of the remainder of the
+terms of this Agreement, and without further action by the parties hereto, such
+provision shall be reformed to the minimum extent necessary to make such
+provision valid and enforceable.
+
+If Recipient institutes patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging that the
+Program itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's rights
+granted under Section 2(b) shall terminate as of the date such litigation is
+filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to
+comply with any of the material terms or conditions of this Agreement and does
+not cure such failure in a reasonable period of time after becoming aware of
+such noncompliance. If all Recipient's rights under this Agreement terminate,
+Recipient agrees to cease use and distribution of the Program as soon as
+reasonably practicable. However, Recipient's obligations under this Agreement
+and any licenses granted by Recipient relating to the Program shall continue
+and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in
+order to avoid inconsistency the Agreement is copyrighted and may only be
+modified in the following manner. The Agreement Steward reserves the right to
+publish new versions (including revisions) of this Agreement from time to time.
+No one other than the Agreement Steward has the right to modify this Agreement.
+The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to
+serve as the Agreement Steward to a suitable separate entity. Each new version
+of the Agreement will be given a distinguishing version number. The Program
+(including Contributions) may always be distributed subject to the version of
+the Agreement under which it was received. In addition, after a new version of
+the Agreement is published, Contributor may elect to distribute the Program
+(including its Contributions) under the new version. Except as expressly stated
+in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to
+the intellectual property of any Contributor under this Agreement, whether
+expressly, by implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the
+intellectual property laws of the United States of America. No party to this
+Agreement will bring a legal action under this Agreement more than one year
+after the cause of action arose. Each party waives its rights to a jury trial
+in any resulting litigation.
+
diff --git a/container/openejb-junit5/NOTICE b/container/openejb-junit5/NOTICE
new file mode 100644
index 0000000..a9f2f42
--- /dev/null
+++ b/container/openejb-junit5/NOTICE
@@ -0,0 +1,9 @@
+
+Apache OpenEJB
+Copyright 1999-2014 The Apache OpenEJB development community
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+==========================================================
+junit - JUnit is a simple framework to write repeatable tests (http://junit.org/)
+License: Eclipse Public License - v 1.0
diff --git a/container/openejb-junit5/pom.xml b/container/openejb-junit5/pom.xml
new file mode 100644
index 0000000..589e573
--- /dev/null
+++ b/container/openejb-junit5/pom.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>container</artifactId>
+ <groupId>org.apache.tomee</groupId>
+ <version>8.0.7-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>openejb-junit5</artifactId>
+ <packaging>jar</packaging>
+ <name>TomEE :: Container :: JUnit 5</name>
+
+ <properties>
+ <tomee.build.name>${project.groupId}.container.junit5</tomee.build.name>
+ <netbeans.hint.license>openejb</netbeans.hint.license>
+ <jacocoArgLine/>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>openejb-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.openejb.itests</groupId>
+ <artifactId>failover-ejb</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <childDelegation>true</childDelegation>
+ <reuseForks>false</reuseForks>
+ <forkCount>1</forkCount>
+ <argLine>
+ ${jacocoArgLine}
+ -enableassertions
+ </argLine>
+ <workingDirectory>${project.basedir}/target</workingDirectory>
+ <systemPropertyVariables>
+ <openejb.home>${project.basedir}/target/test-classes</openejb.home>
+ </systemPropertyVariables>
+ </configuration>
+ <executions>
+ <execution>
+ <id>default-test</id>
+ <phase>test</phase>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ <configuration>
+ <excludes>
+ <exclude>org/apache/openejb/junit5/SingleAppComposerTest</exclude>
+ <exclude>org/apache/openejb/junit5/SingleAppComposerJVMTest</exclude>
+ </excludes>
+ </configuration>
+ </execution>
+ <execution>
+ <id>per-jvm-tests</id>
+ <phase>test</phase>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ <configuration>
+ <forkCount>0</forkCount>
+ <includes>
+ <include>org/apache/openejb/junit5/SingleAppComposerTest</include>
+ <include>org/apache/openejb/junit5/SingleAppComposerJVMTest</include>
+ </includes>
+ <argLine>
+ ${jacocoArgLine}
+ -enableassertions
+ -Djunit.jupiter.testclass.order.default=org.apache.openejb.junit5.order.AppComposerTestClassOrderer
+ -Dtomee.application-composer.application=org.apache.openejb.junit5.app.MyApp
+ </argLine>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/AfterAllReleaser.java b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/AfterAllReleaser.java
new file mode 100644
index 0000000..fcc65b9
--- /dev/null
+++ b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/AfterAllReleaser.java
@@ -0,0 +1,27 @@
+/*
+ * 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.openejb.junit5;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+public class AfterAllReleaser extends AfterReleaserBase {
+
+ AfterAllReleaser(ExtensionContext.Namespace namespace) {
+ super(namespace);
+ }
+
+}
diff --git a/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/AfterEachReleaser.java b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/AfterEachReleaser.java
new file mode 100644
index 0000000..162c768
--- /dev/null
+++ b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/AfterEachReleaser.java
@@ -0,0 +1,27 @@
+/*
+ * 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.openejb.junit5;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+public class AfterEachReleaser extends AfterReleaserBase {
+
+ AfterEachReleaser(ExtensionContext.Namespace namespace) {
+ super(namespace);
+ }
+}
+
diff --git a/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/AfterReleaserBase.java b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/AfterReleaserBase.java
new file mode 100644
index 0000000..920c536
--- /dev/null
+++ b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/AfterReleaserBase.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.junit5;
+
+import org.apache.openejb.testing.ApplicationComposers;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+public abstract class AfterReleaserBase extends ApplicationComposerExtensionBase {
+
+ private final ExtensionContext.Namespace namespace;
+
+ AfterReleaserBase(ExtensionContext.Namespace namespace) {
+ this.namespace = namespace;
+ }
+
+ void run(final ExtensionContext extensionContext) throws Exception {
+ extensionContext.getStore(namespace).get(ApplicationComposers.class, ApplicationComposers.class).after();
+ }
+}
diff --git a/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerExtension.java b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerExtension.java
new file mode 100644
index 0000000..eaacb3d
--- /dev/null
+++ b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerExtension.java
@@ -0,0 +1,85 @@
+/*
+ * 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.openejb.junit5;
+
+import org.apache.openejb.OpenEJBRuntimeException;
+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.ExtensionContext;
+
+public class ApplicationComposerExtension extends ApplicationComposerExtensionBase implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback {
+
+ private static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace.create(ApplicationComposerExtension.class.getName());
+
+ private final Object[] modules;
+
+ public ApplicationComposerExtension() {
+ this((Object[]) null);
+ }
+
+ public ApplicationComposerExtension(Object... modules) {
+ this.modules = modules;
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+
+ if (isPerJvm(context)) {
+ if(this.modules != null && this.modules.length > 0) {
+ throw new IllegalArgumentException("Modules are not supported in PER_JVM mode. Set 'tomee.application-composer.application' system property.");
+ }
+ context.getStore(NAMESPACE).put(ApplicationComposerPerXYExtensionBase.class, new ApplicationComposerPerJVMExtension());
+ } else if (isPerAll(context)) {
+ context.getStore(NAMESPACE).put(ApplicationComposerPerXYExtensionBase.class, new ApplicationComposerPerAllExtension(this.modules));
+ } else if (isPerEach(context)) {
+ context.getStore(NAMESPACE).put(ApplicationComposerPerXYExtensionBase.class, new ApplicationComposerPerEachExtension(this.modules));
+ } else if (isPerDefault(context)) {
+ context.getStore(NAMESPACE).put(ApplicationComposerPerXYExtensionBase.class, new ApplicationComposerPerDefaultExtension(this.modules));
+ } else {
+ throw new OpenEJBRuntimeException("No ExtensionMode is present.");
+ }
+
+ context.getStore(NAMESPACE).get(ApplicationComposerPerXYExtensionBase.class, ApplicationComposerPerXYExtensionBase.class).beforeAll(context);
+ }
+
+ @Override
+ public void afterAll(ExtensionContext context) throws Exception {
+ ApplicationComposerPerXYExtensionBase delegate = context.getStore(NAMESPACE).get(ApplicationComposerPerXYExtensionBase.class, ApplicationComposerPerXYExtensionBase.class);
+ if(delegate != null) {
+ delegate.afterAll(context);
+ }
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ ApplicationComposerPerXYExtensionBase delegate = context.getStore(NAMESPACE).get(ApplicationComposerPerXYExtensionBase.class, ApplicationComposerPerXYExtensionBase.class);
+ if(delegate != null) {
+ delegate.beforeEach(context);
+ }
+ }
+
+ @Override
+ public void afterEach(ExtensionContext context) throws Exception {
+ ApplicationComposerPerXYExtensionBase delegate = context.getStore(NAMESPACE).get(ApplicationComposerPerXYExtensionBase.class, ApplicationComposerPerXYExtensionBase.class);
+ if(delegate != null) {
+ delegate.afterEach(context);
+ }
+ }
+
+}
diff --git a/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerExtensionBase.java b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerExtensionBase.java
new file mode 100644
index 0000000..24a095b
--- /dev/null
+++ b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerExtensionBase.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.openejb.junit5;
+
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.platform.commons.util.AnnotationUtils;
+
+import java.util.Optional;
+
+public abstract class ApplicationComposerExtensionBase {
+
+ boolean isPerClassLifecycle(final ExtensionContext context) {
+ return isPerTestInstanceLifecycle(context, TestInstance.Lifecycle.PER_CLASS);
+ }
+
+ boolean isPerMethodLifecycle(final ExtensionContext context) {
+ return isPerTestInstanceLifecycle(context, TestInstance.Lifecycle.PER_METHOD);
+ }
+
+ boolean isPerTestInstanceLifecycle(final ExtensionContext context, TestInstance.Lifecycle lifecycle) {
+ return context.getTestInstanceLifecycle()
+ .map(it -> it.equals(lifecycle))
+ .orElse(false);
+ }
+
+ protected boolean isPerEach(final ExtensionContext context) {
+ return checkMode(context, ExtensionMode.PER_EACH);
+ }
+
+ boolean isPerAll(final ExtensionContext context) {
+ return checkMode(context, ExtensionMode.PER_ALL);
+ }
+
+ boolean isPerJvm(final ExtensionContext context) {
+ return checkMode(context, ExtensionMode.PER_JVM);
+ }
+
+ boolean isPerDefault(final ExtensionContext context) {
+ return checkMode(context, ExtensionMode.AUTO);
+ }
+
+ boolean checkMode(final ExtensionContext context, ExtensionMode extensionMode ) {
+ return extensionMode == getModeFromAnnotation(context);
+ }
+
+ ExtensionMode getModeFromAnnotation(final ExtensionContext context) {
+ return context.getTestClass()
+ .flatMap(test -> AnnotationUtils.findAnnotation(test, RunWithApplicationComposer.class))
+ .map(RunWithApplicationComposer::mode)
+ .orElse(ExtensionMode.AUTO);
+ }
+}
diff --git a/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerAllExtension.java b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerAllExtension.java
new file mode 100644
index 0000000..7f901ae
--- /dev/null
+++ b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerAllExtension.java
@@ -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.openejb.junit5;
+
+import org.apache.openejb.util.LogCategory;
+import org.apache.openejb.util.Logger;
+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.ExtensionContext;
+
+public class ApplicationComposerPerAllExtension extends ApplicationComposerPerXYExtensionBase implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback {
+
+ private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB, ApplicationComposerPerAllExtension.class);
+
+ public ApplicationComposerPerAllExtension() {
+ this((Object[]) null);
+ }
+
+ public ApplicationComposerPerAllExtension(Object... modules) {
+ super(modules);
+ }
+
+ @Override
+ protected void validate(ExtensionContext context) {
+ super.validate(context);
+ if (isPerAll(context) && isPerMethodLifecycle(context)) {
+ logger.info("Running PER_ALL in combination with TestInstance.Lifecycle.PER_METHOD.");
+ logger.info("Please note, there are some limitations (N = amount of test methods):");
+ logger.info("N = 1: Will work as expected.");
+ logger.info("N > 1: Injections are lost after the first test method was executed.");
+ logger.info("N > 1: Use CDI.current(), InitialContext or pure (http) client to implement the test.");
+ }
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+ super.beforeAll(context);
+ doInit(context);
+ doStart(context);
+ doInject(context);
+ addAfterAllReleaser(context);
+ }
+}
diff --git a/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerDefaultExtension.java b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerDefaultExtension.java
new file mode 100644
index 0000000..1b88018
--- /dev/null
+++ b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerDefaultExtension.java
@@ -0,0 +1,57 @@
+/*
+ * 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.openejb.junit5;
+
+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.ExtensionContext;
+
+public class ApplicationComposerPerDefaultExtension extends ApplicationComposerPerXYExtensionBase implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback {
+
+ public ApplicationComposerPerDefaultExtension() {
+ this((Object[]) null);
+ }
+
+ public ApplicationComposerPerDefaultExtension(Object... modules) {
+ super(modules);
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+ super.beforeAll(context);
+ if (isPerClassLifecycle(context)) {
+ doInit(context);
+ doStart(context);
+ doInject(context);
+ addAfterAllReleaser(context);
+ } else {
+ addAfterEachReleaser(context);
+ }
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ if (isPerMethodLifecycle(context)) {
+ doInit(context);
+ doStart(context);
+ doInject(context);
+ }
+ }
+
+}
diff --git a/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerEachExtension.java b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerEachExtension.java
new file mode 100644
index 0000000..5e1e3d1
--- /dev/null
+++ b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerEachExtension.java
@@ -0,0 +1,47 @@
+/*
+ * 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.openejb.junit5;
+
+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.ExtensionContext;
+
+public class ApplicationComposerPerEachExtension extends ApplicationComposerPerXYExtensionBase implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback {
+
+ public ApplicationComposerPerEachExtension() {
+ this((Object[]) null);
+ }
+
+ public ApplicationComposerPerEachExtension(Object... modules) {
+ super(modules);
+ }
+
+ @Override
+ public void afterEach(ExtensionContext context) {
+ addAfterEachReleaser(context);
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ doInit(context);
+ doStart(context);
+ doInject(context);
+ }
+
+}
diff --git a/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerJVMExtension.java b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerJVMExtension.java
new file mode 100644
index 0000000..7a9e974
--- /dev/null
+++ b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerJVMExtension.java
@@ -0,0 +1,108 @@
+/*
+ * 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.openejb.junit5;
+
+import org.apache.openejb.OpenEJBRuntimeException;
+import org.apache.openejb.testing.Classes;
+import org.apache.openejb.testing.Component;
+import org.apache.openejb.testing.Default;
+import org.apache.openejb.testing.Jars;
+import org.apache.openejb.testing.Module;
+import org.apache.openejb.testing.SingleApplicationComposerBase;
+import org.apache.xbean.finder.ClassFinder;
+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.ExtensionContext;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.openejb.util.Classes.ancestors;
+
+public class ApplicationComposerPerJVMExtension extends ApplicationComposerPerXYExtensionBase implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback {
+
+ private static final SingleApplicationComposerBase BASE = new SingleApplicationComposerBase();
+
+ @Override
+ protected void validate(ExtensionContext context) {
+ if (!isPerJvm(context) && BASE.isStarted()) {
+ //XXX: Future work: We might get it to work via a JVM singleton/lock, see https://github.com/apache/tomee/pull/767#discussion_r595343572
+ throw new OpenEJBRuntimeException("Cannot run PER_JVM in combination with PER_ALL, PER_EACH or AUTO");
+ }
+
+ Class<?> clazz = context.getTestClass()
+ .orElseThrow(() -> new OpenEJBRuntimeException("Could not obtain test class from extension context"));
+
+ final List<Throwable> errors = new ArrayList<>();
+
+ ClassFinder classFinder = new ClassFinder(ancestors(clazz));
+
+ Class<? extends Annotation>[] toCheck = new Class[]{Component.class, Module.class, Classes.class, Default.class, Jars.class};
+
+ for (Class<? extends Annotation> annotation : toCheck) {
+ if (classFinder.isAnnotationPresent(annotation)) {
+ errors.add(new Exception("@" + annotation.getName() + " is not allowed with @Application in PER_JVM mode"));
+ }
+ }
+
+ if (!errors.isEmpty()) {
+ throw new OpenEJBRuntimeException(errors.toString());
+ }
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+ super.beforeAll(context);
+
+ doInit(context);
+ doStart(context);
+ if (isPerClassLifecycle(context)) {
+ doInject(context);
+ }
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) {
+ if (isPerMethodLifecycle(context)) {
+ doInject(context);
+ }
+ }
+
+ @Override
+ void doInit(final ExtensionContext extensionContext) {
+ //no-op
+ }
+
+ @Override
+ void doStart(final ExtensionContext extensionContext) throws Exception {
+ BASE.start(extensionContext.getTestClass().orElse(null));
+ }
+
+ @Override
+ void doInject(Object target, final ExtensionContext context) throws Exception {
+ BASE.composerInject(target);
+ }
+
+ public static boolean isStarted() {
+ return BASE.isStarted();
+ }
+
+}
+
diff --git a/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerXYExtensionBase.java b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerXYExtensionBase.java
new file mode 100644
index 0000000..7a19e70
--- /dev/null
+++ b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ApplicationComposerPerXYExtensionBase.java
@@ -0,0 +1,133 @@
+/*
+ * 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.openejb.junit5;
+
+import org.apache.openejb.OpenEJBRuntimeException;
+import org.apache.openejb.testing.ApplicationComposers;
+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.ExtensionContext;
+import org.junit.jupiter.api.extension.TestInstances;
+
+import java.util.List;
+
+abstract class ApplicationComposerPerXYExtensionBase extends ApplicationComposerExtensionBase implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback {
+
+ private static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace.create(ApplicationComposerPerXYExtensionBase.class.getName());
+
+ private final Object[] modules;
+
+ public ApplicationComposerPerXYExtensionBase() {
+ this((Object[]) null);
+ }
+
+ public ApplicationComposerPerXYExtensionBase(Object... modules) {
+ this.modules = modules;
+ }
+
+ protected void validate(ExtensionContext context) {
+ if (!isPerJvm(context) && ApplicationComposerPerJVMExtension.isStarted()) {
+ //XXX: Future work: We might get it to work via a JVM singleton/lock, see https://github.com/apache/tomee/pull/767#discussion_r595343572
+ throw new OpenEJBRuntimeException("Cannot run PER_JVM in combination with PER_ALL, PER_EACH or AUTO");
+ }
+ }
+
+ @Override
+ public void afterAll(ExtensionContext context) throws Exception {
+ AfterAllReleaser releaser = context.getStore(NAMESPACE).get(AfterAllReleaser.class, AfterAllReleaser.class);
+ if (releaser != null) {
+ releaser.run(context);
+ }
+ }
+
+ @Override
+ public void afterEach(ExtensionContext context) throws Exception {
+ AfterEachReleaser releaser = context.getStore(NAMESPACE).get(AfterEachReleaser.class, AfterEachReleaser.class);
+ if (releaser != null) {
+ releaser.run(context);
+ }
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext extensionContext) throws Exception {
+ validate(extensionContext);
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext extensionContext) throws Exception {
+ //no-op
+ }
+
+ void doInit(final ExtensionContext extensionContext) {
+ Class<?> oClazz = extensionContext.getTestClass()
+ .orElseThrow(() -> new OpenEJBRuntimeException("Could not get test class from the given extension context."));
+
+ extensionContext.getStore(NAMESPACE).put(ApplicationComposers.class,
+ new ApplicationComposers(oClazz, this.modules));
+
+ }
+
+ void doStart(final ExtensionContext extensionContext) throws Exception {
+ TestInstances oTestInstances = extensionContext.getTestInstances()
+ .orElseThrow(() -> new OpenEJBRuntimeException("No test instances available for the given extension context."));
+
+ List<Object> testInstances = oTestInstances.getAllInstances();
+
+ ApplicationComposers delegate = extensionContext.getStore(NAMESPACE)
+ .get(ApplicationComposers.class, ApplicationComposers.class);
+
+ testInstances.forEach(t -> {
+ try {
+ delegate.before(t);
+ } catch (Exception e) {
+ throw new OpenEJBRuntimeException(e);
+ }
+ });
+ }
+
+ void doInject(final ExtensionContext extensionContext) {
+ TestInstances oTestInstances = extensionContext.getTestInstances()
+ .orElseThrow(() -> new OpenEJBRuntimeException("No test instances available for the given extension context."));
+
+ List<Object> testInstances = oTestInstances.getAllInstances();
+
+ testInstances.forEach(target -> {
+ try {
+ doInject(target, extensionContext);
+ } catch (Exception e) {
+ throw new OpenEJBRuntimeException(e);
+ }
+ });
+ }
+
+ void doInject(Object target, final ExtensionContext context) throws Exception {
+ ApplicationComposers delegate = context.getStore(NAMESPACE)
+ .get(ApplicationComposers.class, ApplicationComposers.class);
+ delegate.enrich(target);
+ }
+
+ void addAfterAllReleaser(ExtensionContext context) {
+ context.getStore(NAMESPACE).put(AfterAllReleaser.class, new AfterAllReleaser(NAMESPACE));
+ }
+
+ void addAfterEachReleaser(ExtensionContext context) {
+ context.getStore(NAMESPACE).put(AfterEachReleaser.class, new AfterEachReleaser(NAMESPACE));
+ }
+}
+
diff --git a/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ExtensionMode.java b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ExtensionMode.java
new file mode 100644
index 0000000..4eb8da1
--- /dev/null
+++ b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/ExtensionMode.java
@@ -0,0 +1,38 @@
+/*
+ * 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.openejb.junit5;
+
+public enum ExtensionMode {
+
+ /**
+ * When using this mode, a container instance will be created once for the JVM lifetime.
+ */
+ PER_JVM,
+
+ /**
+ * When using this mode, a container instance will be created for each test class
+ */
+ PER_ALL,
+
+ /**
+ * When using this mode, a container instance will be created for each test
+ */
+ PER_EACH,
+
+ AUTO;
+
+}
\ No newline at end of file
diff --git a/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/RunWithApplicationComposer.java b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/RunWithApplicationComposer.java
new file mode 100644
index 0000000..12ee2b3
--- /dev/null
+++ b/container/openejb-junit5/src/main/java/org/apache/openejb/junit5/RunWithApplicationComposer.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.openejb.junit5;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@ExtendWith(ApplicationComposerExtension.class)
+public @interface RunWithApplicationComposer {
+
+ /**
+ * The test container lifecycle <em>mode</em> to use.
+ */
+ ExtensionMode mode() default ExtensionMode.AUTO;
+}
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AddInnerTest.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AddInnerTest.java
new file mode 100644
index 0000000..a0aacc3
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AddInnerTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.openejb.junit5;
+
+import org.apache.openejb.jee.WebApp;
+import org.apache.openejb.testing.Classes;
+import org.apache.openejb.testing.Module;
+import org.junit.jupiter.api.Test;
+
+import javax.inject.Inject;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@RunWithApplicationComposer
+public class AddInnerTest {
+ @Module
+ @Classes(innerClassesAsBean = true, cdi = true)
+ public WebApp web() {
+ return new WebApp();
+ }
+
+ @Inject
+ private Injectable notNull;
+
+ @Test
+ public void run() {
+ assertNotNull(notNull);
+ }
+
+ public static class Injectable {}
+}
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerContextInjectionTest.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerContextInjectionTest.java
new file mode 100644
index 0000000..c83ec5c
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerContextInjectionTest.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.junit5;
+
+import org.apache.openejb.jee.EnterpriseBean;
+import org.apache.openejb.jee.SingletonBean;
+import org.apache.openejb.rest.ThreadLocalContextManager;
+import org.apache.openejb.testing.AppResource;
+import org.apache.openejb.testing.Module;
+import org.apache.openejb.testing.rest.ContextProvider;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.ws.rs.core.SecurityContext;
+import java.security.Principal;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+@RunWithApplicationComposer(mode = ExtensionMode.PER_ALL)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class AppComposerContextInjectionTest {
+ @Module
+ public EnterpriseBean bean() {
+ return new SingletonBean(MyBean.class).localBean();
+ }
+
+ @AppResource
+ private Context context;
+
+ @AppResource
+ private ContextProvider provider;
+
+ @Test
+ public void lookupShouldWorkOnOpenEJBNames() throws NamingException {
+ assertEquals("ok", MyBean.class.cast(context.lookup("MyBeanLocalBean")).ok());
+ }
+
+ @Test
+ public void lookupShouldWorkOnGlobalNames() throws NamingException {
+ assertEquals("ok", MyBean.class.cast(context.lookup("java:global/AppComposerContextInjectionTest/bean/MyBean")).ok());
+ }
+
+ @Test
+ public void jaxrs() throws NamingException {
+ assertNotNull(provider);
+ assertNull(provider.find(SecurityContext.class));
+ final SecurityContext securityContext = new SecurityContext() {
+ @Override
+ public Principal getUserPrincipal() {
+ return null;
+ }
+
+ @Override
+ public boolean isUserInRole(final String s) {
+ return "foo".equals(s);
+ }
+
+ @Override
+ public boolean isSecure() {
+ return false;
+ }
+
+ @Override
+ public String getAuthenticationScheme() {
+ return null;
+ }
+ };
+ provider.register(SecurityContext.class, securityContext);
+ assertNotNull(provider.find(SecurityContext.class));
+ assertTrue(SecurityContext.class.cast(ThreadLocalContextManager.findThreadLocal(SecurityContext.class)).isUserInRole("foo"));
+ assertFalse(SecurityContext.class.cast(ThreadLocalContextManager.findThreadLocal(SecurityContext.class)).isUserInRole("bar"));
+ }
+
+ public static class MyBean {
+ public String ok() {
+ return "ok";
+ }
+ }
+}
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerDynamicTest.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerDynamicTest.java
new file mode 100644
index 0000000..ff5fa0f
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerDynamicTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.openejb.junit5;
+
+import org.apache.openejb.jee.WebApp;
+import org.apache.openejb.junit5.RunWithApplicationComposer;
+import org.apache.openejb.testing.Classes;
+import org.apache.openejb.testing.Module;
+import org.junit.jupiter.api.DynamicTest;
+import org.junit.jupiter.api.TestFactory;
+
+import javax.inject.Inject;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@RunWithApplicationComposer
+public class AppComposerDynamicTest {
+
+ private static final Map<String, String> domainMap = new HashMap<>();
+
+ {
+ domainMap.put("www.somedomain.com","154.174.10.56");
+ domainMap.put("www.anotherdomain.com","211.152.104.132");
+ domainMap.put("www.yetanotherdomain.com","78.144.120.15");
+ }
+
+ @Inject
+ private DemoResolver resolver;
+
+ @Module
+ @Classes(innerClassesAsBean = true, cdi = true)
+ public WebApp web() {
+ return new WebApp();
+ }
+
+ @TestFactory
+ public Stream<DynamicTest> dynamicTestsFromStream() {
+
+ Set<String> input = domainMap.keySet();
+
+ return input.stream()
+ .map(dom -> DynamicTest.dynamicTest("Resolving: " + dom,
+ () -> {
+ assertEquals(domainMap.get(dom), resolver.resolve(dom));
+ }));
+ }
+
+ public static class DemoResolver {
+ public String resolve(String domain) {
+ return domainMap.get(domain);
+ }
+
+ }
+}
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerJarsTest.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerJarsTest.java
new file mode 100644
index 0000000..594d3d0
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerJarsTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.openejb.junit5;
+
+import org.apache.openejb.itest.failover.ejb.Calculator;
+import org.apache.openejb.jee.WebApp;
+import org.apache.openejb.junit5.RunWithApplicationComposer;
+import org.apache.openejb.testing.Jars;
+import org.apache.openejb.testing.Module;
+import org.junit.jupiter.api.Test;
+
+import javax.ejb.EJB;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@RunWithApplicationComposer
+public class AppComposerJarsTest {
+ @Module
+ @Jars("failover-ejb-")
+ public WebApp war() {
+ return new WebApp();
+ }
+
+ @EJB
+ private Calculator calculator;
+
+ @Test
+ public void externalBeanFound() {
+ assertNotNull(calculator);
+ assertEquals(3, calculator.sum(1, 2));
+ }
+}
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerOpenejbConfigTest.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerOpenejbConfigTest.java
new file mode 100644
index 0000000..b4db2f6
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerOpenejbConfigTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.openejb.junit5;
+
+import org.apache.openejb.jee.EjbJar;
+import org.apache.openejb.junit5.RunWithApplicationComposer;
+import org.apache.openejb.testing.AppResource;
+import org.apache.openejb.testing.Configuration;
+import org.apache.openejb.testing.Module;
+import org.junit.jupiter.api.Test;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@RunWithApplicationComposer
+public class AppComposerOpenejbConfigTest {
+ @Configuration
+ public String openejbXmlPath() {
+ return "custom-openejb.xml";
+ }
+
+ @Module
+ public EjbJar empty() {
+ return new EjbJar();
+ }
+
+ @AppResource
+ private Context ctx;
+
+ @Test
+ public void checkDsIsHere() throws NamingException {
+ assertNotNull(ctx.lookup("openejb:Resource/app-composer"));
+ }
+}
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerWithModulesTest.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerWithModulesTest.java
new file mode 100644
index 0000000..11c09e0
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/AppComposerWithModulesTest.java
@@ -0,0 +1,211 @@
+/**
+ * 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.openejb.junit5;
+
+import java.util.Properties;
+
+import javax.annotation.Resource;
+import javax.ejb.EJB;
+import javax.ejb.EJBException;
+import javax.ejb.LocalBean;
+import javax.ejb.Stateless;
+import javax.ejb.TransactionAttribute;
+import javax.ejb.TransactionAttributeType;
+import javax.naming.InitialContext;
+import javax.persistence.Entity;
+import javax.persistence.EntityManager;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.PersistenceContext;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import org.apache.openejb.jee.Empty;
+import org.apache.openejb.jee.StatelessBean;
+import org.apache.openejb.jee.jpa.unit.Persistence;
+import org.apache.openejb.jee.jpa.unit.PersistenceUnit;
+import org.apache.openejb.junit5.ApplicationComposerPerEachExtension;
+import org.apache.openejb.testing.Configuration;
+import org.apache.openejb.testing.Module;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class AppComposerWithModulesTest {
+
+ @RegisterExtension
+ ApplicationComposerPerEachExtension ext = new ApplicationComposerPerEachExtension(new Modules());
+
+ @EJB
+ private PersistManager persistManager;
+
+ @Resource
+ private Validator validator;
+
+ @Resource
+ private ValidatorFactory validatorFactory;
+
+ @Configuration
+ public Properties config() {
+ final Properties p = new Properties();
+ p.put("bvalDatabase", "new://Resource?type=DataSource");
+ p.put("bvalDatabase.JdbcDriver", "org.hsqldb.jdbcDriver");
+ p.put("bvalDatabase.JdbcUrl", "jdbc:hsqldb:mem:bval");
+ return p;
+ }
+
+ public static class Modules {
+ @Module
+ public StatelessBean app() throws Exception {
+ final StatelessBean bean = new StatelessBean(PersistManager.class);
+ bean.setLocalBean(new Empty());
+ return bean;
+ }
+
+ @Module
+ public Persistence persistence() {
+ final PersistenceUnit unit = new PersistenceUnit("foo-unit");
+ unit.addClass(EntityToValidate.class);
+ unit.setProperty("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)");
+ unit.getProperties().setProperty("openjpa.RuntimeUnenhancedClasses", "supported");
+ unit.setExcludeUnlistedClasses(true);
+
+ final Persistence persistence = new Persistence(unit);
+ persistence.setVersion("2.0");
+ return persistence;
+ }
+ }
+
+ @LocalBean
+ @Stateless
+ public static class PersistManager {
+ @PersistenceContext
+ private EntityManager em;
+
+ @Resource
+ private Validator validator;
+
+ @Resource
+ private ValidatorFactory validatorFactory;
+
+ public void persistValid() {
+ final EntityToValidate entity = new EntityToValidate();
+ entity.setName("name");
+ em.persist(entity);
+ }
+
+ public void persistNotValid() {
+ em.persist(new EntityToValidate());
+ }
+
+ @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
+ public Validator getValidator() {
+ return validator;
+ }
+
+ @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
+ public ValidatorFactory getValidatorFactory() {
+ return validatorFactory;
+ }
+ }
+
+ @Entity
+ public static class EntityToValidate {
+ @Id
+ @GeneratedValue
+ private long id;
+
+ @NotNull
+ @Size(min = 1, max = 5)
+ private String name;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(final long i) {
+ id = i;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String n) {
+ name = n;
+ }
+ }
+
+ @Test
+ public void valid() {
+ persistManager.persistValid();
+ }
+
+ @Test
+ public void notValid() {
+ try {
+ persistManager.persistNotValid();
+ fail();
+ } catch (final EJBException ejbException) {
+ assertTrue(ejbException.getCause() instanceof ConstraintViolationException);
+ final ConstraintViolationException constraintViolationException = (ConstraintViolationException) ejbException.getCause();
+ assertEquals(1, constraintViolationException.getConstraintViolations().size());
+ }
+ }
+
+ @Test
+ public void lookupValidatorFactory() throws Exception {
+ final ValidatorFactory validatorFactory = (ValidatorFactory) new InitialContext().lookup("java:comp/ValidatorFactory");
+ assertNotNull(validatorFactory);
+ }
+
+ @Test
+ public void lookupValidator() throws Exception {
+ final Validator validator = (Validator) new InitialContext().lookup("java:comp/Validator");
+ assertNotNull(validator);
+ }
+
+ @Test
+ public void injectionValidatorFactory() {
+ final ValidatorFactory validatorFactory = persistManager.getValidatorFactory();
+ assertNotNull(validatorFactory);
+ }
+
+ @Test
+ public void injectionValidator() {
+ final Validator validator = persistManager.getValidator();
+ assertNotNull(validator);
+ }
+
+ @Test
+ public void injection2ValidatorFactory() {
+ assertNotNull(validatorFactory);
+ }
+
+ @Test
+ public void injection2Validator() {
+ assertNotNull(validator);
+ }
+}
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/PreDestroyTest.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/PreDestroyTest.java
new file mode 100644
index 0000000..b9c5966
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/PreDestroyTest.java
@@ -0,0 +1,78 @@
+/**
+ * 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.openejb.junit5;
+
+import org.apache.openejb.jee.SessionBean;
+import org.apache.openejb.jee.SingletonBean;
+import org.apache.openejb.junit5.RunWithApplicationComposer;
+import org.apache.openejb.testing.Module;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.ejb.Singleton;
+import javax.inject.Inject;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@RunWithApplicationComposer
+public class PreDestroyTest {
+
+ private static final AtomicBoolean isConstructed = new AtomicBoolean(false);
+ private static final AtomicBoolean isDestroyed = new AtomicBoolean(false);
+
+ @Inject
+ private TestMe testMe;
+
+ @AfterAll
+ public static void onAfterClass() {
+ assertTrue(isConstructed.get(), "onPostConstruct was not called");
+ assertTrue(isDestroyed.get(), "onPreDestroy was not called");
+ }
+
+ @Module
+ public SessionBean getEjbs() {
+ return new SingletonBean(TestMe.class);
+ }
+
+ @Test
+ public void testLifecycle() {
+ this.testMe.noOp();
+ }
+
+ @Singleton
+ public static class TestMe {
+
+ @PostConstruct
+ public void onPostConstruct() {
+ isConstructed.set(true);
+ }
+
+ @PreDestroy
+ public void onPreDestroy() {
+ isDestroyed.set(true);
+ }
+
+ public void noOp() {
+ //no-op
+ }
+
+ }
+
+}
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/RandomPortTest.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/RandomPortTest.java
new file mode 100644
index 0000000..4cb38a1
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/RandomPortTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.openejb.junit5;
+
+import org.apache.openejb.jee.EjbJar;
+import org.apache.openejb.junit5.RunWithApplicationComposer;
+import org.apache.openejb.testing.Module;
+import org.apache.openejb.testing.RandomPort;
+import org.junit.jupiter.api.Test;
+
+import java.net.URL;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@RunWithApplicationComposer
+public class RandomPortTest {
+ @Module
+ public EjbJar jar() {
+ return new EjbJar();
+ }
+
+ @RandomPort("httpejb")
+ private int port;
+
+ @RandomPort("httpejb")
+ private URL portUrl;
+
+ @Test
+ public void checkRandom() {
+ assertTrue(port > 0);
+ assertNotNull(portUrl);
+ assertEquals(port, portUrl.getPort());
+ }
+}
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/SingleAppComposerJVMTest.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/SingleAppComposerJVMTest.java
new file mode 100644
index 0000000..dc34b17
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/SingleAppComposerJVMTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.openejb.junit5;
+
+import org.apache.openejb.junit5.app.MyApp;
+import org.apache.openejb.loader.SystemInstance;
+import org.junit.jupiter.api.Test;
+import org.apache.openejb.testing.Application;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+//-Djunit.jupiter.testclass.order.default=org.apache.openejb.junit5.order.AppComposerTestClassOrderer -Dtomee.application-composer.application=org.apache.openejb.junit5.app.MyApp
+@RunWithApplicationComposer(mode = ExtensionMode.PER_JVM)
+public class SingleAppComposerJVMTest {
+
+ @Application
+ private MyApp app;
+
+ @Test
+ public void run() {
+ assertNotNull(app);
+ app.check();
+ assertEquals("Set-Via-SingleAppComposerTest-In-Same-JVM", SystemInstance.get().getProperty("key"));
+ }
+
+}
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/SingleAppComposerTest.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/SingleAppComposerTest.java
new file mode 100644
index 0000000..152f0c5
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/SingleAppComposerTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.junit5;
+
+import org.apache.openejb.junit5.app.MyApp;
+import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.testing.Application;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+//-Djunit.jupiter.testclass.order.default=org.apache.openejb.junit5.order.AppComposerTestClassOrderer -Dtomee.application-composer.application=org.apache.openejb.junit5.app.MyApp
+@RunWithApplicationComposer(mode = ExtensionMode.PER_JVM)
+public class SingleAppComposerTest {
+
+ @Application
+ private MyApp app;
+
+ @Test
+ public void run() {
+ assertNotNull(app);
+ app.check();
+ SystemInstance.get().setProperty("key", "Set-Via-SingleAppComposerTest-In-Same-JVM");
+ }
+}
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/WebAppEnvEntryTest.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/WebAppEnvEntryTest.java
new file mode 100644
index 0000000..355b42e
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/WebAppEnvEntryTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.junit5;
+
+import org.apache.openejb.jee.EnvEntry;
+import org.apache.openejb.jee.WebApp;
+import org.apache.openejb.junit5.RunWithApplicationComposer;
+import org.apache.openejb.testing.Classes;
+import org.apache.openejb.testing.Module;
+import org.junit.jupiter.api.Test;
+
+import javax.inject.Inject;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@RunWithApplicationComposer
+public class WebAppEnvEntryTest {
+ @Module
+ @Classes(cdi = true, value = {CdiBean.class})
+ public WebApp war() {
+ final WebApp webApp = new WebApp().contextRoot("/myapp");
+ webApp.getEnvEntry().add(new EnvEntry("foo", String.class.getName(), "bar"));
+ return webApp;
+ }
+
+ @Inject
+ private CdiBean bean;
+
+ @Test
+ public void test() {
+ assertEquals("bar", bean.lookup());
+ }
+
+ public static class CdiBean {
+ public String lookup() {
+ try {
+ return String.class.cast(new InitialContext().lookup("java:comp/env/foo"));
+ } catch (final NamingException e) {
+ return "-";
+ }
+ }
+ }
+}
+
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/WebappMultipleModuleTest.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/WebappMultipleModuleTest.java
new file mode 100644
index 0000000..f753b82
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/WebappMultipleModuleTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.junit5;
+
+import org.apache.openejb.jee.jpa.unit.Persistence;
+import org.apache.openejb.jee.jpa.unit.PersistenceUnit;
+import org.apache.openejb.testing.CdiExtensions;
+import org.apache.openejb.testing.Classes;
+import org.apache.openejb.testing.Module;
+import org.apache.openejb.testing.PersistenceRootUrl;
+import org.junit.jupiter.api.Test;
+
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.AfterDeploymentValidation;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.Extension;
+import javax.inject.Inject;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@RunWithApplicationComposer
+@CdiExtensions(WebappMultipleModuleTest.SuperViciousExtension.class)
+@Classes(cdi = true, innerClassesAsBean = true)
+public class WebappMultipleModuleTest {
+ @Module
+ @PersistenceRootUrl(value = "")
+ public Persistence jpa() throws Exception {
+ SuperViciousExtension.CALLED.set(false); // reset before container boot
+ return new Persistence(new PersistenceUnit("jpa"));
+ }
+
+ @Inject
+ private Marker bean;
+
+ @Test
+ public void run() {
+ assertNotNull(bean);
+ assertTrue(SuperViciousExtension.CALLED.get());
+ }
+
+ public static class Marker {}
+
+ public static class SuperViciousExtension implements Extension {
+ public static final AtomicBoolean CALLED = new AtomicBoolean();
+
+ private void end(@Observes final AfterDeploymentValidation ignored , final BeanManager manager) {
+ final Bean<?> bean = manager.resolve(manager.getBeans(Marker.class));
+ assertNotNull(manager.getReference(bean, Marker.class, manager.createCreationalContext(bean)));
+ CALLED.set(true);
+ }
+ }
+}
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/app/MyApp.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/app/MyApp.java
new file mode 100644
index 0000000..6ec52fd
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/app/MyApp.java
@@ -0,0 +1,40 @@
+/**
+ * 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.openejb.junit5.app;
+
+import org.apache.openejb.testing.Application;
+import org.apache.openejb.testing.Classes;
+
+import javax.inject.Inject;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@Application
+@Classes(cdi = true, innerClassesAsBean = true)
+public class MyApp {
+
+ @Inject
+ private MyBean ok;
+
+ public void check() {
+ assertNotNull(ok);
+ }
+
+ public static class MyBean {
+ }
+
+}
diff --git a/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/order/AppComposerTestClassOrderer.java b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/order/AppComposerTestClassOrderer.java
new file mode 100644
index 0000000..1c8ae91
--- /dev/null
+++ b/container/openejb-junit5/src/test/java/org/apache/openejb/junit5/order/AppComposerTestClassOrderer.java
@@ -0,0 +1,48 @@
+/*
+ * 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.openejb.junit5.order;
+
+import org.apache.openejb.junit5.SingleAppComposerJVMTest;
+import org.apache.openejb.junit5.SingleAppComposerTest;
+import org.junit.jupiter.api.ClassDescriptor;
+import org.junit.jupiter.api.ClassOrderer;
+import org.junit.jupiter.api.ClassOrdererContext;
+
+import java.util.Comparator;
+
+public class AppComposerTestClassOrderer implements ClassOrderer {
+
+ @Override
+ public void orderClasses(ClassOrdererContext classOrdererContext) {
+ classOrdererContext.getClassDescriptors().sort(Comparator.comparingInt(AppComposerTestClassOrderer::getOrder));
+ }
+
+ /*
+ * SingleAppComposerTest before SingleAppComposerJVMTest (same JVM test)
+ */
+ private static int getOrder(ClassDescriptor classDescriptor) {
+ if (classDescriptor.getTestClass().equals(SingleAppComposerTest.class)) {
+ return 2;
+ }
+
+ if (classDescriptor.getTestClass().equals(SingleAppComposerJVMTest.class)) {
+ return 3;
+ }
+
+ return 1;
+ }
+}
diff --git a/container/openejb-junit5/src/test/resources/custom-openejb.xml b/container/openejb-junit5/src/test/resources/custom-openejb.xml
new file mode 100644
index 0000000..9837b8c
--- /dev/null
+++ b/container/openejb-junit5/src/test/resources/custom-openejb.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<openejb>
+ <Resource id="app-composer" type="DataSource" />
+</openejb>
\ No newline at end of file
diff --git a/container/pom.xml b/container/pom.xml
index 9508572..e6197af 100644
--- a/container/pom.xml
+++ b/container/pom.xml
@@ -38,6 +38,7 @@
<module>openejb-jee-accessors</module>
<module>openejb-jpa-integration</module>
<module>openejb-junit</module>
+ <module>openejb-junit5</module>
<module>openejb-junit5-backward</module>
</modules>
</project>
diff --git a/examples/junit5-application-composer/README.adoc b/examples/junit5-application-composer/README.adoc
new file mode 100644
index 0000000..940a873
--- /dev/null
+++ b/examples/junit5-application-composer/README.adoc
@@ -0,0 +1,131 @@
+= JUnit 5: Application Composer
+:index-group: Testing Techniques
+:jbake-type: page
+:jbake-status: published
+
+The `org.apache.openejb.junit5.ApplicationComposerExtension` is a **JUnit 5 extension** internally re-using `ApplicationComposers`, which has been used in the JUnit test runner ( `ApplicationComposer`) for testing OpenEJB in recent years. It involves no classpath scanning at all. If you want something to be in the app, you must build it directly in your testcase.
+
+With the `RunWithApplicationComposer` you can do identical testing that OpenEJB uses internally, but with limited dependency on OpenEJB itself.
+The main dependency is:
+
+[source,xml]
+----
+<dependency>
+ <groupId>org.apache.tomee</groupId>
+ <artifactId>openejb-junit5</artifactId>
+ <version>${openejb.version}</version>
+</dependency>
+----
+
+Make sure to use a maven surefire version greater or equal to `3.0.0-M5` and add the required dependencies for JUnit 5.
+
+== Composing an Application
+
+The main difference to the embedded `EJBContainer` API is building the application in the test code. This is done with one or more methods in the test case annotated
+with `org.apache.openejb.testing.Module` using the following format:
+
+[source,java]
+----
+@Module
+public <return-value> <module-name>() {
+----
+
+Where **module-name** is the name you wish to use for that module and **return-value** can be any one of the following:
+
+ - java.lang.Class
+ - java.lang.Class[]
+ - org.apache.openejb.jee.EjbJar
+ - org.apache.openejb.jee.EnterpriseBean
+ - org.apache.openejb.jee.Application
+ - org.apache.openejb.jee.Connector
+ - org.apache.openejb.jee.Beans
+ - org.apache.openejb.jee.jpa.unit.Persistence
+ - org.apache.openejb.jee.jpa.unit.PersistenceUnit
+
+== Example
+
+Used in an actual testcase, that might look like so:
+
+[source,java,numbered]
+----
+import org.apache.openejb.jee.EjbJar;
+import org.apache.openejb.jee.StatefulBean;
+import org.apache.openejb.jee.jpa.unit.PersistenceUnit;
+import org.apache.openejb.junit5.RunWithApplicationComposer;
+import org.apache.openejb.testing.Configuration;
+import org.apache.openejb.testing.Module;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+import javax.ejb.EJB;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.transaction.UserTransaction;
+import java.util.List;
+import java.util.Properties;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@RunWithApplicationComposer
+public class MoviesTest {
+
+ @EJB
+ private Movies movies;
+
+ @Resource
+ private UserTransaction userTransaction;
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ @Module
+ public PersistenceUnit persistence() {
+ PersistenceUnit unit = new PersistenceUnit("movie-unit");
+ unit.setJtaDataSource("movieDatabase");
+ unit.setNonJtaDataSource("movieDatabaseUnmanaged");
+ unit.getClazz().add(Movie.class.getName());
+ unit.setProperty("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)");
+ return unit;
+ }
+
+ @Module
+ public EjbJar beans() {
+ EjbJar ejbJar = new EjbJar("movie-beans");
+ ejbJar.addEnterpriseBean(new StatefulBean(MoviesImpl.class));
+ return ejbJar;
+ }
+
+ @Configuration
+ public Properties config() throws Exception {
+ Properties p = new Properties();
+ p.put("movieDatabase", "new://Resource?type=DataSource");
+ p.put("movieDatabase.JdbcDriver", "org.hsqldb.jdbcDriver");
+ p.put("movieDatabase.JdbcUrl", "jdbc:hsqldb:mem:moviedb");
+ return p;
+ }
+
+ @Test
+ public void test() throws Exception {
+
+ userTransaction.begin();
+
+ try {
+ entityManager.persist(new Movie("Quentin Tarantino", "Reservoir Dogs", 1992));
+ entityManager.persist(new Movie("Joel Coen", "Fargo", 1996));
+ entityManager.persist(new Movie("Joel Coen", "The Big Lebowski", 1998));
+
+ List<Movie> list = movies.getMovies();
+ assertEquals(3, list.size(), "List.size()");
+
+ for (Movie movie : list) {
+ movies.deleteMovie(movie);
+ }
+
+ assertEquals(0, movies.getMovies().size(), "Movies.getMovies()");
+
+ } finally {
+ userTransaction.commit();
+ }
+ }
+}
+----
diff --git a/examples/junit5-application-composer/pom.xml b/examples/junit5-application-composer/pom.xml
new file mode 100644
index 0000000..aff07d9
--- /dev/null
+++ b/examples/junit5-application-composer/pom.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Rev: 636494 $ $Date: 2008-03-12 21:24:02 +0100 (Wed, 12 Mar 2008) $ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.superbiz</groupId>
+ <artifactId>junit5-application-composer</artifactId>
+ <packaging>jar</packaging>
+ <version>8.0.7-SNAPSHOT</version>
+ <name>TomEE :: Examples :: JUnit 5 :: Application Composer</name>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+ <build>
+ <defaultGoal>install</defaultGoal>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.5.1</version>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>3.0.0-M5</version>
+ </plugin>
+ <plugin>
+ <groupId>org.tomitribe.transformer</groupId>
+ <artifactId>org.eclipse.transformer.maven</artifactId>
+ <version>0.1.1a</version>
+ <configuration>
+ <classifier>jakartaee9</classifier>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <phase>package</phase>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <repositories>
+ <repository>
+ <id>apache-m2-snapshot</id>
+ <name>Apache Snapshot Repository</name>
+ <url>https://repository.apache.org/content/groups/snapshots</url>
+ </repository>
+ </repositories>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.tomee</groupId>
+ <artifactId>javaee-api</artifactId>
+ <version>[8.0,)</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <version>5.8.0-M1</version>
+ <scope>test</scope>
+ </dependency>
+ <!--
+ The <scope>test</scope> guarantees that non of your runtime
+ code is dependent on any OpenEJB classes.
+ -->
+ <dependency>
+ <groupId>org.apache.tomee</groupId>
+ <artifactId>openejb-core</artifactId>
+ <version>8.0.7-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tomee</groupId>
+ <artifactId>openejb-junit5</artifactId>
+ <version>8.0.7-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <!--
+ This section allows you to configure where to publish libraries for sharing.
+ It is not required and may be deleted. For more information see:
+ http://maven.apache.org/plugins/maven-deploy-plugin/
+ -->
+ <distributionManagement>
+ <repository>
+ <id>localhost</id>
+ <url>file://${basedir}/target/repo/</url>
+ </repository>
+ <snapshotRepository>
+ <id>localhost</id>
+ <url>file://${basedir}/target/snapshot-repo/</url>
+ </snapshotRepository>
+ </distributionManagement>
+</project>
diff --git a/examples/junit5-application-composer/src/main/java/org/superbiz/composed/Movie.java b/examples/junit5-application-composer/src/main/java/org/superbiz/composed/Movie.java
new file mode 100644
index 0000000..b6bd4e8
--- /dev/null
+++ b/examples/junit5-application-composer/src/main/java/org/superbiz/composed/Movie.java
@@ -0,0 +1,61 @@
+/**
+ * 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.superbiz.composed;
+
+import javax.persistence.Entity;
+
+@Entity
+public class Movie {
+
+ private String director;
+ private String title;
+ private int year;
+
+ public Movie() {
+ }
+
+ public Movie(String director, String title, int year) {
+ this.director = director;
+ this.title = title;
+ this.year = year;
+ }
+
+ public String getDirector() {
+ return director;
+ }
+
+ public void setDirector(String director) {
+ this.director = director;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public int getYear() {
+ return year;
+ }
+
+ public void setYear(int year) {
+ this.year = year;
+ }
+
+}
diff --git a/examples/junit5-application-composer/src/main/java/org/superbiz/composed/Movies.java b/examples/junit5-application-composer/src/main/java/org/superbiz/composed/Movies.java
new file mode 100644
index 0000000..456aa75
--- /dev/null
+++ b/examples/junit5-application-composer/src/main/java/org/superbiz/composed/Movies.java
@@ -0,0 +1,31 @@
+/**
+ * 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.superbiz.composed;
+
+import java.util.List;
+
+/**
+ * @version $Revision: 607077 $ $Date: 2007-12-27 06:55:23 -0800 (Thu, 27 Dec 2007) $
+ */
+public interface Movies {
+
+ void addMovie(Movie movie) throws Exception;
+
+ void deleteMovie(Movie movie) throws Exception;
+
+ List<Movie> getMovies() throws Exception;
+}
diff --git a/examples/junit5-application-composer/src/main/java/org/superbiz/composed/MoviesImpl.java b/examples/junit5-application-composer/src/main/java/org/superbiz/composed/MoviesImpl.java
new file mode 100644
index 0000000..4e126fb
--- /dev/null
+++ b/examples/junit5-application-composer/src/main/java/org/superbiz/composed/MoviesImpl.java
@@ -0,0 +1,50 @@
+/**
+ * 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.superbiz.composed;
+
+import javax.ejb.Stateful;
+import javax.ejb.TransactionAttribute;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.PersistenceContextType;
+import javax.persistence.Query;
+import java.util.List;
+
+import static javax.ejb.TransactionAttributeType.MANDATORY;
+
+//START SNIPPET: code
+@Stateful(name = "Movies")
+@TransactionAttribute(MANDATORY)
+public class MoviesImpl implements Movies {
+
+ @PersistenceContext(unitName = "movie-unit", type = PersistenceContextType.TRANSACTION)
+ private EntityManager entityManager;
+
+ public void addMovie(Movie movie) throws Exception {
+ entityManager.persist(movie);
+ }
+
+ public void deleteMovie(Movie movie) throws Exception {
+ entityManager.remove(movie);
+ }
+
+ public List<Movie> getMovies() throws Exception {
+ Query query = entityManager.createQuery("SELECT m from Movie as m");
+ return query.getResultList();
+ }
+}
+//END SNIPPET: code
diff --git a/examples/junit5-application-composer/src/test/java/org/superbiz/composed/MoviesTest.java b/examples/junit5-application-composer/src/test/java/org/superbiz/composed/MoviesTest.java
new file mode 100644
index 0000000..568873c
--- /dev/null
+++ b/examples/junit5-application-composer/src/test/java/org/superbiz/composed/MoviesTest.java
@@ -0,0 +1,100 @@
+/**
+ * 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.superbiz.composed;
+
+import org.apache.openejb.jee.EjbJar;
+import org.apache.openejb.jee.StatefulBean;
+import org.apache.openejb.jee.jpa.unit.PersistenceUnit;
+import org.apache.openejb.junit5.RunWithApplicationComposer;
+import org.apache.openejb.testing.Configuration;
+import org.apache.openejb.testing.Module;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+import javax.ejb.EJB;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.transaction.UserTransaction;
+import java.util.List;
+import java.util.Properties;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+//START SNIPPET: code
+@RunWithApplicationComposer
+public class MoviesTest {
+
+ @EJB
+ private Movies movies;
+
+ @Resource
+ private UserTransaction userTransaction;
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ @Module
+ public PersistenceUnit persistence() {
+ PersistenceUnit unit = new PersistenceUnit("movie-unit");
+ unit.setJtaDataSource("movieDatabase");
+ unit.setNonJtaDataSource("movieDatabaseUnmanaged");
+ unit.getClazz().add(Movie.class.getName());
+ unit.setProperty("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)");
+ return unit;
+ }
+
+ @Module
+ public EjbJar beans() {
+ EjbJar ejbJar = new EjbJar("movie-beans");
+ ejbJar.addEnterpriseBean(new StatefulBean(MoviesImpl.class));
+ return ejbJar;
+ }
+
+ @Configuration
+ public Properties config() throws Exception {
+ Properties p = new Properties();
+ p.put("movieDatabase", "new://Resource?type=DataSource");
+ p.put("movieDatabase.JdbcDriver", "org.hsqldb.jdbcDriver");
+ p.put("movieDatabase.JdbcUrl", "jdbc:hsqldb:mem:moviedb");
+ return p;
+ }
+
+ @Test
+ public void test() throws Exception {
+
+ userTransaction.begin();
+
+ try {
+ entityManager.persist(new Movie("Quentin Tarantino", "Reservoir Dogs", 1992));
+ entityManager.persist(new Movie("Joel Coen", "Fargo", 1996));
+ entityManager.persist(new Movie("Joel Coen", "The Big Lebowski", 1998));
+
+ List<Movie> list = movies.getMovies();
+ assertEquals(3, list.size(), "List.size()");
+
+ for (Movie movie : list) {
+ movies.deleteMovie(movie);
+ }
+
+ assertEquals(0, movies.getMovies().size(), "Movies.getMovies()");
+
+ } finally {
+ userTransaction.commit();
+ }
+ }
+}
+//END SNIPPET: code
diff --git a/examples/pom.xml b/examples/pom.xml
index 88cebda..46b75e0 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -96,6 +96,7 @@
<module>jpa-enumerated</module>
<module>jsf-managedBean-and-ejb</module>
<module>jsf-cdi-and-ejb</module>
+ <module>junit5-application-composer</module>
<module>lookup-of-ejbs</module>
<module>lookup-of-ejbs-with-descriptor</module>
<!-- Does not work with Java 9+ because of the modules. Needs more work
diff --git a/pom.xml b/pom.xml
index f875209..5c84bec 100644
--- a/pom.xml
+++ b/pom.xml
@@ -97,8 +97,7 @@
<tomee.version>${project.version}</tomee.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
- <surefire.version>2.21.0</surefire.version>
- <surefire.junit5.version>3.0.0-M5</surefire.junit5.version>
+ <surefire.version>3.0.0-M5</surefire.version>
<!-- for the default name of the module. Needs to be overridden -->
<tomee.build.name>${project.groupId}.${project.artifactId}</tomee.build.name>
@@ -116,7 +115,7 @@
<geronimo-javamail_1.6_mail.version>1.0.0</geronimo-javamail_1.6_mail.version>
<openjpa.version>3.1.2</openjpa.version>
- <openwebbeans.version>2.0.22-SNAPSHOT</openwebbeans.version>
+ <openwebbeans.version>2.0.22</openwebbeans.version>
<jcs.version>2.1</jcs.version>
<johnzon.version>1.2.10</johnzon.version>
<quartz-openejb-shade.version>2.2.4</quartz-openejb-shade.version>
@@ -191,7 +190,7 @@
<org.apache.activemq.version>5.16.1</org.apache.activemq.version>
<org.springframework.version>3.1.4.RELEASE</org.springframework.version>
<junit.version>4.13.1</junit.version>
- <junit.jupiter.version>5.7.0</junit.jupiter.version>
+ <junit.jupiter.version>5.8.0-M1</junit.jupiter.version>
<org.apache.axis2.version>1.4.1</org.apache.axis2.version>
<scannotation.version>1.0.2</scannotation.version>
<geronimo.connector.version>3.1.4</geronimo.connector.version>
@@ -1102,6 +1101,24 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <version>${junit.jupiter.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <version>${junit.jupiter.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.vintage</groupId>
+ <artifactId>junit-vintage-engine</artifactId>
+ <version>${junit.jupiter.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>com.agical.rmock</groupId>
<artifactId>rmock</artifactId>
<version>2.0.2</version>
diff --git a/tomee/tomee-embedded/pom.xml b/tomee/tomee-embedded/pom.xml
index 1650151..7558398 100644
--- a/tomee/tomee-embedded/pom.xml
+++ b/tomee/tomee-embedded/pom.xml
@@ -65,6 +65,7 @@
<artifactSet>
<excludes>
<exclude>junit:junit</exclude>
+ <exclude>org.junit.jupiter:junit-jupiter-api</exclude>
<exclude>*:jaxb-api</exclude>
<exclude>*:jaxb-impl</exclude>
</excludes>
@@ -81,7 +82,7 @@
<shadedClassifierName>jaxrs</shadedClassifierName>
<artifactSet>
<excludes>
- <exclude>junit:junit</exclude>
+ <exclude>org.junit.jupiter:junit-jupiter-api</exclude>
<exclude>*:jaxb-api</exclude>
<exclude>*:jaxb-impl</exclude>
<exclude>org.apache.activemq:*</exclude>
@@ -132,6 +133,7 @@
<artifactSet>
<excludes>
<exclude>junit:junit</exclude>
+ <exclude>org.junit.jupiter:junit-jupiter-api</exclude>
<exclude>*:jaxb-api</exclude>
<exclude>*:jaxb-impl</exclude>
<exclude>org.apache.activemq:*</exclude>
@@ -210,7 +212,9 @@
<configuration>
<excludes>
<exclude>**/SingleInstanceRunnerTest*</exclude>
+ <exclude>**/SingleInstanceRunnerExtensionTest*</exclude>
<exclude>**/NoScannerSingleRunnerTest*</exclude>
+ <exclude>**/NoScannerSingleRunnerExtensionTest*</exclude>
</excludes>
</configuration>
</execution>
@@ -423,6 +427,22 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <!-- Required for running JUnit 4 via JUnit 5 environment -->
+ <dependency>
+ <groupId>org.junit.vintage</groupId>
+ <artifactId>junit-vintage-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<type>jar</type>
@@ -480,5 +500,6 @@
<version>1.60</version>
<scope>test</scope>
</dependency>
+
</dependencies>
</project>
diff --git a/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/TomEEEmbeddedBase.java b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/TomEEEmbeddedBase.java
new file mode 100644
index 0000000..48dcf47
--- /dev/null
+++ b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/TomEEEmbeddedBase.java
@@ -0,0 +1,56 @@
+/*
+ * 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.junit;
+
+import org.apache.tomee.embedded.TomEEEmbeddedApplicationRunner;
+
+import javax.enterprise.inject.Vetoed;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+
+@Vetoed
+public class TomEEEmbeddedBase {
+ private static final AtomicReference<TomEEEmbeddedApplicationRunner> RUNNER = new AtomicReference<>();
+
+ public void start(final Object marker) throws Exception {
+ getRunner().start(marker.getClass(), (Properties) null);
+ }
+
+ public void close() {
+ final TomEEEmbeddedApplicationRunner runner = RUNNER.get();
+ if (runner != null) {
+ runner.close();
+ }
+ }
+
+ public void setApp(final Object o) {
+ getRunner().setApp(o);
+ }
+
+ public void composerInject(final Object target) throws IllegalAccessException {
+ getRunner().composerInject(target);
+ }
+
+ private TomEEEmbeddedApplicationRunner getRunner() {
+ final TomEEEmbeddedApplicationRunner runner = RUNNER.get();
+ if (runner == null) {
+ RUNNER.compareAndSet(null, new TomEEEmbeddedApplicationRunner());
+ }
+ return RUNNER.get();
+ }
+
+}
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 6151a12..9b2864f 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,7 +16,6 @@
*/
package org.apache.tomee.embedded.junit;
-import org.apache.tomee.embedded.TomEEEmbeddedApplicationRunner;
import org.junit.rules.MethodRule;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
@@ -28,31 +27,29 @@
import javax.enterprise.inject.Vetoed;
import java.util.List;
-import java.util.Properties;
-import java.util.concurrent.atomic.AtomicReference;
/**
* 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.
- *
+ * <p>
* Base is to declare an @Application class which holds the model and some injections.
* Note: this can be replaced setting tomee.application-composer.application property to the fully qualified name of the app.
* Note: @Application classes are only searched in the same jar as the test.
- *
+ * <p>
* Model:
* - @Configuration: programmatic properties - note injections don't work there.
* - @Classes: only context value is used.
* - @ContainerProperties: to configure the container
* - @WebResource: first value can be used to set the docBase (other values are ignored)
* - @TomEEEmbeddedSingleRunner.LifecycleTasks: allow to add some lifecycle tasks (like starting a ftp/sft/elasticsearch... server)
- *
+ * <p>
* Injections:
* - CDI
* - @RandomPort: with the value http or https. Supported types are URL (context base) and int (the port).
*/
@Vetoed
public class TomEEEmbeddedSingleRunner extends BlockJUnit4ClassRunner {
- private static final AtomicReference<TomEEEmbeddedApplicationRunner> RUNNER = new AtomicReference<>();
+ private static final TomEEEmbeddedBase BASE = new TomEEEmbeddedBase();
// use when you use another runner like Parameterized of JUnit
public static class Rule implements TestRule {
@@ -67,8 +64,8 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
- start(test);
- RUNNER.get().composerInject(test);
+ BASE.start(test);
+ BASE.composerInject(test);
base.evaluate();
}
};
@@ -78,31 +75,17 @@
public static class Start extends RunListener {
@Override
public void testStarted(final Description description) throws Exception {
- start(null);
+ BASE.start(null);
}
}
- private static void start(final Object marker) throws Exception {
- getRunner().start(marker.getClass(), (Properties) null);
- }
-
public static void setApp(final Object o) {
- getRunner().setApp(o);
+ BASE.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 TomEEEmbeddedApplicationRunner runner = RUNNER.get();
- if (runner != null) {
- runner.close();
- }
+ BASE.close();
}
public TomEEEmbeddedSingleRunner(final Class<?> klass) throws InitializationError {
@@ -118,8 +101,8 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
- start(test);
- getRunner().composerInject(target);
+ BASE.start(test);
+ BASE.composerInject(target);
base.evaluate();
}
};
diff --git a/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/jupiter/RunWithTomEEEmbedded.java b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/jupiter/RunWithTomEEEmbedded.java
new file mode 100644
index 0000000..79bf105
--- /dev/null
+++ b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/jupiter/RunWithTomEEEmbedded.java
@@ -0,0 +1,32 @@
+/*
+ * 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.junit.jupiter;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@ExtendWith(org.apache.tomee.embedded.junit.jupiter.TomEEEmbeddedExtension.class)
+public @interface RunWithTomEEEmbedded {
+
+}
diff --git a/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/jupiter/TomEEEmbeddedExtension.java b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/jupiter/TomEEEmbeddedExtension.java
new file mode 100644
index 0000000..106555f
--- /dev/null
+++ b/tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/junit/jupiter/TomEEEmbeddedExtension.java
@@ -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.tomee.embedded.junit.jupiter;
+
+import org.apache.openejb.OpenEJBRuntimeException;
+import org.apache.tomee.embedded.junit.TomEEEmbeddedBase;
+import org.junit.jupiter.api.TestInstance;
+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.ExtensionContext;
+import org.junit.jupiter.api.extension.TestInstances;
+
+import java.util.List;
+
+public class TomEEEmbeddedExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback {
+
+ private static final TomEEEmbeddedBase BASE = new TomEEEmbeddedBase();
+
+ @Override
+ public void afterAll(ExtensionContext context) {
+ if (isPerClass(context)) {
+ BASE.close();
+ }
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+
+ if (isPerClass(context)) {
+ BASE.start(context.getRequiredTestInstance());
+ doInject(context);
+ }
+ }
+
+ @Override
+ public void afterEach(ExtensionContext context) throws Exception {
+ if (!isPerClass(context)) {
+ BASE.close();
+ }
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ if (!isPerClass(context)) {
+ BASE.start(context.getRequiredTestInstance());
+ doInject(context);
+ }
+ }
+
+ private void doInject(final ExtensionContext extensionContext) {
+ TestInstances oTestInstances = extensionContext.getTestInstances()
+ .orElseThrow(() -> new OpenEJBRuntimeException("No test instances available for the given extension context."));
+
+ List<Object> testInstances = oTestInstances.getAllInstances();
+
+ testInstances.forEach(t -> {
+ try {
+ BASE.composerInject(t);
+ } catch (Exception e) {
+ throw new OpenEJBRuntimeException(e);
+ }
+ });
+ }
+
+ boolean isPerClass(final ExtensionContext context) {
+ return context.getTestInstanceLifecycle()
+ .map(it -> it.equals(TestInstance.Lifecycle.PER_CLASS))
+ .orElse(false);
+ }
+}
diff --git a/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/NoScannerSingleRunnerExtensionTest.java b/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/NoScannerSingleRunnerExtensionTest.java
new file mode 100644
index 0000000..129dd1f
--- /dev/null
+++ b/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/NoScannerSingleRunnerExtensionTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.embedded;
+
+import org.apache.openejb.testing.Application;
+import org.apache.openejb.testing.Classes;
+import org.apache.tomee.embedded.junit.jupiter.RunWithTomEEEmbedded;
+import org.apache.tomee.embedded.junit.jupiter.TomEEEmbeddedExtension;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+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
+//-Dtomee.application-composer.application=org.apache.tomee.embedded.NoScannerSingleRunnerExtensionTest$ScanApp
+@RunWithTomEEEmbedded
+public class NoScannerSingleRunnerExtensionTest {
+ @Application // app can have several injections/helpers
+ private ScanApp app;
+
+ @Test
+ public void run() {
+ assertNotNull(app);
+ 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 {
+ }
+}
diff --git a/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/SingleInstanceRunnerExtensionTest.java b/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/SingleInstanceRunnerExtensionTest.java
new file mode 100644
index 0000000..00445be
--- /dev/null
+++ b/tomee/tomee-embedded/src/test/java/org/apache/tomee/embedded/SingleInstanceRunnerExtensionTest.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.embedded;
+
+import org.apache.openejb.assembler.classic.Assembler;
+import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.observer.Observes;
+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.testng.PropertiesBuilder;
+import org.apache.tomee.embedded.event.TomEEEmbeddedApplicationRunnerInjection;
+import org.apache.tomee.embedded.junit.jupiter.RunWithTomEEEmbedded;
+import org.apache.tomee.embedded.junit.jupiter.TomEEEmbeddedExtension;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Properties;
+
+import static org.junit.Assert.*;
+
+// 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
+//-Dtomee.application-composer.application=org.apache.tomee.embedded.SingleInstanceRunnerExtensionTest$TheApp
+@RunWithTomEEEmbedded
+public class SingleInstanceRunnerExtensionTest {
+ @Application // app can have several injections/helpers
+ private TheApp app;
+
+ @RandomPort("http") // @RandomPort are propagated by value + type only (both need to match ATM)
+ private int port;
+
+ @Test
+ public void run() {
+ assertNotNull(SystemInstance.get().getComponent(Assembler.class));
+ assertEquals("val", SystemInstance.get().getProperty("simple"));
+ assertEquals("set", SystemInstance.get().getProperty("t"));
+ assertEquals("p", SystemInstance.get().getProperty("prog"));
+ assertEquals("128463", SystemInstance.get().getProperty("my.server.port"));
+ assertEquals("true", SystemInstance.get().getProperty("configurer"));
+ assertNotEquals(8080, app.port);
+ assertTrue(app.base.toExternalForm().endsWith("/app"));
+ assertEquals(app.port, port);
+ assertNotNull(app.task);
+ assertNotNull(app.tasks);
+ assertEquals(1, app.tasks.size());
+ assertEquals(app.task, app.tasks.iterator().next());
+ assertEquals(app.task, MyTask.instance);
+ assertNotNull(app.custom);
+ }
+
+ @Application
+ @Classes(context = "app")
+ @ContainerProperties({
+ @ContainerProperties.Property(name = "simple", value = "val"),
+ @ContainerProperties.Property(name = "tomee.embedded.application.runner.properties.t", value = "${t.value}"),
+ @ContainerProperties.Property(name = "tomee.embedded.application.runner.t.value", value = "set")
+ })
+ @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;
+
+ @RandomPort("http")
+ private URL base;
+
+ @TomEEEmbeddedApplicationRunner.LifecycleTask
+ private MyTask task;
+
+ @TomEEEmbeddedApplicationRunner.LifecycleTask
+ private Collection<LifecycleTask> tasks;
+
+ @org.apache.openejb.testing.Configuration
+ public Properties add() {
+ return new PropertiesBuilder().p("prog", "p").build();
+ }
+
+ private Custom custom;
+
+ public void doInject(@Observes final TomEEEmbeddedApplicationRunnerInjection injector) {
+ injector.inject(Custom.class, new Custom())
+ .inject(NotHere.class, new NotHere());
+ }
+ }
+
+ public static class NotHere {
+ }
+
+ public static class Custom {
+ }
+
+ public static class MyTask implements LifecycleTask {
+ private static MyTask instance;
+
+ @Override
+ public Closeable beforeContainerStartup() {
+ instance = this;
+ System.out.println(">>> start");
+ System.setProperty("my.server.port", "128463");
+ return new Closeable() {
+ @Override
+ public void close() throws IOException {
+ System.out.println(">>> close");
+ }
+ };
+ }
+ }
+
+ public static class SetMyProperty implements TomEEEmbeddedApplicationRunner.Configurer {
+ @Override
+ public void configure(final Configuration configuration) {
+ configuration.property("configurer", "true");
+ }
+ }
+}