[KEYCLOAK-17254] Adaptively add the default modular JVM options

to the "javaVmArguments" to start the cache server container with,
if the JVM used to run the cache server is modular (JDK 9+)

Signed-off-by: Jan Lieskovsky <jlieskov@redhat.com>
This commit is contained in:
Jan Lieskovsky 2021-05-27 18:02:52 +02:00 committed by Marek Posolda
parent de8dd59d66
commit cbd4288205
2 changed files with 81 additions and 11 deletions

View file

@ -46,6 +46,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream; import java.util.stream.Stream;
import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.lessThan;
@ -83,7 +85,7 @@ public class CrossDCTestEnricher {
//if annotation is present on method //if annotation is present on method
InitialDcState annotation = event.getTestMethod().getAnnotation(InitialDcState.class); InitialDcState annotation = event.getTestMethod().getAnnotation(InitialDcState.class);
//annotation not present on method, taking it from class //annotation not present on method, taking it from class
if (annotation == null) { if (annotation == null) {
Class<?> annotatedClass = getNearestSuperclassWithAnnotation(event.getTestClass().getJavaClass(), InitialDcState.class); Class<?> annotatedClass = getNearestSuperclassWithAnnotation(event.getTestClass().getJavaClass(), InitialDcState.class);
@ -156,7 +158,7 @@ public class CrossDCTestEnricher {
forAllBackendNodesInDc(DC.FIRST, CrossDCTestEnricher::startAuthServerBackendNode); forAllBackendNodesInDc(DC.FIRST, CrossDCTestEnricher::startAuthServerBackendNode);
break; break;
} }
suspendPeriodicTasks(); suspendPeriodicTasks();
} }
@ -266,12 +268,59 @@ public class CrossDCTestEnricher {
} }
} }
/* Code to detect if underlying JVM is modular (AKA JDK 9+) taken over from Wildfly Core code base:
* https://github.com/wildfly/wildfly-core/blob/master/launcher/src/main/java/org/wildfly/core/launcher/Jvm.java#L59
* and turned into a function for easier reuse.
*/
public static boolean isModularJvm() {
boolean modularJvm = false;
final String javaSpecVersion = System.getProperty("java.specification.version");
if (javaSpecVersion != null) {
final Matcher matcher = Pattern.compile("^(?:1\\.)?(\\d+)$").matcher(javaSpecVersion);
if (matcher.find()) modularJvm = Integer.parseInt(matcher.group(1)) >= 9;
}
return modularJvm;
}
public static void startCacheServer(DC dc) { public static void startCacheServer(DC dc) {
if (AuthServerTestEnricher.CACHE_SERVER_LIFECYCLE_SKIP) return; if (AuthServerTestEnricher.CACHE_SERVER_LIFECYCLE_SKIP) return;
if (!containerController.get().isStarted(getCacheServer(dc).getQualifier())) { if (!containerController.get().isStarted(getCacheServer(dc).getQualifier())) {
log.infof("--DC: Starting %s", getCacheServer(dc).getQualifier()); log.infof("--DC: Starting %s", getCacheServer(dc).getQualifier());
containerController.get().start(getCacheServer(dc).getQualifier()); // Original config of the cache server container as a map
Map<String, String> containerConfig = getCacheServer(dc).getProperties();
// Start cache server with default modular JVM options set if JDK is modular (JDK 9+)
final String defaultModularJvmOptions = System.getProperty("default.modular.jvm.options");
final String originalJvmArguments = getCacheServer(dc).getProperties().get("javaVmArguments");
/* When JVM used to launch the cache server container is modular, add the default
* modular JVM options to the configuration of the cache server container if
* these aren't present there yet.
*
* See the definition of the 'default.modular.jvm.options' property for details.
*/
if (!originalJvmArguments.contains(defaultModularJvmOptions)) {
if(isModularJvm() && defaultModularJvmOptions != null) {
log.infof("Modular JVM detected. Adding default modular JVM '%s' options to the cache server container's configuration.", defaultModularJvmOptions);
final String lineSeparator = System.getProperty("line.separator");
final String adjustedJvmArguments = originalJvmArguments.replace(lineSeparator, " ") + defaultModularJvmOptions + lineSeparator;
/* Since next time the cache server container might get started using a non-modular
* JVM again, don't store the default modular JVM options into the cache server container's
* configuration permanently (not to need to remove them again later).
*
* Rather, instead of that, retrieve the original cache server container's configuration
* as a map, add the default modular JVM options there, and one-time way start the cache server
* using this custom temporary configuration.
*/
containerConfig.put("javaVmArguments", adjustedJvmArguments);
}
}
/* Finally start the cache server container:
* - Either using the original container config (case of a non-modular JVM),
* - Or using the updated container config (case of a modular JVM)
*/
containerController.get().start(getCacheServer(dc).getQualifier(), containerConfig);
log.infof("--DC: Started %s", getCacheServer(dc).getQualifier()); log.infof("--DC: Started %s", getCacheServer(dc).getQualifier());
} }
} }

View file

