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 <rmannibu...@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>