/* * JBoss, Home of Professional Open Source. * Copyright 2014, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.wildfly.core.launcher; import java.nio.file.Path; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.wildfly.core.launcher.Arguments.Argument; /** * Builds a list of commands used to launch a standalone instance of WildFly. * <p/> * This builder is not thread safe and the same instance should not be used in multiple threads. * * @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a> */ @SuppressWarnings("unused") public class StandaloneCommandBuilder extends AbstractCommandBuilder<StandaloneCommandBuilder> 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 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"; private Path baseDir; private final Arguments javaOpts; private String debugArg; private String modulesLocklessArg; private String modulesMetricsArg; private final Map<String, String> securityProperties; /** * Creates a new command builder for a standalone instance. * <p/> * Note the {@code wildflyHome} and {@code javaHome} are not validated using the constructor. The static {@link * #of(java.nio.file.Path)} is preferred. * * @param wildflyHome the path to WildFly */ private StandaloneCommandBuilder(final Path wildflyHome) { super(wildflyHome); javaOpts = new Arguments(); javaOpts.addAll(DEFAULT_VM_ARGUMENTS); securityProperties = new LinkedHashMap<>(); } /** * Creates a command builder for a standalone instance of WildFly. * * @param wildflyHome the path to the WildFly home directory * * @return a new builder */ public static StandaloneCommandBuilder of(final Path wildflyHome) { return new StandaloneCommandBuilder(validateWildFlyDir(wildflyHome)); } /** * Creates a command builder for a standalone instance of WildFly. * * @param wildflyHome the path to the WildFly home directory * * @return a new builder */ public static StandaloneCommandBuilder of(final String wildflyHome) { return new StandaloneCommandBuilder(validateWildFlyDir(wildflyHome)); } /** * Adds a JVM argument to the command ignoring {@code null} arguments. * * @param jvmArg the JVM argument to add * * @return the builder */ public StandaloneCommandBuilder addJavaOption(final String jvmArg) { if (jvmArg != null && !jvmArg.trim().isEmpty()) { final Argument argument = Arguments.parse(jvmArg); switch (argument.getKey()) { case SERVER_BASE_DIR: if (argument.getValue() != null) { setBaseDirectory(argument.getValue()); } break; case SERVER_CONFIG_DIR: if (argument.getValue() != null) { setConfigurationDirectory(argument.getValue()); } break; case SERVER_LOG_DIR: if (argument.getValue() != null) { setLogDirectory(argument.getValue()); } break; case SECURITY_MANAGER_PROP: setUseSecurityManager(true); break; default: javaOpts.add(argument); break; } } 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 StandaloneCommandBuilder 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 StandaloneCommandBuilder addJavaOptions(final Iterable<String> 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. * <p/> * 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 StandaloneCommandBuilder setJavaOptions(final Iterable<String> 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. * <p/> * 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 StandaloneCommandBuilder setJavaOptions(final String... javaOpts) { this.javaOpts.clear(); return addJavaOptions(javaOpts); } /** * Returns the JVM arguments. * * @return the JVM arguments */ public List<String> getJavaOptions() { return javaOpts.asList(); } /** * Sets the debug argument for the JVM with a default port of {@code 8787}. * * @return the builder */ public StandaloneCommandBuilder setDebug() { return setDebug(false, 8787); } /** * Sets the debug argument for the JVM. * * @param port the port to listen on * * @return the builder */ public StandaloneCommandBuilder setDebug(final int port) { return setDebug(false, port); } /** * Sets the debug JPDA remote socket debugging argument. * * @param suspend {@code true} to suspend otherwise {@code false} * @param port the port to listen on * * @return the builder */ public StandaloneCommandBuilder setDebug(final boolean suspend, final int port) { debugArg = String.format(DEBUG_FORMAT, (suspend ? "y" : "n"), port); return this; } /** * Sets the base directory to use. * <p/> * The default is {@code $JBOSS_HOME/standalone}. * * @param baseDir the base directory or {@code null} to resolve the base directory * * @return the builder */ public StandaloneCommandBuilder setBaseDirectory(final String baseDir) { this.baseDir = validateAndNormalizeDir(baseDir, true); return this; } /** * Sets the base directory to use. * <p/> * The default is {@code $JBOSS_HOME/standalone}. * * @param baseDir the base directory or {@code null} to resolve the base directory * * @return the builder */ public StandaloneCommandBuilder setBaseDirectory(final Path baseDir) { this.baseDir = validateAndNormalizeDir(baseDir, true); 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 StandaloneCommandBuilder setJavaHome(final String javaHome) { environment.setJavaHome(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 StandaloneCommandBuilder setJavaHome(final Path javaHome) { environment.setJavaHome(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 StandaloneCommandBuilder 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 StandaloneCommandBuilder setModulesMetrics(final boolean b) { if (b) { modulesMetricsArg = "-Djboss.modules.metrics=true"; } else { modulesMetricsArg = null; } return this; } /** * Sets the configuration file for the server. The file must be in the {@link #setConfigurationDirectory(String) * configuration} directory. A value of {@code null} will remove the configuration file. * <p/> * This will override any previous value set via {@link #addServerArgument(String)}. * * @param configFile the configuration file name or {@code null} to remove the configuration file * * @return the builder */ public StandaloneCommandBuilder setServerConfiguration(final String configFile) { setSingleServerArg("-c", configFile); return this; } /** * Returns the configuration file {@link #setServerConfiguration(String) set} or {@code null} if one was not set. * * @return the configuration file set or {@code null} if not set */ public String getServerConfiguration() { return getServerArg("-c"); } /** * Sets the configuration file for the server. The file must be in the {@link #setConfigurationDirectory(String) * configuration} directory. A value of {@code null} will remove the configuration file. * <p/> * This will override any previous value set via {@link #addServerArgument(String)}. * * @param configFile the configuration file name or {@code null} to remove the configuration file * * @return the builder */ public StandaloneCommandBuilder setServerReadOnlyConfiguration(final String configFile) { setSingleServerArg("--read-only-server-config", configFile); return this; } /** * Returns the configuration file {@link #setServerConfiguration(String) set} or {@code null} if one was not set. * * @return the configuration file set or {@code null} if not set */ public String getReadOnlyServerConfiguration() { return getServerArg("--read-only-server-config"); } /** * Adds a security property to be passed to the server with a {@code null} value. * * @param key the property key * * @return the builder */ public StandaloneCommandBuilder addSecurityProperty(final String key) { securityProperties.put(key, null); return this; } /** * Adds a security property to be passed to the server. * * @param key the property key * @param value the property value * * @return the builder */ public StandaloneCommandBuilder addSecurityProperty(final String key, final String value) { securityProperties.put(key, value); return this; } /** * Adds all the security properties to be passed to the server. * * @param properties a map of the properties to add, {@code null} values are allowed in the map * * @return the builder */ public StandaloneCommandBuilder addSecurityProperties(final Map<String, String> properties) { securityProperties.putAll(properties); return this; } @Override public List<String> buildArguments() { final List<String> cmd = new ArrayList<>(); cmd.add("-D[Standalone]"); cmd.addAll(getJavaOptions()); if (modulesLocklessArg != null) { cmd.add(modulesLocklessArg); } if (modulesMetricsArg != null) { cmd.add(modulesMetricsArg); } if (debugArg != null) { cmd.add(debugArg); } cmd.add(getBootLogArgument("server.log")); cmd.add(getLoggingPropertiesArgument("logging.properties")); cmd.add("-jar"); cmd.add(getModulesJarName()); if (useSecurityManager()) { cmd.add(SECURITY_MANAGER_ARG); } cmd.add("-mp"); cmd.add(getModulePaths()); cmd.add("org.jboss.as.standalone"); addSystemPropertyArg(cmd, HOME_DIR, getWildFlyHome()); addSystemPropertyArg(cmd, SERVER_BASE_DIR, getBaseDirectory()); addSystemPropertyArg(cmd, SERVER_LOG_DIR, getLogDirectory()); addSystemPropertyArg(cmd, SERVER_CONFIG_DIR, getConfigurationDirectory()); // Add the security properties StringBuilder sb = new StringBuilder(64); for (Map.Entry<String, String> entry : securityProperties.entrySet()) { sb.append("-S").append(entry.getKey()); if (entry.getValue() != null) { sb.append('=').append(entry.getValue()); } cmd.add(sb.toString()); sb.setLength(0); } cmd.addAll(getServerArguments()); return cmd; } @Override public List<String> build() { final List<String> cmd = new ArrayList<>(); cmd.add(getJavaCommand()); cmd.addAll(buildArguments()); return cmd; } @Override public Path getJavaHome() { return environment.getJavaHome(); } @Override public Path getBaseDirectory() { if (baseDir == null) { return normalizePath("standalone"); } return baseDir; } @Override protected StandaloneCommandBuilder getThis() { return this; } }