diff --git a/launcher/src/main/java/org/wildfly/core/launcher/AbstractCommandBuilder.java b/launcher/src/main/java/org/wildfly/core/launcher/AbstractCommandBuilder.java index b0099a520bd..47e856b726b 100644 --- a/launcher/src/main/java/org/wildfly/core/launcher/AbstractCommandBuilder.java +++ b/launcher/src/main/java/org/wildfly/core/launcher/AbstractCommandBuilder.java @@ -8,196 +8,65 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.List; -import org.wildfly.core.launcher.Arguments.Argument; import org.wildfly.core.launcher.logger.LauncherMessages; /** * @author James R. Perkins */ -@SuppressWarnings("unused") -abstract class AbstractCommandBuilder> implements CommandBuilder { +@SuppressWarnings({"unused", "UnusedReturnValue"}) +abstract class AbstractCommandBuilder> extends JBossModulesCommandBuilder implements CommandBuilder { - private static final String ALLOW_VALUE = "allow"; - private static final String DISALLOW_VALUE = "disallow"; static final String HOME_DIR = Environment.HOME_DIR; - static final String SECURITY_MANAGER_ARG = "-secmgr"; - static final String SECURITY_MANAGER_PROP = "java.security.manager"; - static final String SECURITY_MANAGER_PROP_WITH_ALLOW_VALUE = "-D" + SECURITY_MANAGER_PROP + "=" + ALLOW_VALUE; - static final String[] DEFAULT_VM_ARGUMENTS; - /** Packages being exported or opened are available on all JVMs */ - static final Collection DEFAULT_MODULAR_VM_ARGUMENTS; - /** Packages being exported or opened may not be available on all JVMs */ - static final Collection OPTIONAL_DEFAULT_MODULAR_VM_ARGUMENTS; - - static { - // Default JVM parameters for all versions - final Collection javaOpts = new ArrayList<>(); - javaOpts.add("-Xms64m"); - javaOpts.add("-Xmx512m"); - javaOpts.add("-Djava.net.preferIPv4Stack=true"); - javaOpts.add("-Djava.awt.headless=true"); - javaOpts.add("-Djboss.modules.system.pkgs=org.jboss.byteman"); - DEFAULT_VM_ARGUMENTS = javaOpts.toArray(new String[javaOpts.size()]); - - // Default JVM parameters for all modular JDKs - // Additions to these should include good explanations why in the relevant JIRA - // Keep them alphabetical to avoid the code history getting confused by reordering commits - final ArrayList modularJavaOpts = new ArrayList<>(); - final boolean skipJPMSOptions = Boolean.getBoolean("launcher.skip.jpms.properties"); - if (!skipJPMSOptions) { - modularJavaOpts.add("--add-exports=java.desktop/sun.awt=ALL-UNNAMED"); - modularJavaOpts.add("--add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED"); - modularJavaOpts.add("--add-exports=java.naming/com.sun.jndi.url.ldap=ALL-UNNAMED"); - modularJavaOpts.add("--add-exports=java.naming/com.sun.jndi.url.ldaps=ALL-UNNAMED"); - modularJavaOpts.add("--add-exports=jdk.naming.dns/com.sun.jndi.dns=ALL-UNNAMED"); - modularJavaOpts.add("--add-opens=java.base/java.lang=ALL-UNNAMED"); - modularJavaOpts.add("--add-opens=java.base/java.lang.invoke=ALL-UNNAMED"); - modularJavaOpts.add("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED"); - modularJavaOpts.add("--add-opens=java.base/java.io=ALL-UNNAMED"); - modularJavaOpts.add("--add-opens=java.base/java.net=ALL-UNNAMED"); - modularJavaOpts.add("--add-opens=java.base/java.security=ALL-UNNAMED"); - modularJavaOpts.add("--add-opens=java.base/java.util=ALL-UNNAMED"); - modularJavaOpts.add("--add-opens=java.base/java.util.concurrent=ALL-UNNAMED"); - modularJavaOpts.add("--add-opens=java.management/javax.management=ALL-UNNAMED"); - modularJavaOpts.add("--add-opens=java.naming/javax.naming=ALL-UNNAMED"); - // As of jboss-modules 1.9.1.Final the java.se module is no longer required to be added. However as this API is - // designed to work with older versions of the server we still need to add this argument of modular JVM's. - modularJavaOpts.add("--add-modules=java.se"); - } - DEFAULT_MODULAR_VM_ARGUMENTS = Collections.unmodifiableList(modularJavaOpts); - - final ArrayList optionalModularJavaOpts = new ArrayList<>(); - if (!skipJPMSOptions) { - optionalModularJavaOpts.add("--add-opens=java.base/com.sun.net.ssl.internal.ssl=ALL-UNNAMED"); - } - OPTIONAL_DEFAULT_MODULAR_VM_ARGUMENTS = Collections.unmodifiableList(optionalModularJavaOpts); - } - protected final Environment environment; - private boolean useSecMgr; private Path logDir; private Path configDir; - private final Arguments serverArgs; - protected AbstractCommandBuilder(final Path wildflyHome) { - this(wildflyHome, null); + protected AbstractCommandBuilder(final Path wildflyHome, final String moduleName) { + this(wildflyHome, null, moduleName); } - protected AbstractCommandBuilder(final Path wildflyHome, final Jvm jvm) { - environment = new Environment(wildflyHome).setJvm(jvm); - useSecMgr = false; - serverArgs = new Arguments(); + protected AbstractCommandBuilder(final Path wildflyHome, final Jvm jvm, final String moduleName) { + super(wildflyHome, jvm, moduleName); } - /** - * Sets whether or not the security manager option, {@code -secmgr}, should be used. - * - * @param useSecMgr {@code true} to use the a security manager, otherwise {@code false} - * - * @return the builder - */ + @Override public T setUseSecurityManager(final boolean useSecMgr) { - this.useSecMgr = useSecMgr; + super.setUseSecurityManager(useSecMgr); return getThis(); } - /** - * Indicates whether or no a security manager should be used for the server launched. - * - * @return {@code true} if a security manager should be used, otherwise {@code false} - */ - public boolean useSecurityManager() { - return useSecMgr; - } - - /** - * Adds a directory to the collection of module paths. - * - * @param moduleDir the module directory to add - * - * @return the builder - * - * @throws java.lang.IllegalArgumentException if the path is {@code null} - */ + @Override public T addModuleDir(final String moduleDir) { - environment.addModuleDir(moduleDir); + super.addModuleDir(moduleDir); return getThis(); } - /** - * Adds all the module directories to the collection of module paths. - * - * @param moduleDirs an array of module paths to add - * - * @return the builder - * - * @throws java.lang.IllegalArgumentException if any of the module paths are invalid or {@code null} - */ + @Override public T addModuleDirs(final String... moduleDirs) { - environment.addModuleDirs(moduleDirs); + super.addModuleDirs(moduleDirs); return getThis(); } - /** - * Adds all the module directories to the collection of module paths. - * - * @param moduleDirs a collection of module paths to add - * - * @return the builder - * - * @throws java.lang.IllegalArgumentException if any of the module paths are invalid or {@code null} - */ + @Override public T addModuleDirs(final Iterable moduleDirs) { - environment.addModuleDirs(moduleDirs); + super.addModuleDirs(moduleDirs); return getThis(); } - /** - * Replaces any previously set module directories with the collection of module directories. - *

- * The default module directory will NOT be used if this method is invoked. - * - * @param moduleDirs the collection of module directories to use - * - * @return the builder - * - * @throws java.lang.IllegalArgumentException if any of the module paths are invalid or {@code null} - */ + @Override public T setModuleDirs(final Iterable moduleDirs) { - environment.setModuleDirs(moduleDirs); + super.setModuleDirs(moduleDirs); return getThis(); } - /** - * Replaces any previously set module directories with the array of module directories. - *

- * The default module directory will NOT be used if this method is invoked. - * - * @param moduleDirs the array of module directories to use - * - * @return the builder - * - * @throws java.lang.IllegalArgumentException if any of the module paths are invalid or {@code null} - */ + @Override public T setModuleDirs(final String... moduleDirs) { - environment.setModuleDirs(moduleDirs); + super.setModuleDirs(moduleDirs); return getThis(); } - /** - * Returns the modules paths used on the command line. - * - * @return the paths separated by the {@link java.io.File#pathSeparator path separator} - */ - public String getModulePaths() { - return environment.getModulePaths(); - } - /** * Returns the log directory for the server. * @@ -276,7 +145,7 @@ public Path getConfigurationDirectory() { * @throws java.lang.IllegalArgumentException if the directory is not a valid directory */ public T setConfigurationDirectory(final String path) { - configDir = validateAndNormalizeDir(path, true); + configDir = Environment.validateAndNormalizeDir(path, true); return getThis(); } @@ -292,62 +161,25 @@ public T setConfigurationDirectory(final String path) { * @throws java.lang.IllegalArgumentException if the directory is not a valid directory */ public T setConfigurationDirectory(final Path path) { - configDir = validateAndNormalizeDir(path, true); + configDir = Environment.validateAndNormalizeDir(path, true); return getThis(); } - /** - * Adds an argument to be passed to the server ignore the argument if {@code null}. - * - * @param arg the argument to pass - * - * @return the builder - */ + @Override public T addServerArgument(final String arg) { - if (arg != null) { - final Argument argument = Arguments.parse(arg); - if (addServerArgument(argument)) { - if (SECURITY_MANAGER_ARG.equals(arg)) { - setUseSecurityManager(true); - } else { - serverArgs.add(argument); - } - } - } + super.addServerArgument(arg); return getThis(); } - /** - * Adds the arguments to the collection of arguments that will be passed to the server ignoring any {@code null} - * arguments. - * - * @param args the arguments to add - * - * @return the builder - */ + @Override public T addServerArguments(final String... args) { - if (args != null) { - for (String arg : args) { - addServerArgument(arg); - } - } + super.addServerArguments(args); return getThis(); } - /** - * Adds the arguments to the collection of arguments that will be passed to the server ignoring any {@code null} - * arguments. - * - * @param args the arguments to add - * - * @return the builder - */ + @Override public T addServerArguments(final Iterable args) { - if (args != null) { - for (String arg : args) { - addServerArgument(arg); - } - } + super.addServerArguments(args); return getThis(); } @@ -504,15 +336,6 @@ public T setPropertiesFile(final Path file) { return addPropertiesFile(file); } - /** - * Returns the home directory used. - * - * @return the home directory - */ - public Path getWildFlyHome() { - return environment.getWildflyHome(); - } - /** * Returns the Java home directory where the java executable command can be found. *

@@ -529,6 +352,7 @@ public Path getWildFlyHome() { * * @return the command line argument */ + @SuppressWarnings("SameParameterValue") protected String getLoggingPropertiesArgument(final String fileName) { return "-Dlogging.configuration=file:" + normalizePath(getConfigurationDirectory(), fileName); } @@ -547,15 +371,6 @@ protected String getBootLogArgument(final String fileName) { return "-Dorg.jboss.boot.log.file=" + normalizePath(getLogDirectory(), fileName); } - /** - * Returns the normalized path to the {@code jboss-modules.jar} for launching the server. - * - * @return the path to {@code jboss-modules.jar} - */ - public String getModulesJarName() { - return environment.getModuleJar().toString(); - } - /** * Resolves the path to the path relative to the WildFly home directory. @@ -590,15 +405,6 @@ protected Path normalizePath(final Path parent, final String path) { */ public abstract Path getBaseDirectory(); - /** - * A collection of server command line arguments. - * - * @return the server arguments - */ - public List getServerArguments() { - return serverArgs.asList(); - } - /** * Returns the concrete builder. * @@ -606,62 +412,9 @@ public List getServerArguments() { */ protected abstract T getThis(); - protected void setSingleServerArg(final String key, final String value) { - serverArgs.set(key, value); - } - - protected void addServerArg(final String key, final String value) { - serverArgs.add(key, value); - } - - /** - * Returns the first argument from the server arguments. - * - * @param key the key to the argument - * - * @return the first argument or {@code null} - */ - protected String getServerArg(final String key) { - return serverArgs.get(key); - } - - /** - * Allows the subclass to do additional checking on whether the server argument should be added or not. In some - * cases it may be desirable for the argument passed to be added or processed elsewhere. - * - * @param argument the argument to test - * - * @return {@code true} if the argument should be added to the server arguments, {@code false} if the argument is - * handled by the subclass - */ - abstract boolean addServerArgument(final Argument argument); - - protected static Path validateAndNormalizeDir(final String path, final boolean allowNull) { - if (path == null) { - if (allowNull) return null; - throw LauncherMessages.MESSAGES.pathDoesNotExist(null); - } - return validateAndNormalizeDir(Paths.get(path), allowNull); - } - - protected static Path validateAndNormalizeDir(final Path path, final boolean allowNull) { - if (allowNull && path == null) return null; - if (path == null || Files.notExists(path)) { - throw LauncherMessages.MESSAGES.pathDoesNotExist(path); - } - if (!Files.isDirectory(path)) { - throw LauncherMessages.MESSAGES.invalidDirectory(path); - } - return path.toAbsolutePath().normalize(); - } - protected static void addSystemPropertyArg(final List cmd, final String key, final Object value) { if (value != null) { cmd.add("-D" + key + "=" + value); } } - - protected static boolean isJavaSecurityManagerConfigured(final Argument argument) { - return !ALLOW_VALUE.equals(argument.getValue()) && !DISALLOW_VALUE.equals(argument.getValue()); - } } diff --git a/launcher/src/main/java/org/wildfly/core/launcher/Arguments.java b/launcher/src/main/java/org/wildfly/core/launcher/Arguments.java index fdc6e7ffd6b..67c31ea63e3 100644 --- a/launcher/src/main/java/org/wildfly/core/launcher/Arguments.java +++ b/launcher/src/main/java/org/wildfly/core/launcher/Arguments.java @@ -191,11 +191,7 @@ public List asList() { void add(final Argument argument) { if (argument != null) { if (argument.multipleValuesAllowed()) { - Collection arguments = map.get(argument.getKey()); - if (arguments == null) { - arguments = new ArrayList<>(); - map.put(argument.getKey(), arguments); - } + final Collection arguments = map.computeIfAbsent(argument.getKey(), k -> new ArrayList<>()); arguments.add(argument); } else { set(argument); diff --git a/launcher/src/main/java/org/wildfly/core/launcher/BootableJarCommandBuilder.java b/launcher/src/main/java/org/wildfly/core/launcher/BootableJarCommandBuilder.java index 856adc1670b..9eaaef962f6 100644 --- a/launcher/src/main/java/org/wildfly/core/launcher/BootableJarCommandBuilder.java +++ b/launcher/src/main/java/org/wildfly/core/launcher/BootableJarCommandBuilder.java @@ -10,7 +10,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import static org.wildfly.core.launcher.AbstractCommandBuilder.DEFAULT_VM_ARGUMENTS; +import static org.wildfly.core.launcher.JBossModulesCommandBuilder.DEFAULT_VM_ARGUMENTS; import org.wildfly.core.launcher.Arguments.Argument; import static org.wildfly.core.launcher.StandaloneCommandBuilder.DEBUG_FORMAT; @@ -23,7 +23,7 @@ * * @author JF Denise */ -@SuppressWarnings("unused") +@SuppressWarnings({"unused", "UnusedReturnValue"}) public class BootableJarCommandBuilder implements CommandBuilder { private final Arguments javaOpts; @@ -430,10 +430,9 @@ public BootableJarCommandBuilder addSecurityProperties(final Map @Override public List buildArguments() { - final List cmd = new ArrayList<>(); - cmd.addAll(getJavaOptions()); + final List cmd = new ArrayList<>(getJavaOptions()); if (jvm.enhancedSecurityManagerAvailable()) { - cmd.add(AbstractCommandBuilder.SECURITY_MANAGER_PROP_WITH_ALLOW_VALUE); + cmd.add(JBossModulesCommandBuilder.SECURITY_MANAGER_PROP_WITH_ALLOW_VALUE); } if (modulesLocklessArg != null) { cmd.add(modulesLocklessArg); diff --git a/launcher/src/main/java/org/wildfly/core/launcher/CliCommandBuilder.java b/launcher/src/main/java/org/wildfly/core/launcher/CliCommandBuilder.java index c76eef5afe2..20f0ce3257f 100644 --- a/launcher/src/main/java/org/wildfly/core/launcher/CliCommandBuilder.java +++ b/launcher/src/main/java/org/wildfly/core/launcher/CliCommandBuilder.java @@ -27,10 +27,10 @@ * * @author James R. Perkins */ -@SuppressWarnings("unused") +@SuppressWarnings({"unused", "UnusedReturnValue"}) public class CliCommandBuilder implements CommandBuilder { - private static Path cliClientJar = Paths.get("bin").resolve("client").resolve("jboss-cli-client.jar"); + private static final Path cliClientJar = Paths.get("bin").resolve("client").resolve("jboss-cli-client.jar"); enum CliArgument { CONNECT("--connect", "-c"), @@ -652,20 +652,19 @@ public Path getJavaHome() { @Override public List buildArguments() { - final List cmd = new ArrayList<>(); - cmd.addAll(getJavaOptions()); + final List cmd = new ArrayList<>(getJavaOptions()); if (modularLauncher) { if (environment.getJvm().isModular()) { - cmd.addAll(AbstractCommandBuilder.DEFAULT_MODULAR_VM_ARGUMENTS); - for (final String optionalModularArgument : AbstractCommandBuilder.OPTIONAL_DEFAULT_MODULAR_VM_ARGUMENTS) { - if (environment.getJvm().isPackageAvailable(environment.getJvm().getPath(), optionalModularArgument)) { + cmd.addAll(JBossModulesCommandBuilder.DEFAULT_MODULAR_VM_ARGUMENTS); + for (final String optionalModularArgument : JBossModulesCommandBuilder.OPTIONAL_DEFAULT_MODULAR_VM_ARGUMENTS) { + if (Jvm.isPackageAvailable(environment.getJvm().getPath(), optionalModularArgument)) { cmd.add(optionalModularArgument); } } } } if (environment.getJvm().enhancedSecurityManagerAvailable()) { - cmd.add(AbstractCommandBuilder.SECURITY_MANAGER_PROP_WITH_ALLOW_VALUE); + cmd.add(JBossModulesCommandBuilder.SECURITY_MANAGER_PROP_WITH_ALLOW_VALUE); } cmd.add("-jar"); diff --git a/launcher/src/main/java/org/wildfly/core/launcher/DomainCommandBuilder.java b/launcher/src/main/java/org/wildfly/core/launcher/DomainCommandBuilder.java index 00337ce87ac..b95f4bdbcf4 100644 --- a/launcher/src/main/java/org/wildfly/core/launcher/DomainCommandBuilder.java +++ b/launcher/src/main/java/org/wildfly/core/launcher/DomainCommandBuilder.java @@ -22,9 +22,10 @@ * * @author James R. Perkins */ -@SuppressWarnings("unused") +@SuppressWarnings({"unused", "UnusedReturnValue"}) public class DomainCommandBuilder extends AbstractCommandBuilder implements CommandBuilder { + private static final String MODULE_NAME = "org.jboss.as.process-controller"; private static final String DOMAIN_BASE_DIR = "jboss.domain.base.dir"; private static final String DOMAIN_CONFIG_DIR = "jboss.domain.config.dir"; private static final String DOMAIN_LOG_DIR = "jboss.domain.log.dir"; @@ -45,7 +46,7 @@ public class DomainCommandBuilder extends AbstractCommandBuilder buildArguments() { if (environment.getJvm().isModular()) { cmd.addAll(DEFAULT_MODULAR_VM_ARGUMENTS); for (final String optionalModularArgument : OPTIONAL_DEFAULT_MODULAR_VM_ARGUMENTS) { - if (environment.getJvm().isPackageAvailable(environment.getJvm().getPath(), optionalModularArgument)) { + if (Jvm.isPackageAvailable(environment.getJvm().getPath(), optionalModularArgument)) { cmd.add(optionalModularArgument); } } @@ -729,7 +730,7 @@ public List buildArguments() { } cmd.add("-mp"); cmd.add(getModulePaths()); - cmd.add("org.jboss.as.process-controller"); + cmd.add(MODULE_NAME); cmd.add("-jboss-home"); cmd.add(getWildFlyHome().toString()); cmd.add("-jvm"); @@ -747,7 +748,7 @@ public List buildArguments() { if (hostControllerJvm.isModular()) { cmd.addAll(DEFAULT_MODULAR_VM_ARGUMENTS); for (final String optionalModularArgument : OPTIONAL_DEFAULT_MODULAR_VM_ARGUMENTS) { - if (environment.getJvm().isPackageAvailable(environment.getJvm().getPath(), optionalModularArgument)) { + if (Jvm.isPackageAvailable(environment.getJvm().getPath(), optionalModularArgument)) { cmd.add(optionalModularArgument); } } @@ -767,14 +768,6 @@ public List buildArguments() { return cmd; } - @Override - public List build() { - final List cmd = new ArrayList<>(); - cmd.add(environment.getJvm().getCommand()); - cmd.addAll(buildArguments()); - return cmd; - } - @Override public Path getJavaHome() { return environment.getJvm().getPath(); diff --git a/launcher/src/main/java/org/wildfly/core/launcher/Environment.java b/launcher/src/main/java/org/wildfly/core/launcher/Environment.java index 16c0b5170e5..26b19ae55ed 100644 --- a/launcher/src/main/java/org/wildfly/core/launcher/Environment.java +++ b/launcher/src/main/java/org/wildfly/core/launcher/Environment.java @@ -231,8 +231,7 @@ static Path validateJar(final Path jarPath) { if (Files.isDirectory(jarPath)) { throw LauncherMessages.MESSAGES.pathNotAFile(jarPath); } - final Path result = jarPath.toAbsolutePath().normalize(); - return result; + return jarPath.toAbsolutePath().normalize(); } static Path validateJar(final String jarPath) { @@ -241,4 +240,24 @@ static Path validateJar(final String jarPath) { } return validateJar(Paths.get(jarPath)); } + + @SuppressWarnings("SameParameterValue") + static Path validateAndNormalizeDir(final String path, final boolean allowNull) { + if (path == null) { + if (allowNull) return null; + throw LauncherMessages.MESSAGES.pathDoesNotExist(null); + } + return validateAndNormalizeDir(Paths.get(path), allowNull); + } + + static Path validateAndNormalizeDir(final Path path, final boolean allowNull) { + if (allowNull && path == null) return null; + if (path == null || Files.notExists(path)) { + throw LauncherMessages.MESSAGES.pathDoesNotExist(path); + } + if (!Files.isDirectory(path)) { + throw LauncherMessages.MESSAGES.invalidDirectory(path); + } + return path.toAbsolutePath().normalize(); + } } diff --git a/launcher/src/main/java/org/wildfly/core/launcher/JBossModulesCommandBuilder.java b/launcher/src/main/java/org/wildfly/core/launcher/JBossModulesCommandBuilder.java new file mode 100644 index 00000000000..32e519f442d --- /dev/null +++ b/launcher/src/main/java/org/wildfly/core/launcher/JBossModulesCommandBuilder.java @@ -0,0 +1,695 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ +package org.wildfly.core.launcher; + +import static org.wildfly.core.launcher.logger.LauncherMessages.MESSAGES; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.wildfly.core.launcher.Arguments.Argument; + +/** + * Builds a list of commands used to launch JBoss Modules module. + *

+ * This builder is not thread safe and the same instance should not be used in multiple threads. + * + * @author James R. Perkins + */ +@SuppressWarnings({"unused", "UnusedReturnValue"}) +public class JBossModulesCommandBuilder implements CommandBuilder { + + static final String[] DEFAULT_VM_ARGUMENTS = { + "-Xms64m", + "-Xmx512m", + "-Djava.net.preferIPv4Stack=true", + "-Djava.awt.headless=true", + "-Djboss.modules.system.pkgs=org.jboss.byteman" + }; + /** + * Packages being exported or opened are available on all JVMs + */ + static final Collection DEFAULT_MODULAR_VM_ARGUMENTS; + /** + * Packages being exported or opened may not be available on all JVMs + */ + static final Collection OPTIONAL_DEFAULT_MODULAR_VM_ARGUMENTS; + private static final String ALLOW_VALUE = "allow"; + private static final String DISALLOW_VALUE = "disallow"; + static final String SECURITY_MANAGER_ARG = "-secmgr"; + static final String SECURITY_MANAGER_PROP = "java.security.manager"; + static final String SECURITY_MANAGER_PROP_WITH_ALLOW_VALUE = "-D" + SECURITY_MANAGER_PROP + "=" + ALLOW_VALUE; + + static { + // Default JVM parameters for all modular JDKs + // Additions to these should include good explanations why in the relevant JIRA + // Keep them alphabetical to avoid the code history getting confused by reordering commits + final List modularJavaOpts; + final boolean skipJPMSOptions = Boolean.getBoolean("launcher.skip.jpms.properties"); + if (!skipJPMSOptions) { + modularJavaOpts = List.of( + "--add-exports=java.desktop/sun.awt=ALL-UNNAMED", + "--add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED", + "--add-exports=java.naming/com.sun.jndi.url.ldap=ALL-UNNAMED", + "--add-exports=java.naming/com.sun.jndi.url.ldaps=ALL-UNNAMED", + "--add-exports=jdk.naming.dns/com.sun.jndi.dns=ALL-UNNAMED", + "--add-opens=java.base/java.lang=ALL-UNNAMED", + "--add-opens=java.base/java.lang.invoke=ALL-UNNAMED", + "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", + "--add-opens=java.base/java.io=ALL-UNNAMED", + "--add-opens=java.base/java.net=ALL-UNNAMED", + "--add-opens=java.base/java.security=ALL-UNNAMED", + "--add-opens=java.base/java.util=ALL-UNNAMED", + "--add-opens=java.base/java.util.concurrent=ALL-UNNAMED", + "--add-opens=java.management/javax.management=ALL-UNNAMED", + "--add-opens=java.naming/javax.naming=ALL-UNNAMED", + // As of jboss-modules 1.9.1.Final the java.se module is no longer required to be added. However as this API is + // designed to work with older versions of the server we still need to add this argument of modular JVM's. + "--add-modules=java.se" + ); + } else { + modularJavaOpts = List.of(); + } + DEFAULT_MODULAR_VM_ARGUMENTS = modularJavaOpts; + + final List optionalModularJavaOpts; + if (!skipJPMSOptions) { + optionalModularJavaOpts = List.of(("--add-opens=java.base/com.sun.net.ssl.internal.ssl=ALL-UNNAMED")); + } else { + optionalModularJavaOpts = List.of(); + } + OPTIONAL_DEFAULT_MODULAR_VM_ARGUMENTS = optionalModularJavaOpts; + } + + final Environment environment; + final Arguments serverArgs; + final Arguments javaOpts; + private final String moduleName; + private String modulesLocklessArg; + private String modulesMetricsArg; + private boolean useSecMgr; + private boolean addModuleAgent; + private final Collection moduleOpts; + + /** + * Creates a command builder for a launching JBoss Modules module. + *

+ * Note the {@code wildflyHome} and {@code javaHome} are not validated using the constructor. The static {@link + * #of(Path, String)} is preferred. + * + * @param wildflyHome the path to WildFly, cannot be {@code null} + * @param moduleName the name of the entry module, cannot be {@code null} + */ + protected JBossModulesCommandBuilder(final Path wildflyHome, final String moduleName) { + this(wildflyHome, null, moduleName); + } + + /** + * Creates a command builder for a launching JBoss Modules module. + *

+ * Note the {@code wildflyHome} and {@code javaHome} are not validated using the constructor. The static {@link + * #of(Path, String)} is preferred. + * + * @param wildflyHome the path to WildFly, cannot be {@code null} + * @param jvm the JVM used for the command builder, {@code null} will use the current JVM + * @param moduleName the name of the entry module, cannot be {@code null} + */ + JBossModulesCommandBuilder(final Path wildflyHome, final Jvm jvm, final String moduleName) { + if (wildflyHome == null) { + throw MESSAGES.nullParam("wildflyHome"); + } + if (moduleName == null) { + throw MESSAGES.nullParam("moduleName"); + } + this.environment = new Environment(wildflyHome).setJvm(jvm); + this.serverArgs = new Arguments(); + this.moduleName = moduleName; + javaOpts = new Arguments(); + moduleOpts = new ArrayList<>(); + addModuleAgent = false; + } + + /** + * Creates a command builder for a launching JBoss Modules module. + * + * @param wildflyHome the path to the WildFly home directory, cannot be {@code null} + * @param moduleName the name of the entry module, cannot be {@code null} + * + * @return a new builder + */ + public static JBossModulesCommandBuilder of(final Path wildflyHome, final String moduleName) { + if (moduleName == null) { + throw MESSAGES.nullParam("moduleName"); + } + return new JBossModulesCommandBuilder(Environment.validateWildFlyDir(wildflyHome), moduleName); + } + + /** + * Creates a command builder for a launching JBoss Modules module. + * + * @param wildflyHome the path to the WildFly home directory, cannot be {@code null} + * @param moduleName the name of the entry module, cannot be {@code null} + * + * @return a new builder + */ + public static JBossModulesCommandBuilder of(final String wildflyHome, final String moduleName) { + if (moduleName == null) { + throw MESSAGES.nullParam("moduleName"); + } + return new JBossModulesCommandBuilder(Environment.validateWildFlyDir(wildflyHome), moduleName); + } + + + /** + * Sets whether or not the security manager option, {@code -secmgr}, should be used. + * + * @param useSecMgr {@code true} to use the a security manager, otherwise {@code false} + * + * @return the builder + */ + public JBossModulesCommandBuilder setUseSecurityManager(final boolean useSecMgr) { + this.useSecMgr = useSecMgr; + return this; + } + + /** + * Indicates whether or no a security manager should be used for the server launched. + * + * @return {@code true} if a security manager should be used, otherwise {@code false} + */ + public boolean useSecurityManager() { + return useSecMgr; + } + + /** + * Adds a directory to the collection of module paths. + * + * @param moduleDir the module directory to add + * + * @return the builder + * + * @throws java.lang.IllegalArgumentException if the path is {@code null} + */ + public JBossModulesCommandBuilder addModuleDir(final String moduleDir) { + environment.addModuleDir(moduleDir); + return this; + } + + /** + * Adds all the module directories to the collection of module paths. + * + * @param moduleDirs an array of module paths to add + * + * @return the builder + * + * @throws java.lang.IllegalArgumentException if any of the module paths are invalid or {@code null} + */ + public JBossModulesCommandBuilder addModuleDirs(final String... moduleDirs) { + environment.addModuleDirs(moduleDirs); + return this; + } + + /** + * Adds all the module directories to the collection of module paths. + * + * @param moduleDirs a collection of module paths to add + * + * @return the builder + * + * @throws java.lang.IllegalArgumentException if any of the module paths are invalid or {@code null} + */ + public JBossModulesCommandBuilder addModuleDirs(final Iterable moduleDirs) { + environment.addModuleDirs(moduleDirs); + return this; + } + + /** + * Replaces any previously set module directories with the collection of module directories. + *

+ * The default module directory will NOT be used if this method is invoked. + * + * @param moduleDirs the collection of module directories to use + * + * @return the builder + * + * @throws java.lang.IllegalArgumentException if any of the module paths are invalid or {@code null} + */ + public JBossModulesCommandBuilder setModuleDirs(final Iterable moduleDirs) { + environment.setModuleDirs(moduleDirs); + return this; + } + + /** + * Replaces any previously set module directories with the array of module directories. + *

+ * The default module directory will NOT be used if this method is invoked. + * + * @param moduleDirs the array of module directories to use + * + * @return the builder + * + * @throws java.lang.IllegalArgumentException if any of the module paths are invalid or {@code null} + */ + public JBossModulesCommandBuilder setModuleDirs(final String... moduleDirs) { + environment.setModuleDirs(moduleDirs); + return this; + } + + /** + * Returns the modules paths used on the command line. + * + * @return the paths separated by the {@link java.io.File#pathSeparator path separator} + */ + public String getModulePaths() { + return environment.getModulePaths(); + } + + /** + * Adds an argument to be passed to the server ignore the argument if {@code null}. + * + * @param arg the argument to pass + * + * @return the builder + */ + public JBossModulesCommandBuilder addServerArgument(final String arg) { + if (arg != null) { + final Argument argument = Arguments.parse(arg); + if (addServerArgument(argument)) { + if (SECURITY_MANAGER_ARG.equals(arg)) { + setUseSecurityManager(true); + } else { + serverArgs.add(argument); + } + } + } + return this; + } + + /** + * Adds the arguments to the collection of arguments that will be passed to the server ignoring any {@code null} + * arguments. + * + * @param args the arguments to add + * + * @return the builder + */ + public JBossModulesCommandBuilder addServerArguments(final String... args) { + if (args != null) { + for (String arg : args) { + addServerArgument(arg); + } + } + return this; + } + + /** + * Adds the arguments to the collection of arguments that will be passed to the server ignoring any {@code null} + * arguments. + * + * @param args the arguments to add + * + * @return the builder + */ + public JBossModulesCommandBuilder addServerArguments(final Iterable args) { + if (args != null) { + for (String arg : args) { + addServerArgument(arg); + } + } + return this; + } + + /** + * Returns the home directory used. + * + * @return the home directory + */ + public Path getWildFlyHome() { + return environment.getWildflyHome(); + } + + /** + * Returns the Java home directory where the java executable command can be found. + *

+ * If the directory was not set the system property value, {@code java.home}, should be used. + * + * @return the path to the Java home directory + */ + public Path getJavaHome() { + return environment.getWildflyHome(); + } + + /** + * Returns the normalized path to the {@code jboss-modules.jar} for launching the server. + * + * @return the path to {@code jboss-modules.jar} + */ + public String getModulesJarName() { + return environment.getModuleJar().toString(); + } + + /** + * A collection of server command line arguments. + * + * @return the server arguments + */ + public List getServerArguments() { + return serverArgs.asList(); + } + + /** + * Adds a JVM argument to the command ignoring {@code null} arguments. + * + * @param jvmArg the JVM argument to add + * + * @return the builder + */ + public JBossModulesCommandBuilder addJavaOption(final String jvmArg) { + if (jvmArg != null && !jvmArg.isBlank()) { + final Argument argument = Arguments.parse(jvmArg); + if (argument.getKey().equals(SECURITY_MANAGER_PROP)) { + // [WFCORE-5778] java.security.manager system property with value "allow" detected. + // It doesn't mean SM is going to be installed but it indicates SM can be installed dynamically. + setUseSecurityManager(isJavaSecurityManagerConfigured(argument)); + } else { + javaOpts.add(argument); + } + } + return this; + } + + /** + * Adds the array of JVM arguments to the command. + * + * @param javaOpts the array of JVM arguments to add, {@code null} arguments are ignored + * + * @return the builder + */ + public JBossModulesCommandBuilder addJavaOptions(final String... javaOpts) { + if (javaOpts != null) { + for (String javaOpt : javaOpts) { + addJavaOption(javaOpt); + } + } + return this; + } + + /** + * Adds the collection of JVM arguments to the command. + * + * @param javaOpts the collection of JVM arguments to add, {@code null} arguments are ignored + * + * @return the builder + */ + public JBossModulesCommandBuilder addJavaOptions(final Iterable javaOpts) { + if (javaOpts != null) { + for (String javaOpt : javaOpts) { + addJavaOption(javaOpt); + } + } + return this; + } + + /** + * Sets the JVM arguments to use. This overrides any default JVM arguments that would normally be added and ignores + * {@code null} values in the collection. + *

+ * If the collection is {@code null} the JVM arguments will be cleared and no new arguments will be added. + * + * @param javaOpts the JVM arguments to use + * + * @return the builder + */ + public JBossModulesCommandBuilder setJavaOptions(final Iterable javaOpts) { + this.javaOpts.clear(); + return addJavaOptions(javaOpts); + } + + /** + * Sets the JVM arguments to use. This overrides any default JVM arguments that would normally be added and ignores + * {@code null} values in the array. + *

+ * If the array is {@code null} the JVM arguments will be cleared and no new arguments will be added. + * + * @param javaOpts the JVM arguments to use + * + * @return the builder + */ + public JBossModulesCommandBuilder setJavaOptions(final String... javaOpts) { + this.javaOpts.clear(); + return addJavaOptions(javaOpts); + } + + /** + * Returns the JVM arguments. + * + * @return the JVM arguments + */ + public List getJavaOptions() { + return javaOpts.asList(); + } + + /** + * Adds an option which will be passed to JBoss Modules. + *

+ * Note that {@code -mp} or {@code --modulepath} is not supported. Use {@link #addModuleDir(String)} to add a module + * directory. + *

+ * + * @param arg the argument to add + * + * @return the builder + */ + public JBossModulesCommandBuilder addModuleOption(final String arg) { + if (arg != null) { + if (arg.startsWith("-javaagent:")) { + addModuleAgent = true; + } else if ("-mp".equals(arg) || "--modulepath".equals(arg)) { + throw MESSAGES.invalidArgument(arg, "addModuleOption"); + } else if ("-secmgr".equals(arg)) { + throw MESSAGES.invalidArgument(arg, "setUseSecurityManager"); + } + moduleOpts.add(arg); + } + return this; + } + + /** + * Adds the options which will be passed to JBoss Modules. + *

+ * Note that {@code -mp} or {@code --modulepath} is not supported. Use {@link #addModuleDirs(String...)} to add a + * module directory. + *

+ * + * @param args the argument to add + * + * @return the builder + */ + public JBossModulesCommandBuilder addModuleOptions(final String... args) { + if (args != null) { + for (String arg : args) { + addModuleOption(arg); + } + } + return this; + } + + /** + * Adds the options which will be passed to JBoss Modules. + *

+ * Note that {@code -mp} or {@code --modulepath} is not supported. Use {@link #addModuleDirs(Iterable)} to add a + * module directory. + *

+ * + * @param args the argument to add + * + * @return the builder + */ + public JBossModulesCommandBuilder addModuleOptions(final Iterable args) { + if (args != null) { + for (String arg : args) { + addModuleOption(arg); + } + } + return this; + } + + /** + * Clears the current module options and adds the options which will be passed to JBoss Modules. + *

+ * Note that {@code -mp} or {@code --modulepath} is not supported. Use {@link #addModuleDirs(String...)} to add a + * module directory. + *

+ * + * @param args the argument to use + * + * @return the builder + */ + public JBossModulesCommandBuilder setModuleOptions(final String... args) { + moduleOpts.clear(); + addModuleOptions(args); + return this; + } + + /** + * Clears the current module options and adds the options which will be passed to JBoss Modules. + *

+ * Note that {@code -mp} or {@code --modulepath} is not supported. Use {@link #addModuleDirs(Iterable)} to add a + * module directory. + *

+ * + * @param args the argument to use + * + * @return the builder + */ + public JBossModulesCommandBuilder setModuleOptions(final Iterable args) { + moduleOpts.clear(); + addModuleOptions(args); + return this; + } + + /** + * Sets the Java home where the Java executable can be found. + * + * @param javaHome the Java home or {@code null} to use te system property {@code java.home} + * + * @return the builder + */ + public JBossModulesCommandBuilder setJavaHome(final String javaHome) { + environment.setJvm(Jvm.of(javaHome)); + return this; + } + + /** + * Sets the Java home where the Java executable can be found. + * + * @param javaHome the Java home or {@code null} to use te system property {@code java.home} + * + * @return the builder + */ + public JBossModulesCommandBuilder setJavaHome(final Path javaHome) { + environment.setJvm(Jvm.of(javaHome)); + return this; + } + + /** + * Set to {@code true} to use JBoss Modules lockless mode. + * + * @param b {@code true} to use lockless mode + * + * @return the builder + */ + public JBossModulesCommandBuilder setModulesLockless(final boolean b) { + if (b) { + modulesLocklessArg = "-Djboss.modules.lockless=true"; + } else { + modulesLocklessArg = null; + } + return this; + } + + /** + * Set to {@code true} to gather metrics for JBoss Modules. + * + * @param b {@code true} to gather metrics for JBoss Modules. + * + * @return this builder + */ + public JBossModulesCommandBuilder setModulesMetrics(final boolean b) { + if (b) { + modulesMetricsArg = "-Djboss.modules.metrics=true"; + } else { + modulesMetricsArg = null; + } + return this; + } + + @Override + public List buildArguments() { + final List cmd = new ArrayList<>(); + // Check to see if an agent was added as a module option, if so we want to add JBoss Modules as an agent. + if (addModuleAgent) { + cmd.add("-javaagent:" + getModulesJarName()); + } + cmd.addAll(getJavaOptions()); + if (environment.getJvm().isModular()) { + cmd.addAll(DEFAULT_MODULAR_VM_ARGUMENTS); + for (final String optionalModularArgument : OPTIONAL_DEFAULT_MODULAR_VM_ARGUMENTS) { + if (Jvm.isPackageAvailable(environment.getJvm().getPath(), optionalModularArgument)) { + cmd.add(optionalModularArgument); + } + } + } + if (environment.getJvm().enhancedSecurityManagerAvailable()) { + cmd.add(SECURITY_MANAGER_PROP_WITH_ALLOW_VALUE); + } + // Add these to JVM level system properties + if (modulesLocklessArg != null) { + cmd.add(modulesLocklessArg); + } + if (modulesMetricsArg != null) { + cmd.add(modulesMetricsArg); + } + cmd.add("-jar"); + cmd.add(getModulesJarName()); + if (useSecurityManager()) { + cmd.add(SECURITY_MANAGER_ARG); + } + // Add the agent argument for jboss-modules + cmd.addAll(moduleOpts); + cmd.add("-mp"); + cmd.add(getModulePaths()); + cmd.add(moduleName); + + cmd.addAll(getServerArguments()); + return cmd; + } + + @Override + public List build() { + final List cmd = new ArrayList<>(); + cmd.add(environment.getJvm().getCommand()); + cmd.addAll(buildArguments()); + return cmd; + } + + protected void setSingleServerArg(final String key, final String value) { + serverArgs.set(key, value); + } + + protected void addServerArg(final String key, final String value) { + serverArgs.add(key, value); + } + + /** + * Returns the first argument from the server arguments. + * + * @param key the key to the argument + * + * @return the first argument or {@code null} + */ + protected String getServerArg(final String key) { + return serverArgs.get(key); + } + + /** + * Allows the subclass to do additional checking on whether the server argument should be added or not. In some + * cases it may be desirable for the argument passed to be added or processed elsewhere. + * + * @param argument the argument to test + * + * @return {@code true} if the argument should be added to the server arguments, {@code false} if the argument is + * handled by the subclass + */ + boolean addServerArgument(final Argument argument) { + return true; + } + + static boolean isJavaSecurityManagerConfigured(final Argument argument) { + return !ALLOW_VALUE.equals(argument.getValue()) && !DISALLOW_VALUE.equals(argument.getValue()); + } +} diff --git a/launcher/src/main/java/org/wildfly/core/launcher/Launcher.java b/launcher/src/main/java/org/wildfly/core/launcher/Launcher.java index 8613095c955..677818c1fcd 100644 --- a/launcher/src/main/java/org/wildfly/core/launcher/Launcher.java +++ b/launcher/src/main/java/org/wildfly/core/launcher/Launcher.java @@ -21,6 +21,7 @@ * * @author James R. Perkins */ +@SuppressWarnings("unused") public class Launcher { private final CommandBuilder builder; @@ -187,7 +188,7 @@ public Launcher setDirectory(final File dir) { * @see java.lang.ProcessBuilder#directory(java.io.File) */ public Launcher setDirectory(final String dir) { - return setDirectory(AbstractCommandBuilder.validateAndNormalizeDir(dir, true)); + return setDirectory(Environment.validateAndNormalizeDir(dir, true)); } /** diff --git a/launcher/src/main/java/org/wildfly/core/launcher/ProcessHelper.java b/launcher/src/main/java/org/wildfly/core/launcher/ProcessHelper.java index eec58ecf4a8..6520fc81d98 100644 --- a/launcher/src/main/java/org/wildfly/core/launcher/ProcessHelper.java +++ b/launcher/src/main/java/org/wildfly/core/launcher/ProcessHelper.java @@ -18,15 +18,12 @@ public class ProcessHelper { * @param process the process to check * * @return {@code true} if the process has died, otherwise {@code false} + * @see Process#isAlive() */ + @SuppressWarnings("unused") + @Deprecated(forRemoval = true) public static boolean processHasDied(final Process process) { - try { - // The process is still running - process.exitValue(); - return true; - } catch (IllegalThreadStateException ignore) { - } - return false; + return !process.isAlive(); } /** @@ -54,16 +51,13 @@ public static int destroyProcess(final Process process) throws InterruptedExcept * java.lang.RuntimePermission RuntimePermission("shutdownHooks")} */ public static Thread addShutdownHook(final Process process) { - final Thread thread = new Thread(new Runnable() { - @Override - public void run() { - if (process != null) { - process.destroy(); - try { - process.waitFor(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } + final Thread thread = new Thread(() -> { + if (process != null) { + process.destroyForcibly(); + try { + process.waitFor(); + } catch (InterruptedException e) { + throw new RuntimeException(e); } } }); diff --git a/launcher/src/main/java/org/wildfly/core/launcher/StandaloneCommandBuilder.java b/launcher/src/main/java/org/wildfly/core/launcher/StandaloneCommandBuilder.java index 2d8e643f993..a0a18953999 100644 --- a/launcher/src/main/java/org/wildfly/core/launcher/StandaloneCommandBuilder.java +++ b/launcher/src/main/java/org/wildfly/core/launcher/StandaloneCommandBuilder.java @@ -24,12 +24,13 @@ * * @author James R. Perkins */ -@SuppressWarnings({"unused", "MagicNumber", "UnusedReturnValue"}) +@SuppressWarnings({"unused", "UnusedReturnValue"}) public class StandaloneCommandBuilder extends AbstractCommandBuilder implements CommandBuilder { // JPDA remote socket debugging static final String DEBUG_FORMAT = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=%s,address=%d"; + private static final String MODULE_NAME = "org.jboss.as.standalone"; private static final String SERVER_BASE_DIR = "jboss.server.base.dir"; private static final String SERVER_CONFIG_DIR = "jboss.server.config.dir"; private static final String SERVER_LOG_DIR = "jboss.server.log.dir"; @@ -52,7 +53,7 @@ public class StandaloneCommandBuilder extends AbstractCommandBuilder(); @@ -334,7 +335,7 @@ public StandaloneCommandBuilder setDebug(final boolean suspend, final int port) * @return the builder */ public StandaloneCommandBuilder setBaseDirectory(final String baseDir) { - this.baseDir = validateAndNormalizeDir(baseDir, true); + this.baseDir = Environment.validateAndNormalizeDir(baseDir, true); return this; } @@ -348,7 +349,7 @@ public StandaloneCommandBuilder setBaseDirectory(final String baseDir) { * @return the builder */ public StandaloneCommandBuilder setBaseDirectory(final Path baseDir) { - this.baseDir = validateAndNormalizeDir(baseDir, true); + this.baseDir = Environment.validateAndNormalizeDir(baseDir, true); return this; } @@ -574,7 +575,7 @@ public List buildArguments() { if (environment.getJvm().isModular()) { cmd.addAll(DEFAULT_MODULAR_VM_ARGUMENTS); for (final String optionalModularArgument : OPTIONAL_DEFAULT_MODULAR_VM_ARGUMENTS) { - if (environment.getJvm().isPackageAvailable(environment.getJvm().getPath(), optionalModularArgument)) { + if (Jvm.isPackageAvailable(environment.getJvm().getPath(), optionalModularArgument)) { cmd.add(optionalModularArgument); } } @@ -607,7 +608,7 @@ public List buildArguments() { cmd.addAll(moduleOpts); cmd.add("-mp"); cmd.add(getModulePaths()); - cmd.add("org.jboss.as.standalone"); + cmd.add(MODULE_NAME); // Add the security properties StringBuilder sb = new StringBuilder(64); @@ -624,14 +625,6 @@ public List buildArguments() { return cmd; } - @Override - public List build() { - final List cmd = new ArrayList<>(); - cmd.add(environment.getJvm().getCommand()); - cmd.addAll(buildArguments()); - return cmd; - } - @Override public Path getJavaHome() { return environment.getJvm().getPath(); diff --git a/launcher/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java b/launcher/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java index 5c724c61752..9c9c360c68e 100644 --- a/launcher/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java +++ b/launcher/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java @@ -39,6 +39,41 @@ public class CommandBuilderTest { } } + @Test + public void testJBossModulesBuilder() { + // Set up a standalone command builder + final JBossModulesCommandBuilder commandBuilder = JBossModulesCommandBuilder.of(WILDFLY_HOME, "org.jboss.as.launcher.test") + .addJavaOption("-Djava.security.manager") + .addJavaOption("-Djava.net.preferIPv4Stack=true") + .addJavaOption("-Djava.net.preferIPv4Stack=false") + .addModuleOption("-javaagent:test-agent1.jar") + .addServerArgument("--server=test"); + + // Get all the commands + List commands = commandBuilder.buildArguments(); + + Assert.assertTrue("Missing -secmgr option", commands.contains("-secmgr")); + + Assert.assertTrue("Missing jboss-modules.jar", commands.stream().anyMatch(entry -> entry.matches("-javaagent:.*jboss-modules.jar$"))); + Assert.assertTrue("Missing test-agent1.jar", commands.contains("-javaagent:test-agent1.jar")); + Assert.assertTrue("Missing --server=test", commands.contains("--server=test")); + + // If we're using Java 9+ ensure the modular JDK options were added + testModularJvmArguments(commands, 1); + + // A system property should only be added ones + long count = 0L; + for (String s : commandBuilder.getJavaOptions()) { + if (s.contains("java.net.preferIPv4Stack")) { + count++; + } + } + Assert.assertEquals("There should be only one java.net.preferIPv4Stack system property", 1, count); + + // The value saved should be the last value added + Assert.assertTrue("java.net.preferIPv4Stack should be set to false", commandBuilder.getJavaOptions().contains("-Djava.net.preferIPv4Stack=false")); + } + @Test public void testStandaloneBuilder() { // Set up a standalone command builder