You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by rm...@apache.org on 2020/03/17 16:17:25 UTC

[openjpa] branch master updated: OPENJPA-2809 adding openjpa-junit5 module

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

rmannibucau pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openjpa.git


The following commit(s) were added to refs/heads/master by this push:
     new ae9b2a9  OPENJPA-2809 adding openjpa-junit5 module
ae9b2a9 is described below

commit ae9b2a904bc5611257e2db888d3c61c7235be19f
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Tue Mar 17 17:17:19 2020 +0100

    OPENJPA-2809 adding openjpa-junit5 module
---
 openjpa-examples/image-gallery/pom.xml             |   2 -
 openjpa-examples/openbooks/pom.xml                 |   1 -
 openjpa-examples/pom.xml                           |   3 +-
 openjpa-examples/simple/pom.xml                    |   1 -
 openjpa-features/src/main/feature/feature.xml      |   6 +-
 openjpa-integration/daytrader/pom.xml              |   2 -
 openjpa-integration/examples/pom.xml               |   3 -
 openjpa-integration/jmx/pom.xml                    |   2 -
 openjpa-integration/slf4j/pom.xml                  |   2 -
 openjpa-integration/tck/pom.xml                    |   1 -
 openjpa-integration/validation/pom.xml             |   1 -
 {openjpa-tools => openjpa-junit5}/pom.xml          |  54 ++--
 .../org/apache/openjpa/junit5/OpenJPASupport.java  |  57 ++++
 .../openjpa/junit5/internal/OpenJPAExtension.java  | 346 +++++++++++++++++++++
 .../java/org/apache/openjpa/junit5/MyEntity.java   |  28 ++
 .../apache/openjpa/junit5/OpenJPASupportTest.java  |  32 ++
 openjpa-kernel/pom.xml                             |   2 +-
 openjpa-project/checkstyle.xml                     |  20 +-
 openjpa-tools/openjpa-fetch-statistics-was/pom.xml |   1 -
 openjpa-tools/openjpa-fetch-statistics/pom.xml     |   1 -
 openjpa-tools/openjpa-maven-plugin/pom.xml         |   1 -
 openjpa-tools/pom.xml                              |   4 -
 pom.xml                                            |  41 ++-
 23 files changed, 543 insertions(+), 68 deletions(-)

diff --git a/openjpa-examples/image-gallery/pom.xml b/openjpa-examples/image-gallery/pom.xml
index bff6ae3..8bfc302 100644
--- a/openjpa-examples/image-gallery/pom.xml
+++ b/openjpa-examples/image-gallery/pom.xml
@@ -38,8 +38,6 @@
   <description>Apache OpenJPA Examples - image-gallery</description>
 
   <properties>
-    <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
-    <checkstyle.suppressions.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}suppressions.xml</checkstyle.suppressions.location>
     <test.jvm.maxpermsize>256m</test.jvm.maxpermsize>
     <test.jvm.maxheapsize>1024m</test.jvm.maxheapsize>
     <test.jvm.arguments>-Xmx${test.jvm.maxheapsize}</test.jvm.arguments>
diff --git a/openjpa-examples/openbooks/pom.xml b/openjpa-examples/openbooks/pom.xml
index b33c758..2a00c1e 100644
--- a/openjpa-examples/openbooks/pom.xml
+++ b/openjpa-examples/openbooks/pom.xml
@@ -40,7 +40,6 @@
     <description>Apache OpenJPA Examples - OpenBooks</description>
 
     <properties>
-        <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
         <checkstyle.suppressions.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}suppressions.xml</checkstyle.suppressions.location>
         <automatic-module-name>org.apache.openjpa.examples.openbooks</automatic-module-name>
     </properties>
diff --git a/openjpa-examples/pom.xml b/openjpa-examples/pom.xml
index 96ed800..b7d5289 100644
--- a/openjpa-examples/pom.xml
+++ b/openjpa-examples/pom.xml
@@ -39,8 +39,7 @@
 
     <properties>
         <openjpa.Log>DefaultLevel=WARN</openjpa.Log>
-        <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
-        <checkstyle.suppressions.location>${project.basedir}${file.separator}..${file.separator}openjpa-project${file.separator}suppressions.xml</checkstyle.suppressions.location>
+        <checkstyle.suppressions.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}suppressions.xml</checkstyle.suppressions.location>
     </properties>
 
     <modules>
diff --git a/openjpa-examples/simple/pom.xml b/openjpa-examples/simple/pom.xml
index 7e9c7df..661d26a 100644
--- a/openjpa-examples/simple/pom.xml
+++ b/openjpa-examples/simple/pom.xml
@@ -47,7 +47,6 @@
     </dependencies>
 
     <properties>
