Ensure that Java's ForkJoinPool is initialized with Quarkus' ThreadPoolFactory

Closes #30120

Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
This commit is contained in:
Alexander Schwartz 2024-06-04 18:34:26 +02:00 committed by Alexander Schwartz
parent 20368b35b9
commit 1b821f3267
4 changed files with 27 additions and 3 deletions

View file

@ -165,7 +165,8 @@ if "x%JAVA%" == "x" (
set CLASSPATH_OPTS="%DIRNAME%..\lib\quarkus-run.jar"
set JAVA_RUN_OPTS=%JAVA_OPTS% -Dkc.home.dir="%DIRNAME%.." -Djboss.server.config.dir="%DIRNAME%..\conf" -Dkeycloak.theme.dir="%DIRNAME%..\themes" %SERVER_OPTS% -cp %CLASSPATH_OPTS% io.quarkus.bootstrap.runner.QuarkusEntryPoint %CONFIG_ARGS%
rem The property 'java.util.concurrent.ForkJoinPool.common.threadFactory' is set here, as a Java Agent or enabling JMX might initialize the factory before Quarkus can set the property in JDK21+.
set JAVA_RUN_OPTS=-Djava.util.concurrent.ForkJoinPool.common.threadFactory=io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThreadFactory %JAVA_OPTS% -Dkc.home.dir="%DIRNAME%.." -Djboss.server.config.dir="%DIRNAME%..\conf" -Dkeycloak.theme.dir="%DIRNAME%..\themes" %SERVER_OPTS% -cp %CLASSPATH_OPTS% io.quarkus.bootstrap.runner.QuarkusEntryPoint %CONFIG_ARGS%
set OPTIMIZED_OPTION=--optimized
set HELP_LONG_OPTION=--help

View file

@ -157,7 +157,8 @@ esceval_args() {
JAVA_RUN_OPTS=$(echo "$JAVA_OPTS" | xargs printf '%s\n' | esceval_args)
JAVA_RUN_OPTS="$JAVA_RUN_OPTS $SERVER_OPTS -cp $CLASSPATH_OPTS io.quarkus.bootstrap.runner.QuarkusEntryPoint ${CONFIG_ARGS#?}"
# The property 'java.util.concurrent.ForkJoinPool.common.threadFactory' is set here, as a Java Agent or enabling JMX might initialize the factory before Quarkus can set the property in JDK21+.
JAVA_RUN_OPTS="-Djava.util.concurrent.ForkJoinPool.common.threadFactory=io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThreadFactory $JAVA_RUN_OPTS $SERVER_OPTS -cp $CLASSPATH_OPTS io.quarkus.bootstrap.runner.QuarkusEntryPoint ${CONFIG_ARGS#?}"
if [ "$PRINT_ENV" = "true" ]; then
echo "Using JAVA_OPTS: $JAVA_OPTS"

View file

@ -29,6 +29,7 @@ import static org.keycloak.quarkus.runtime.cli.command.Start.isDevProfileNotAllo
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import jakarta.enterprise.context.ApplicationScoped;
import org.keycloak.common.profile.ProfileException;
@ -56,6 +57,8 @@ import io.quarkus.runtime.annotations.QuarkusMain;
public class KeycloakMain implements QuarkusApplication {
public static void main(String[] args) {
ensureForkJoinPoolThreadFactoryHasBeenSetToQuarkus();
System.setProperty("kc.version", Version.VERSION);
List<String> cliArgs = null;
try {
@ -96,6 +99,25 @@ public class KeycloakMain implements QuarkusApplication {
parseAndRun(cliArgs);
}
/**
* Verify that the property for the ForkJoinPool factory set by Quarkus matches the actual factory.
* If this is not the case, the classloader for those threads is not set correctly, and for example loading configurations
* via SmallRye is unreliable. This can happen if a Java Agent or JMX initializes the ForkJoinPool before Java's main method is run.
*/
private static void ensureForkJoinPoolThreadFactoryHasBeenSetToQuarkus() {
// At this point, the settings from the CLI are no longer visible as they have been overwritten in the QuarkusEntryPoint.
// Therefore, the only thing we can do is to check if the thread pool class name is the same as in the configuration.
final String FORK_JOIN_POOL_COMMON_THREAD_FACTORY = "java.util.concurrent.ForkJoinPool.common.threadFactory";
String sf = System.getProperty(FORK_JOIN_POOL_COMMON_THREAD_FACTORY);
//noinspection resource
if (!ForkJoinPool.commonPool().getFactory().getClass().getName().equals(sf)) {
Logger.getLogger(KeycloakMain.class).errorf("The ForkJoinPool has been initialized with the wrong thread factory. The property '%s' should be set on the Java CLI to ensure Java's ForkJoinPool will always be initialized with '%s' even if there are Java agents which might initialize logging or other capabilities earlier than the main method.",
FORK_JOIN_POOL_COMMON_THREAD_FACTORY,
sf);
throw new RuntimeException("The ForkJoinPool has been initialized with the wrong thread factory");
}
}
private static void handleUsageError(String message) {
handleUsageError(message, null);
}

View file

@ -114,7 +114,7 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Djdk.net.hosts.file=${project.build.testOutputDirectory}/hosts_file -XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError --add-opens=java.base/java.security=ALL-UNNAMED</argLine>
<argLine>-Djdk.net.hosts.file=${project.build.testOutputDirectory}/hosts_file -XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError --add-opens=java.base/java.security=ALL-UNNAMED -Djava.util.concurrent.ForkJoinPool.common.threadFactory=io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThreadFactory</argLine>
<systemPropertyVariables>
<kc.quarkus.tests.dist>${kc.quarkus.tests.dist}</kc.quarkus.tests.dist>
</systemPropertyVariables>