From 5b25046ba2acf966166b69ba9c3688537b6a3fe4 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 6 Oct 2021 18:08:34 +0100 Subject: [PATCH] Introduce a custom QuarkusForkJoinWorkerThread to controll the classloader in the Fork/Join common pool --- .../deployment/ForkJoinPoolProcessor.java | 13 +++++++ .../deployment/jbang/JBangAugmentorImpl.java | 2 + .../main/java/io/quarkus/runtime/Quarkus.java | 5 ++- .../forkjoin/QuarkusForkJoinWorkerThread.java | 37 +++++++++++++++++++ .../QuarkusForkJoinWorkerThreadFactory.java | 23 ++++++++++++ .../bootstrap/runner/QuarkusEntryPoint.java | 4 ++ 6 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/ForkJoinPoolProcessor.java create mode 100644 independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/forkjoin/QuarkusForkJoinWorkerThread.java create mode 100644 independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/forkjoin/QuarkusForkJoinWorkerThreadFactory.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/ForkJoinPoolProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/ForkJoinPoolProcessor.java new file mode 100644 index 00000000000000..e98be4904efa01 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/ForkJoinPoolProcessor.java @@ -0,0 +1,13 @@ +package io.quarkus.deployment; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.SystemPropertyBuildItem; + +public final class ForkJoinPoolProcessor { + + @BuildStep + SystemPropertyBuildItem setProperty() { + return new SystemPropertyBuildItem("java.util.concurrent.ForkJoinPool.common.threadFactory", + "io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThreadFactory"); + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/jbang/JBangAugmentorImpl.java b/core/deployment/src/main/java/io/quarkus/deployment/jbang/JBangAugmentorImpl.java index b49d007a01c752..db6a105b1b336f 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/jbang/JBangAugmentorImpl.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/jbang/JBangAugmentorImpl.java @@ -124,6 +124,8 @@ public void accept(BuildChainBuilder builder) { resultMap.put("files", result); final List javaargs = new ArrayList<>(); javaargs.add("-Djava.util.logging.manager=org.jboss.logmanager.LogManager"); + javaargs.add( + "-Djava.util.concurrent.ForkJoinPool.common.threadFactory=io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThreadFactory"); resultMap.put("java-args", javaargs); resultMap.put("main-class", buildResult.consume(MainClassBuildItem.class).getClassName()); if (nativeRequested) { diff --git a/core/runtime/src/main/java/io/quarkus/runtime/Quarkus.java b/core/runtime/src/main/java/io/quarkus/runtime/Quarkus.java index e6ecf8b5f4d11e..5b92c73344abef 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/Quarkus.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/Quarkus.java @@ -5,7 +5,6 @@ import java.util.function.BiConsumer; import org.jboss.logging.Logger; -import org.jboss.logmanager.LogManager; import io.quarkus.launcher.QuarkusLauncher; @@ -57,7 +56,9 @@ public static void run(Class quarkusApplication, S public static void run(Class quarkusApplication, BiConsumer exitHandler, String... args) { try { - System.setProperty("java.util.logging.manager", LogManager.class.getName()); + System.setProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager"); + System.setProperty("java.util.concurrent.ForkJoinPool.common.threadFactory", + "io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThreadFactory"); //production and common dev mode path //we already have an application, run it directly Class appClass = (Class) Class.forName(Application.APP_CLASS_NAME, diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/forkjoin/QuarkusForkJoinWorkerThread.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/forkjoin/QuarkusForkJoinWorkerThread.java new file mode 100644 index 00000000000000..9e00f5a06bcda1 --- /dev/null +++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/forkjoin/QuarkusForkJoinWorkerThread.java @@ -0,0 +1,37 @@ +package io.quarkus.bootstrap.forkjoin; + +import io.quarkus.bootstrap.runner.RunnerClassLoader; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; + +public class QuarkusForkJoinWorkerThread extends ForkJoinWorkerThread { + + private static volatile ClassLoader qClassloader; + + protected QuarkusForkJoinWorkerThread(ForkJoinPool pool) { + super(pool); + } + + public static synchronized void setQuarkusAppClassloader(RunnerClassLoader runnerClassLoader) { + if (qClassloader != null) { + throw new IllegalStateException("Attempting to set the Quarkus root classloader while it was already set"); + } + if (runnerClassLoader == null) { + throw new IllegalStateException("Attempting to set the Quarkus root classloader to null"); + } + qClassloader = runnerClassLoader; + } + + protected void onStart() { + super.onStart(); + if (qClassloader == null) { + throw new IllegalStateException( + "Fork Join pool initialization has been triggered before the application classloader has been initialized. " + + + "Use this stacktrace to figure out what triggered it, and avoid using the Fork Join pool this early during the boot process."); + } else { + super.setContextClassLoader(qClassloader); + } + } + +} diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/forkjoin/QuarkusForkJoinWorkerThreadFactory.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/forkjoin/QuarkusForkJoinWorkerThreadFactory.java new file mode 100644 index 00000000000000..67f7d8344fff8b --- /dev/null +++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/forkjoin/QuarkusForkJoinWorkerThreadFactory.java @@ -0,0 +1,23 @@ +package io.quarkus.bootstrap.forkjoin; + +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; + +/** + * This implementation of JDK's ForkJoinPool.ForkJoinWorkerThreadFactory + * needs to be enabled by setting system property {@code java.util.concurrent.ForkJoinPool.common.threadFactory} + * to this class name, so to allow Quarkus to set the contextual classloader to the correct + * runtime classloader for each thread being started by the common pool. + * Otherwise the system classloader will be set, which is unable to load all application resources. + */ +public final class QuarkusForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { + + public QuarkusForkJoinWorkerThreadFactory() { + } + + @Override + public ForkJoinWorkerThread newThread(ForkJoinPool pool) { + return new QuarkusForkJoinWorkerThread(pool); + } + +} diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/QuarkusEntryPoint.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/QuarkusEntryPoint.java index 2ae1c385cfe5a5..6b543df1886483 100644 --- a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/QuarkusEntryPoint.java +++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/QuarkusEntryPoint.java @@ -1,5 +1,6 @@ package io.quarkus.bootstrap.runner; +import io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThread; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; @@ -21,6 +22,8 @@ public class QuarkusEntryPoint { public static void main(String... args) throws Throwable { System.setProperty("java.util.logging.manager", org.jboss.logmanager.LogManager.class.getName()); + System.setProperty("java.util.concurrent.ForkJoinPool.common.threadFactory", + "io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThreadFactory"); Timing.staticInitStarted(false); doRun(args); } @@ -44,6 +47,7 @@ private static void doRun(Object args) throws IOException, ClassNotFoundExceptio } try { Thread.currentThread().setContextClassLoader(app.getRunnerClassLoader()); + QuarkusForkJoinWorkerThread.setQuarkusAppClassloader(app.getRunnerClassLoader()); Class mainClass = app.getRunnerClassLoader().loadClass(app.getMainClass()); mainClass.getMethod("main", String[].class).invoke(null, args); } finally {