-        <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
         <checkstyle.suppressions.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}suppressions.xml</checkstyle.suppressions.location>
         <automatic-module-name>org.apache.openjpa.examples.simple</automatic-module-name>
     </properties>
diff --git a/openjpa-features/src/main/feature/feature.xml b/openjpa-features/src/main/feature/feature.xml
index 4737feb..63b39c0 100644
--- a/openjpa-features/src/main/feature/feature.xml
+++ b/openjpa-features/src/main/feature/feature.xml
@@ -25,10 +25,10 @@
         <bundle dependency="true">mvn:org.apache.geronimo.specs/geronimo-annotation_1.0_spec/1.1.1</bundle>
         <bundle dependency="true">mvn:org.apache.geronimo.specs/geronimo-el_1.0_spec/1.0.1</bundle>
         <bundle dependency="true">mvn:org.apache.commons/commons-pool2/2.6.0</bundle>
-        <bundle dependency="true">mvn:org.apache.commons/commons-dbcp2/2.6.0</bundle>
-        <bundle dependency="true">mvn:org.apache.commons/commons-collections4/4.3</bundle>
+        <bundle dependency="true">mvn:org.apache.commons/commons-dbcp2/2.7.0</bundle>
+        <bundle dependency="true">mvn:org.apache.commons/commons-collections4/4.4</bundle>
         <bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.serp/1.14.1_1</bundle>
-        <bundle dependency="true">mvn:org.apache.xbean/xbean-asm7-shaded/4.12</bundle>
+        <bundle dependency="true">mvn:org.apache.xbean/xbean-asm7-shaded/${xbean.version}</bundle>
         <bundle>mvn:org.apache.openjpa/openjpa/${project.version}</bundle>
         <capability>
             osgi.service;objectClass=javax.persistence.spi.PersistenceProvider;effective:=active;javax.persistence.provider=org.apache.openjpa.persistence.PersistenceProviderImpl
diff --git a/openjpa-integration/daytrader/pom.xml b/openjpa-integration/daytrader/pom.xml
index ee08f23..52403bb 100644
--- a/openjpa-integration/daytrader/pom.xml
+++ b/openjpa-integration/daytrader/pom.xml
@@ -36,8 +36,6 @@
     <description>OpenJPA Integration Tests - Daytrader</description>
 
     <properties>
-        <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
-        <checkstyle.suppressions.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}suppressions.xml</checkstyle.suppressions.location>
         <daytrader.version>2.2-SNAPSHOT</daytrader.version>
         <dbcp.maxTotal>10</dbcp.maxTotal>
         <dbcp.maxIdle>5</dbcp.maxIdle>
diff --git a/openjpa-integration/examples/pom.xml b/openjpa-integration/examples/pom.xml
index 4be96f6..9723b89 100644
--- a/openjpa-integration/examples/pom.xml
+++ b/openjpa-integration/examples/pom.xml
@@ -45,9 +45,6 @@
     <name>OpenJPA Integration Tests - Examples</name>
     <description>OpenJPA Integration Tests - Examples</description>
 
-    <properties>
-        <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
-    </properties>
     <profiles>
     <profile>
     <id>examples-profile</id>
diff --git a/openjpa-integration/jmx/pom.xml b/openjpa-integration/jmx/pom.xml
index cf779a0..47f68c2 100644
--- a/openjpa-integration/jmx/pom.xml
+++ b/openjpa-integration/jmx/pom.xml
@@ -37,8 +37,6 @@
     <description>OpenJPA Integration Tests - JMX Platform MBeans</description>
 
   <properties>
-    <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
-    <checkstyle.suppressions.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}suppressions.xml</checkstyle.suppressions.location>
     <!-- Enable the JMX platform MBean provider -->
     <test.jvm.jmxprovider>com.sun.management.jmxremote</test.jvm.jmxprovider>
     <!-- Disable JMX platform MBean authentication  -->
diff --git a/openjpa-integration/slf4j/pom.xml b/openjpa-integration/slf4j/pom.xml
index b1b83f3..d58e50c 100644
--- a/openjpa-integration/slf4j/pom.xml
+++ b/openjpa-integration/slf4j/pom.xml
@@ -36,8 +36,6 @@
     <description>OpenJPA Integration Tests - SLF4JLogFactory</description>
 
     <properties>
-        <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
-        <checkstyle.suppressions.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}suppressions.xml</checkstyle.suppressions.location>
         <!-- use SLF4JLogFactory for logging -->
         <openjpa.Log>slf4j</openjpa.Log>
         <automatic-module-name>org.apache.openjpa.integration.slf4j</automatic-module-name>