@ -134,6 +134,25 @@
<cache.server.2.management.port>12000</cache.server.2.management.port> <cache.server.2.management.port>12000</cache.server.2.management.port>
<cache.server.console.output>true</cache.server.console.output> <cache.server.console.output>true</cache.server.console.output>
<!--
~ Definition of default JVM parameters for all modular JDKs. See:
~
~ https://github.com/wildfly/wildfly-core/blob/master/core-feature-pack/common/src/main/resources/content/bin/common.sh#L19 and
~ https://github.com/wildfly/wildfly-core/blob/master/launcher/src/main/java/org/wildfly/core/launcher/AbstractCommandBuilder.java#L58
~
~ for details. The explanation / purpose of adding a particular modular option is as follows:
~ * add-exports=java.desktop/sun.awt=ALL-UNNAMED Needed by the iiop-openjdk subsystem
~ * add-opens=java.base/java.lang=ALL-UNNAMED Needed if Hibernate applications use Javassist
~ * add-opens=java.base/java.lang.invoke=ALL-UNNAMED Needed by the MicroProfile REST Client subsystem
~ * add-opens=java.base/java.io=ALL-UNNAMED Needed by JBoss Marshalling
~ * add-opens=java.base/java.security=ALL-UNNAMED Needed by WildFly Security Manager
~ * add-opens=java.base/java.util=ALL-UNNAMED Needed for marshalling of enum maps
~ * add-opens=java.management/javax.management=ALL-UNNAMED EE integration with sar mbeans requires deep reflection in javax.management
~ * add-opens=java.naming/javax.naming=ALL-UNNAMED InitialContext proxy generation requires deep reflection in javax.naming
~ * add-modules=java.se Needed for backward compatibility with jboss-modules older than jboss-modules 1.9.1.Final
-->
<default.modular.jvm.options>--add-exports=java.desktop/sun.awt=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-modules=java.se</default.modular.jvm.options>
<dependency.keystore.root>${project.build.directory}/dependency/keystore</dependency.keystore.root> <dependency.keystore.root>${project.build.directory}/dependency/keystore</dependency.keystore.root>
<dependency.truststore>${dependency.keystore.root}/keycloak.truststore</dependency.truststore> <dependency.truststore>${dependency.keystore.root}/keycloak.truststore</dependency.truststore>
<dependency.truststore.password>secret</dependency.truststore.password> <dependency.truststore.password>secret</dependency.truststore.password>
@ -561,6 +580,8 @@
<cli.log.output>${cli.log.output}</cli.log.output> <cli.log.output>${cli.log.output}</cli.log.output>
<test.intermittent>${test.intermittent}</test.intermittent> <test.intermittent>${test.intermittent}</test.intermittent>
<default.modular.jvm.options>${default.modular.jvm.options}</default.modular.jvm.options>
<dependency.keystore.root>${dependency.keystore.root}</dependency.keystore.root> <dependency.keystore.root>${dependency.keystore.root}</dependency.keystore.root>
<dependency.truststore>${dependency.truststore}</dependency.truststore> <dependency.truststore>${dependency.truststore}</dependency.truststore>
<dependency.truststore.password>${dependency.truststore.password}</dependency.truststore.password> <dependency.truststore.password>${dependency.truststore.password}</dependency.truststore.password>
@ -1341,7 +1362,7 @@
<systemPropertyVariables> <systemPropertyVariables>
<run.h2>true</run.h2> <run.h2>true</run.h2>
<docker.database.skip>${docker.database.skip}</docker.database.skip> <docker.database.skip>${docker.database.skip}</docker.database.skip>
<auth.server.jboss>false</auth.server.jboss> <auth.server.jboss>false</auth.server.jboss>
<auth.server.backend1.home>${auth.server.backend1.home}</auth.server.backend1.home> <auth.server.backend1.home>${auth.server.backend1.home}</auth.server.backend1.home>
@ -1615,10 +1636,10 @@
<version>${appium.client.version}</version> <version>${appium.client.version}</version>
</dependency> </dependency>
<!-- <!--
httpclient and httpcore are here to ensure we use the same version httpclient and httpcore are here to ensure we use the same version
as in keycloak/pom.xml and to prevent the other versions beeing present as in keycloak/pom.xml and to prevent the other versions beeing present
on classpath during tests (as a transitive dependencies e.g.). on classpath during tests (as a transitive dependencies e.g.).
There has beeen issues due to this. There has beeen issues due to this.
--> -->
<dependency> <dependency>
@ -1876,16 +1897,16 @@
</profile> </profile>
<profile> <profile>
<id>java11-auth-server</id> <!-- a temporary workaround; TODO remove this once Java 11 is officially supported by Arquillian --> <id>java11-auth-server</id>
<properties> <properties>
<auth.server.jvm.args.extra>--add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED --add-modules=java.se</auth.server.jvm.args.extra> <auth.server.jvm.args.extra>${default.modular.jvm.options}</auth.server.jvm.args.extra>
</properties> </properties>
</profile> </profile>
<profile> <profile>
<id>java11-app-server</id> <!-- a temporary workaround; TODO remove this once Java 11 is officially supported by Arquillian --> <id>java11-app-server</id>
<properties> <properties>
<app.server.jvm.args.extra>--add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED --add-modules=java.se</app.server.jvm.args.extra> <app.server.jvm.args.extra>${default.modular.jvm.options}</app.server.jvm.args.extra>
</properties> </properties>
</profile> </profile>