From 752ec72787e8c67f213bd8b4ee65f616e113da2f Mon Sep 17 00:00:00 2001 From: Nicolas Filotto Date: Thu, 29 Jun 2023 21:06:50 +0200 Subject: [PATCH] Allow loading custom Groovy extension modules --- core/runtime/pom.xml | 4 --- .../test/common/GroovyCacheCleaner.java | 34 ------------------- .../quarkus/test/common/GroovyClassValue.java | 18 ++++++++++ .../io/quarkus/test/QuarkusDevModeTest.java | 4 +-- .../java/io/quarkus/test/QuarkusUnitTest.java | 4 +-- .../test/junit/QuarkusTestExtension.java | 4 +-- 6 files changed, 24 insertions(+), 44 deletions(-) delete mode 100644 test-framework/common/src/main/java/io/quarkus/test/common/GroovyCacheCleaner.java create mode 100644 test-framework/common/src/main/java/io/quarkus/test/common/GroovyClassValue.java diff --git a/core/runtime/pom.xml b/core/runtime/pom.xml index 156132a7679de..31ec4f8549d1a 100644 --- a/core/runtime/pom.xml +++ b/core/runtime/pom.xml @@ -192,10 +192,6 @@ io.quarkus:quarkus-bootstrap-gradle-resolver io.quarkus:quarkus-junit5-mockito-config - - org.apache.groovy:groovy - org.junit.platform:junit-platform-launcher diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/GroovyCacheCleaner.java b/test-framework/common/src/main/java/io/quarkus/test/common/GroovyCacheCleaner.java deleted file mode 100644 index e46bc11b2fe18..0000000000000 --- a/test-framework/common/src/main/java/io/quarkus/test/common/GroovyCacheCleaner.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.quarkus.test.common; - -import java.lang.reflect.Method; -import java.util.Collection; - -import io.quarkus.bootstrap.classloading.QuarkusClassLoader; - -/** - * Groovy maintains a cache that causes memory leaks - * - * We need to manually clear it if present - */ -public class GroovyCacheCleaner { - - public static void clearGroovyCache() { - try { - Class clazz = Class.forName("org.codehaus.groovy.reflection.ClassInfo", true, - GroovyCacheCleaner.class.getClassLoader()); - Method getTheClass = clazz.getDeclaredMethod("getTheClass"); - Method remove = clazz.getDeclaredMethod("remove", Class.class); - Collection info = (Collection) clazz.getDeclaredMethod("getAllClassInfo").invoke(null); - for (Object obj : info) { - Class theClass = (Class) getTheClass.invoke(obj); - ClassLoader classLoader = theClass.getClassLoader(); - if (classLoader instanceof QuarkusClassLoader && ((QuarkusClassLoader) classLoader).isClosed()) { - remove.invoke(null, theClass); - } - } - } catch (Exception exception) { - return; - } - } - -} diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/GroovyClassValue.java b/test-framework/common/src/main/java/io/quarkus/test/common/GroovyClassValue.java new file mode 100644 index 0000000000000..79d8368e6f4b5 --- /dev/null +++ b/test-framework/common/src/main/java/io/quarkus/test/common/GroovyClassValue.java @@ -0,0 +1,18 @@ +package io.quarkus.test.common; + +/** + * {@link ClassValue} are used in Groovy which causes memory leaks if not properly cleaned, but unfortunately, they are + * very complex to clean up, especially the {@link ClassValue} corresponding to system classes that must be cleaned too + * to avoid memory leaks moreover if not cleaned wisely errors of type {@code MissingMethodException} can be thrown, so + * we had better to simply disable them by setting the System property {@code groovy.use.classvalue} to {@code false} + * see GROOVY-7591 for more details. + */ +public final class GroovyClassValue { + + private GroovyClassValue() { + } + + public static void disable() { + System.setProperty("groovy.use.classvalue", "false"); + } +} diff --git a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusDevModeTest.java b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusDevModeTest.java index f3806306e8cf8..abc123acc4f09 100644 --- a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusDevModeTest.java +++ b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusDevModeTest.java @@ -51,7 +51,7 @@ import io.quarkus.paths.PathList; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.configuration.ProfileManager; -import io.quarkus.test.common.GroovyCacheCleaner; +import io.quarkus.test.common.GroovyClassValue; import io.quarkus.test.common.PathTestHelper; import io.quarkus.test.common.PropertyTestUtil; import io.quarkus.test.common.TestResourceManager; @@ -219,6 +219,7 @@ public Object createTestInstance(TestInstanceFactoryContext factoryContext, Exte @Override public void beforeAll(ExtensionContext context) throws Exception { + GroovyClassValue.disable(); //set the right launch mode in the outer CL, used by the HTTP host config source ProfileManager.setLaunchMode(LaunchMode.DEVELOPMENT); originalRootLoggerHandlers = rootLogger.getHandlers(); @@ -313,7 +314,6 @@ public void afterAll(ExtensionContext context) throws Exception { inMemoryLogHandler.clearRecords(); inMemoryLogHandler.setFilter(null); ClearCache.clearAnnotationCache(); - GroovyCacheCleaner.clearGroovyCache(); } @Override diff --git a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java index 959fe86c83333..e7369ea65cee0 100644 --- a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java +++ b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java @@ -71,7 +71,7 @@ import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.configuration.ProfileManager; import io.quarkus.runtime.logging.JBossVersion; -import io.quarkus.test.common.GroovyCacheCleaner; +import io.quarkus.test.common.GroovyClassValue; import io.quarkus.test.common.PathTestHelper; import io.quarkus.test.common.PropertyTestUtil; import io.quarkus.test.common.RestAssuredURLManager; @@ -506,6 +506,7 @@ private void runExtensionMethod(ReflectiveInvocationContext invocationCo @Override public void beforeAll(ExtensionContext extensionContext) throws Exception { + GroovyClassValue.disable(); //set the right launch mode in the outer CL, used by the HTTP host config source ProfileManager.setLaunchMode(LaunchMode.TEST); if (beforeAllCustomizer != null) { @@ -759,7 +760,6 @@ public void afterAll(ExtensionContext extensionContext) throws Exception { afterAllCustomizer.run(); } ClearCache.clearAnnotationCache(); - GroovyCacheCleaner.clearGroovyCache(); } if (records != null) { assertLogRecords.accept(records); diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java index ceb51b5c4f7ca..e1d0bd9b3e0a5 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java @@ -92,7 +92,7 @@ import io.quarkus.runtime.logging.JBossVersion; import io.quarkus.runtime.test.TestHttpEndpointProvider; import io.quarkus.test.TestMethodInvoker; -import io.quarkus.test.common.GroovyCacheCleaner; +import io.quarkus.test.common.GroovyClassValue; import io.quarkus.test.common.PathTestHelper; import io.quarkus.test.common.PropertyTestUtil; import io.quarkus.test.common.RestAssuredURLManager; @@ -296,7 +296,6 @@ public void close() throws IOException { tm.close(); } finally { restorableSystemProperties.close(); - GroovyCacheCleaner.clearGroovyCache(); shutdownHangDetection(); } } @@ -645,6 +644,7 @@ private void throwBootFailureException() { @Override public void beforeAll(ExtensionContext context) throws Exception { + GroovyClassValue.disable(); currentTestClassStack.push(context.getRequiredTestClass()); //set the right launch mode in the outer CL, used by the HTTP host config source ProfileManager.setLaunchMode(LaunchMode.TEST);