diff --git a/openjpa-integration/tck/pom.xml b/openjpa-integration/tck/pom.xml
index e230ccd..5230c6d 100644
--- a/openjpa-integration/tck/pom.xml
+++ b/openjpa-integration/tck/pom.xml
@@ -87,7 +87,6 @@
     <description>OpenJPA Integration Tests - JPA TCK</description>
 
     <properties>
-        <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
         <tck2.level>20110815</tck2.level>
     </properties>
     <profiles>
diff --git a/openjpa-integration/validation/pom.xml b/openjpa-integration/validation/pom.xml
index eb3b02d..334518d 100644
--- a/openjpa-integration/validation/pom.xml
+++ b/openjpa-integration/validation/pom.xml
@@ -36,7 +36,6 @@
     <description>OpenJPA Integration Tests - Bean Validation</description>
 
     <properties>
-        <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
         <checkstyle.suppressions.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}suppressions.xml</checkstyle.suppressions.location>
         <automatic-module-name>org.apache.openjpa.integration.validation</automatic-module-name>
     </properties>
diff --git a/openjpa-tools/pom.xml b/openjpa-junit5/pom.xml
similarity index 51%
copy from openjpa-tools/pom.xml
copy to openjpa-junit5/pom.xml
index 2d07509..4f72473 100644
--- a/openjpa-tools/pom.xml
+++ b/openjpa-junit5/pom.xml
@@ -17,9 +17,6 @@
  specific language governing permissions and limitations
  under the License.
 -->
-<!--
-    Maven release plugin requires the project tag to be on a single line.
--->
 <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>
@@ -27,32 +24,39 @@
     <parent>
         <groupId>org.apache.openjpa</groupId>
         <artifactId>openjpa-parent</artifactId>
-        <version>3.1.2-SNAPSHOT</version>
-<!--        <relativePath>../pom.xml</relativePath>-->
+        <version>3.0.1-SNAPSHOT</version>
     </parent>
 
-    <artifactId>openjpa-tools</artifactId>
-    <packaging>pom</packaging>
-
-    <name>OpenJPA tools</name>
+    <artifactId>openjpa-junit5</artifactId>
+    <packaging>jar</packaging>
+    <name>OpenJPA JUnit 5</name>
+    <description>OpenJPA JUnit 5</description>
 
     <properties>
-        <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
+        <automatic-module-name>org.apache.openjpa.junit5</automatic-module-name>
     </properties>
 
-    <modules>
-        <module>openjpa-maven-plugin</module>
-        <module>openjpa-fetch-statistics</module>
-        <module>openjpa-fetch-statistics-was</module>
-    </modules>
-
-    <profiles>
-        <profile>
-            <id>test-dynamic-enhancer</id>
-            <properties>
-                <policy.file>${basedir}/../../openjpa-persistence-jdbc/src/test/resources/j2.security.test.policy</policy.file>
-            </properties>
-        </profile>
-    </profiles>
-
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-jpa_2.1_spec</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.openjpa</groupId>
+            <artifactId>openjpa</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency> <!-- only for auto mode, can be excluded if not used -->
+            <groupId>org.apache.xbean</groupId>
+            <artifactId>xbean-finder</artifactId>
+            <version>${xbean.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>5.6.0</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
 </project>
diff --git a/openjpa-junit5/src/main/java/org/apache/openjpa/junit5/OpenJPASupport.java b/openjpa-junit5/src/main/java/org/apache/openjpa/junit5/OpenJPASupport.java
new file mode 100644
index 0000000..1348b02
--- /dev/null
+++ b/openjpa-junit5/src/main/java/org/apache/openjpa/junit5/OpenJPASupport.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.openjpa.junit5;
+
+import org.apache.openjpa.junit5.internal.OpenJPAExtension;
+import org.apache.openjpa.lib.log.LogFactory;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Enables <b>in place</b> enhancement for entities listed or found by scanning in test classpath.
+ * It enables to run tests from the IDE with enhancement.
+ *
+ * WARNING: if you use that for tests but don't want to enhance your entities, ensure to clean+recompile them
+ *          after tests otherwise you will have entities depending on OpenJPA at bytecode level.
+ */
+@Target(TYPE)
+@Retention(RUNTIME)
+@ExtendWith(OpenJPAExtension.class)
+public @interface OpenJPASupport {
+    /**
+     * @return the list of class names (don't use {@code MyEntity.class}) to enhance if {@code auto} is false.
+     */
+    String[] entities() default {};
+
+    /**
+     * @return if true, only directories in the classpath will be browsed to enhance entities,
+     *         if false {@link OpenJPASupport#entities()} will be used.
+     */
+    boolean auto() default true;
+
+    /**
+     * @return the log factory to use, if not set slf4j will be tried and if it fails will fallback on the default one.
+     */
+    Class<?> logFactory() default LogFactory.class;
+}
diff --git a/openjpa-junit5/src/main/java/org/apache/openjpa/junit5/internal/OpenJPAExtension.java b/openjpa-junit5/src/main/java/org/apache/openjpa/junit5/internal/OpenJPAExtension.java
new file mode 100644
index 0000000..2761291
--- /dev/null
+++ b/openjpa-junit5/src/main/java/org/apache/openjpa/junit5/internal/OpenJPAExtension.java
@@ -0,0 +1,346 @@
+/*
+ * 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.openjpa.junit5.internal;
+
+import org.apache.openjpa.conf.OpenJPAConfigurationImpl;
+import org.apache.openjpa.enhance.AsmAdaptor;
+import org.apache.openjpa.enhance.PCEnhancer;
+import org.apache.openjpa.enhance.PersistenceCapable;
+import org.apache.openjpa.junit5.OpenJPASupport;
+import org.apache.openjpa.lib.log.LogFactory;
+import org.apache.openjpa.lib.log.LogFactoryImpl;
+import org.apache.openjpa.lib.log.SLF4JLogFactory;
+import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.persistence.PersistenceMetaDataFactory;
+import org.apache.xbean.asm7.AnnotationVisitor;
+import org.apache.xbean.asm7.ClassReader;
+import org.apache.xbean.asm7.Type;
+import org.apache.xbean.asm7.shade.commons.EmptyVisitor;
+import org.apache.xbean.finder.ClassLoaders;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.platform.commons.util.AnnotationUtils;
+import serp.bytecode.BCClass;
+import serp.bytecode.Project;
+
+import javax.persistence.Entity;
+import javax.persistence.MappedSuperclass;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.logging.Logger;
+import java.util.stream.Stream;
+
+import static java.util.Arrays.asList;
+import static org.apache.xbean.asm7.ClassReader.SKIP_CODE;
+import static org.apache.xbean.asm7.ClassReader.SKIP_DEBUG;
+import static org.apache.xbean.asm7.ClassReader.SKIP_FRAMES;
+
+public class OpenJPAExtension implements BeforeAllCallback {
+    private static final Logger LOGGER = Logger.getLogger(OpenJPAExtension.class.getName());
+
+    @Override
+    public void beforeAll(final ExtensionContext context) {
+        AnnotationUtils.findAnnotation(context.getElement(), OpenJPASupport.class).ifPresent(s -> {
+            final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+            final OpenJpaClassLoader enhancementClassLoader = new OpenJpaClassLoader(
+                    classLoader, createLogFactory(classLoader, s.logFactory()));
+            final Thread thread = Thread.currentThread();
+            thread.setContextClassLoader(enhancementClassLoader);
+            try {
+                if (s.auto()) {
+                    try {
+                        ClassLoaders.findUrls(enhancementClassLoader.getParent()).stream()
+                                .map(org.apache.xbean.finder.util.Files::toFile)
+                                .filter(File::isDirectory)
+                                .map(File::toPath)
+                                .forEach(dir -> {
+                                    LOGGER.fine(() -> "Enhancing folder '" + dir + "'");
+                                    try {
+                                        enhanceDirectory(enhancementClassLoader, dir);
+                                    } catch (final IOException e) {
+                                        throw new IllegalStateException(e);
+                                    }
+                                });
+                    } catch (final IOException e) {
+                        throw new IllegalStateException(e);
+                    }
+                } else {
+                    Stream.of(s.entities()).forEach(e -> {
+                        try {
+                            enhancementClassLoader.loadClass(e);
+                        } catch (final ClassNotFoundException e1) {
+                            throw new IllegalArgumentException(e1);
+                        }
+                    });
+                }
+            } finally {
+                thread.setContextClassLoader(enhancementClassLoader.getParent());
+            }
+        });
+    }
+
+    private LogFactory createLogFactory(final ClassLoader classLoader, final Class<?> logFactory) {
+        try {
+            if (logFactory == LogFactory.class) {
+                try {
+                    return new SLF4JLogFactory();
+                } catch (final Error | Exception e) {
+                    return new LogFactoryImpl();
+                }
+            }
+            return logFactory.asSubclass(LogFactory.class).getConstructor().newInstance();
+        } catch (final RuntimeException e) {
+            throw e;
+        } catch (final Exception e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private void enhanceDirectory(final OpenJpaClassLoader enhancementClassLoader, final Path dir) throws IOException {
+        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+                if (file.getFileName().toString().endsWith(".class")) {
+                    final String relativeName = dir.relativize(file).toString();
+                    try {
+                        enhancementClassLoader.handleEnhancement(
+                                relativeName.substring(0, relativeName.length() - ".class".length()));
+                    } catch (final ClassNotFoundException e) {
+                        throw new IllegalStateException(e);
+                    }
+                }
+                return super.visitFile(file, attrs);
+            }
+        });
+    }
+
+    private static abstract class BaseClassLoader extends ClassLoader {
+        private BaseClassLoader(final ClassLoader parent) {
+            super(parent);
+        }
+
+        protected abstract Class<?> doLoadClass(String name, boolean resolve) throws ClassNotFoundException;
+
+        @Override
+        protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
+            if (name != null && !name.startsWith("java") && !name.startsWith("sun") && !name.startsWith("jdk")) {
+                return doLoadClass(name, resolve);
+            }
+            return defaultLoadClass(name, resolve);
+        }
+
+        protected Class<?> defaultLoadClass(final String name, final boolean resolve) throws ClassNotFoundException {
+            return super.loadClass(name, resolve);
+        }
+
+        protected byte[] loadBytes(final String name) {
+            final URL url = findUrl(name);
+            if (url == null || "jar".equals(url.getProtocol()) /*assume done in build*/) {
+                return null;
+            }
+            byte[] buffer = new byte[4096];
+            final ByteArrayOutputStream inMem = new ByteArrayOutputStream(buffer.length);
+            try (final InputStream is = url.openStream()) {
+                int read;
+                while ((read = is.read(buffer)) >= 0) {
+                    if (read > 0) {
+                        inMem.write(buffer, 0, read);
+                    }
+                }
+            } catch (final IOException e) {
+                throw new IllegalStateException(e);
+            }
+            return inMem.toByteArray();
+        }
+
+        protected URL findUrl(final String name) {
+            return getResource(name.replace('.', '/') + ".class");
+        }
+    }
+
+    private static class OpenJpaClassLoader extends BaseClassLoader {
+        private static final String PERSITENCE_CAPABLE = Type.getDescriptor(PersistenceCapable.class);
+        private static final String ENTITY = Type.getDescriptor(Entity.class);
+        private static final String MAPPED_SUPERCLASS = Type.getDescriptor(MappedSuperclass.class);
+
+        private final MetaDataRepository repos;
+        private final ClassLoader tmpLoader;
+        private final Collection<String> alreadyEnhanced = new ArrayList<>();
+
+        private OpenJpaClassLoader(final ClassLoader parent, final LogFactory logFactory) {
+            super(parent);
+
+            final OpenJPAConfigurationImpl conf = new OpenJPAConfigurationImpl();
+            conf.setLogFactory(logFactory);
+
+            tmpLoader = new CompanionLoader(parent);
+            repos = new MetaDataRepository();
+            repos.setConfiguration(conf);
+            repos.setMetaDataFactory(new PersistenceMetaDataFactory());
+        }
+
+        @Override
+        protected synchronized Class<?> doLoadClass(final String name, final boolean resolve) throws ClassNotFoundException {
+            final Class<?> clazz = findLoadedClass(name);
+            if (clazz != null) {
+                if (resolve) {
+                    resolveClass(clazz);
+                }
+                return clazz;
+            }
+            handleEnhancement(name);
+            return defaultLoadClass(name, resolve);
+        }
+
+        private void handleEnhancement(final String name) throws ClassNotFoundException {
+            final byte[] enhanced = ensureEnhancedIfNeeded(name);
+            if (enhanced != null && alreadyEnhanced.add(name)) {
+                // we could do that but test classes will be loaded with parent loader
+                // so just rewrite the class on the fly assuming it was not yet read
+                try {
+                    Files.write(findTarget(name), enhanced, StandardOpenOption.TRUNCATE_EXISTING);
+                    LOGGER.info(() -> "Enhanced '" + name + "'");
+                } catch (final IOException e) {
+                    throw new ClassNotFoundException(e.getMessage(), e);
+                }
+            }
+        }
+
+        private Path findTarget(final String name) {
+            final URL url = findUrl(name);
+            if (!"file".equals(url.getProtocol())) {
+                throw new IllegalStateException("Only file urls are supported today: " + url);
+            }
+            return Paths.get(url.getPath());
+        }
+
+        private byte[] enhance(final byte[] classBytes) {
+            final Thread thread = Thread.currentThread();
+            final ClassLoader old = thread.getContextClassLoader();
+            thread.setContextClassLoader(tmpLoader);
+            try (final InputStream stream = new ByteArrayInputStream(classBytes)) {
+                final PCEnhancer enhancer = new PCEnhancer(
+                        repos.getConfiguration(),
+                        new Project().loadClass(stream, tmpLoader),
+                        repos, tmpLoader);
+                if (enhancer.run() == PCEnhancer.ENHANCE_NONE) {
+                    return null;
+                }
+                final BCClass pcb = enhancer.getPCBytecode();
+                return AsmAdaptor.toByteArray(pcb, pcb.toByteArray());
+            } catch (final IOException e) {
+                throw new IllegalStateException(e);
+            } finally {
+                thread.setContextClassLoader(old);
+            }
+        }
+
+        private boolean isJpaButNotEnhanced(final byte[] classBytes) {
+            try (final InputStream stream = new ByteArrayInputStream(classBytes)) {
+                final ClassReader reader = new ClassReader(stream);
+                reader.accept(new EmptyVisitor() {
+                    @Override
+                    public void visit(final int version, final int access, final String name,
+                                      final String signature, final String superName, final String[] interfaces) {
+                        if (interfaces != null && asList(interfaces).contains(PERSITENCE_CAPABLE)) {
+                            throw new AlreadyEnhanced(); // exit
+                        }
+                        super.visit(version, access, name, signature, superName, interfaces);
+                    }
+
+                    @Override
+                    public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+                        if (ENTITY.equals(descriptor) || MAPPED_SUPERCLASS.equals(descriptor)) {
+                            throw new MissingEnhancement(); // we already went into visit() so we miss the enhancement
+                        }
+                        return new EmptyVisitor().visitAnnotation(descriptor, visible);
+                    }
+                }, SKIP_DEBUG + SKIP_CODE + SKIP_FRAMES);
+                return false;
+            } catch (final IOException e) {
+                throw new IllegalStateException(e);
+            } catch (final AlreadyEnhanced alreadyEnhanced) {
+                return false;
+            } catch (final MissingEnhancement alreadyEnhanced) {
+                return true;
+            }
+        }
+
+        private byte[] ensureEnhancedIfNeeded(final String name) {
+            final byte[] classBytes = loadBytes(name);
+            if (classBytes == null) {
+                return null;
+            }
+            if (isJpaButNotEnhanced(classBytes)) {
+                final byte[] enhanced = enhance(classBytes);
+                if (enhanced != null) {
+                    return enhanced;
+                }
+                LOGGER.info("'" + name + "' already enhanced");
+            }
+            return null;
+        }
+    }
+
+    private static class CompanionLoader extends BaseClassLoader {
+        private CompanionLoader(final ClassLoader parent) {
+            super(parent);
+        }
+
+        @Override
+        protected Class<?> doLoadClass(final String name, final boolean resolve) throws ClassNotFoundException {
+            final Class<?> clazz = findLoadedClass(name);
+            if (clazz != null) {
+                if (resolve) {
+                    resolveClass(clazz);
+                }
+                return clazz;
+            }
+            final byte[] content = loadBytes(name);
+            if (content != null) {
+                final Class<?> value = super.defineClass(name, content, 0, content.length);
+                if (resolve) {
+                    resolveClass(value);
+                }
+                return value;
+            }
+            return defaultLoadClass(name, resolve);
+        }
+    }
+
+    private static class MissingEnhancement extends RuntimeException {
+    }
+
+    private static class AlreadyEnhanced extends RuntimeException {
+    }
+}
diff --git a/openjpa-junit5/src/test/java/org/apache/openjpa/junit5/MyEntity.java b/openjpa-junit5/src/test/java/org/apache/openjpa/junit5/MyEntity.java
new file mode 100644
index 0000000..2db80ce
--- /dev/null
+++ b/openjpa-junit5/src/test/java/org/apache/openjpa/junit5/MyEntity.java
@@ -0,0 +1,28 @@
+/*
+ * 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.openjpa.junit5;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class MyEntity {
+    @Id
+    private long id;
+}
diff --git a/openjpa-junit5/src/test/java/org/apache/openjpa/junit5/OpenJPASupportTest.java b/openjpa-junit5/src/test/java/org/apache/openjpa/junit5/OpenJPASupportTest.java
new file mode 100644
index 0000000..f5a2054
--- /dev/null
+++ b/openjpa-junit5/src/test/java/org/apache/openjpa/junit5/OpenJPASupportTest.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.openjpa.junit5;
+
+import org.apache.openjpa.enhance.PersistenceCapable;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@OpenJPASupport
+class OpenJPASupportTest {
+    @Test
+    void ensureEnhanced() {
+        assertTrue(PersistenceCapable.class.isAssignableFrom(MyEntity.class));
+    }
+}
diff --git a/openjpa-kernel/pom.xml b/openjpa-kernel/pom.xml
index 0762eba..5a32849 100644
--- a/openjpa-kernel/pom.xml
+++ b/openjpa-kernel/pom.xml
@@ -78,7 +78,7 @@
         <dependency>
             <groupId>org.apache.xbean</groupId>
             <artifactId>xbean-asm7-shaded</artifactId>
-            <version>4.12</version>
+            <version>${xbean.version}</version>
         </dependency>
     </dependencies>
 
diff --git a/openjpa-project/checkstyle.xml b/openjpa-project/checkstyle.xml
index ec7bd24..d59aa17 100644
--- a/openjpa-project/checkstyle.xml
+++ b/openjpa-project/checkstyle.xml
@@ -21,21 +21,23 @@
     <module name="NewlineAtEndOfFile">
         <property name="lineSeparator" value="lf"/>
     </module>
+    <module name="LineLength">
+        <property name="max" value="150"/>
+    </module>
     <module name="TreeWalker">
-        <module name="FileContentsHolder"/>
-        <module name="LineLength">
-            <property name="max" value="150"/>
-        </module>
         <module name="RegexpSinglelineJava">
             <property name="format" value="System\.(out|err)\.print(ln)?\("/>
             <property name="ignoreComments" value="true"/>
         </module>
-    </module>
-    <module name="SuppressionCommentFilter">
-        <property name="offCommentFormat" value="// START - ALLOW PRINT STATEMENTS"/>
-        <property name="onCommentFormat" value="// STOP - ALLOW PRINT STATEMENTS"/>
+        <module name="SuppressionCommentFilter">
+            <property name="offCommentFormat" value="// START - ALLOW PRINT STATEMENTS"/>
+            <property name="onCommentFormat" value="// STOP - ALLOW PRINT STATEMENTS"/>
+        </module>
     </module>
 
     <!-- File location is specified in root pom.xml via ${checkstyle.suppressions.location} -->
-    <module name="SuppressionFilter"/>
+    <module name="SuppressionFilter">
+        <property name="file" value="${checkstyle.suppressions.location}"/>
+        <property name="optional" value="true"/>
+    </module>
 </module>
diff --git a/openjpa-tools/openjpa-fetch-statistics-was/pom.xml b/openjpa-tools/openjpa-fetch-statistics-was/pom.xml
index 973fe0e..33928e7 100644
--- a/openjpa-tools/openjpa-fetch-statistics-was/pom.xml
+++ b/openjpa-tools/openjpa-fetch-statistics-was/pom.xml
@@ -28,7 +28,6 @@
     <inceptionYear>2012</inceptionYear>
 
     <properties>
-        <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
         <checkstyle.suppressions.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}suppressions.xml</checkstyle.suppressions.location>
         <automatic-module-name>org.apache.openjpa.tools.statistics.was</automatic-module-name>
     </properties>
diff --git a/openjpa-tools/openjpa-fetch-statistics/pom.xml b/openjpa-tools/openjpa-fetch-statistics/pom.xml
index d2fbf61..bb76922 100644
--- a/openjpa-tools/openjpa-fetch-statistics/pom.xml
+++ b/openjpa-tools/openjpa-fetch-statistics/pom.xml
@@ -28,7 +28,6 @@
     <inceptionYear>2012</inceptionYear>
 
     <properties>
-        <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
         <checkstyle.suppressions.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}suppressions.xml</checkstyle.suppressions.location>
         <automatic-module-name>org.apache.openjpa.tools.statistics</automatic-module-name>
     </properties>
diff --git a/openjpa-tools/openjpa-maven-plugin/pom.xml b/openjpa-tools/openjpa-maven-plugin/pom.xml
index f83508f..a14be22 100644
--- a/openjpa-tools/openjpa-maven-plugin/pom.xml
+++ b/openjpa-tools/openjpa-maven-plugin/pom.xml
@@ -41,7 +41,6 @@
     </description>
     <inceptionYear>2011</inceptionYear>
     <properties>
-        <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
         <checkstyle.suppressions.location>${project.basedir}${file.separator}..${file.separator}..${file.separator}openjpa-project${file.separator}suppressions.xml</checkstyle.suppressions.location>
         <min.maven.version>3.3.9</min.maven.version>
     </properties>
diff --git a/openjpa-tools/pom.xml b/openjpa-tools/pom.xml
index 2d07509..4f2abbe 100644
--- a/openjpa-tools/pom.xml
+++ b/openjpa-tools/pom.xml
@@ -36,10 +36,6 @@
 
     <name>OpenJPA tools</name>
 
-    <properties>
-        <checkstyle.config.location>${project.basedir}${file.separator}..${file.separator}openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
-    </properties>
-
     <modules>
         <module>openjpa-maven-plugin</module>
         <module>openjpa-fetch-statistics</module>
diff --git a/pom.xml b/pom.xml
index e0b1426..84776e6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,7 +47,6 @@
         <openjpa.version>${project.version}</openjpa.version>
         <openjpa.Log>DefaultLevel=INFO</openjpa.Log>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <checkstyle.config.location>openjpa-project${file.separator}checkstyle.xml</checkstyle.config.location>
         <checkstyle.suppressions.location>openjpa-project${file.separator}suppressions.xml</checkstyle.suppressions.location>
 
         <site.deploy.url>scp://people.apache.org/home/${site.deploy.user.name}/public_html/openjpa/${project.version}/staging-site</site.deploy.url>
@@ -93,8 +92,9 @@
 
         <maven.javadoc.version>3.0.1</maven.javadoc.version>
         <javadoc.additionalparam />
-        <maven.surefire.version>2.22.0</maven.surefire.version>
+        <maven.surefire.version>3.0.0-M4</maven.surefire.version>
 
+        <xbean.version>4.16</xbean.version>
         <bval.version>1.1.2</bval.version>
         <jmock.version>2.9.0</jmock.version>
         <automatic-module-name>-SUBMODULES-NEED-TO-OVERRIDE-THIS-</automatic-module-name>
@@ -183,6 +183,7 @@
         <module>openjpa-all</module>
         <module>openjpa-tools</module>
         <module>openjpa-features</module>
+        <module>openjpa-junit5</module>
     </modules>
 
     <profiles>
@@ -1702,7 +1703,7 @@
             <dependency>
                 <groupId>org.apache.commons</groupId>
                 <artifactId>commons-collections4</artifactId>
-                <version>4.3</version>
+                <version>4.4</version>
             </dependency>
             <dependency>
                 <groupId>net.sourceforge.serp</groupId>
@@ -1718,7 +1719,7 @@
             <dependency>
                 <groupId>org.apache.geronimo.specs</groupId>
                 <artifactId>geronimo-jpa_2.2_spec</artifactId>
-                <version>1.1-SNAPSHOT</version>
+                <version>1.0</version>
             </dependency>
             <dependency>
                 <groupId>org.apache.geronimo.specs</groupId>
@@ -1758,7 +1759,7 @@
             <dependency>
                 <groupId>org.apache.commons</groupId>
                 <artifactId>commons-dbcp2</artifactId>
-                <version>2.6.0</version>
+                <version>2.7.0</version>
             </dependency>
             <dependency>
                 <groupId>javax.xml.bind</groupId>
@@ -2067,7 +2068,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-checkstyle-plugin</artifactId>
-                    <version>3.0.0</version>
+                    <version>3.1.1</version>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
@@ -2322,6 +2323,34 @@
                             <failsOnError>true</failsOnError>
                             <consoleOutput>true</consoleOutput>
                             <includeTestSourceDirectory>true</includeTestSourceDirectory>
+                            <excludes>**/*_.java</excludes>
+                            <logViolationsToConsole>true</logViolationsToConsole>
+                            <checkstyleRules>
+                                <module name="Checker">
+                                    <module name="NewlineAtEndOfFile">
+                                        <property name="lineSeparator" value="lf"/>
+                                    </module>
+                                    <module name="LineLength">
+                                        <property name="max" value="150"/>
+                                    </module>
+                                    <module name="TreeWalker">
+                                        <module name="RegexpSinglelineJava">
+                                            <property name="format" value="System\.(out|err)\.print(ln)?\("/>
+                                            <property name="ignoreComments" value="true"/>
+                                        </module>
+                                        <module name="SuppressionCommentFilter">
+                                            <property name="offCommentFormat" value="// START - ALLOW PRINT STATEMENTS"/>
+                                            <property name="onCommentFormat" value="// STOP - ALLOW PRINT STATEMENTS"/>
+                                        </module>
+                                    </module>
+
+                                    <!-- File location is specified in root pom.xml via ${checkstyle.suppressions.location} -->
+                                    <module name="SuppressionFilter">
+                                        <property name="file" value="${checkstyle.suppressions.location}"/>
+                                        <property name="optional" value="true"/>
+                                    </module>
+                                </module>
+                            </checkstyleRules>
                         </configuration>
                     </execution>
                 </executions>