import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
-import java.nio.charset.Charset;
-import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.function.Supplier;
-import java.util.stream.Collectors;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.process.command.AbstractCommand;
import org.sonar.process.command.EsCommand;
import org.sonar.process.command.JavaCommand;
+import org.sonar.process.jmvoptions.JvmOptions;
import org.sonar.process.sharedmemoryfile.AllProcessesCommands;
import org.sonar.process.sharedmemoryfile.ProcessCommands;
public class ProcessLauncherImpl implements ProcessLauncher {
private static final Logger LOG = LoggerFactory.getLogger(ProcessLauncherImpl.class);
- private static final String ELASTICSEARCH_JVM_OPTIONS_HEADER = "# This file has been automatically generated by SonarQube during startup.\n" +
- "# Please use the sonar.search.javaOpts in sonar.properties to specify jvm options for Elasticsearch\n" +
- "\n" +
- "# DO NOT EDIT THIS FILE\n" +
- "\n";
private final File tempDir;
private final AllProcessesCommands allProcessesCommands;
try {
IOUtils.copy(getClass().getResourceAsStream("elasticsearch.yml"), new FileOutputStream(new File(confDir, "elasticsearch.yml")));
- writeJvmOptions(esCommand, new File(confDir, "jvm.options"));
+ esCommand.getEsJvmOptions().writeToJvmOptionFile(new File(confDir, "jvm.options"));
esCommand.getLog4j2Properties().store(new FileOutputStream(new File(confDir, "log4j2.properties")), "log42 properties file for ES bundled in SonarQube");
} catch (IOException e) {
throw new IllegalStateException("Failed to write ES configuration files", e);
return create(esCommand, commands);
}
- private static void writeJvmOptions(EsCommand esCommand, File jvmOptionsFile) {
- String jvmOptions = esCommand.getJvmOptions()
- .stream()
-
- // we do not expect the user to use parameters containing " -"
- .map(s -> s.split(" (?=-)"))
- .flatMap(Arrays::stream)
- .collect(Collectors.joining("\n"));
- String jvmOptionsContent = ELASTICSEARCH_JVM_OPTIONS_HEADER + jvmOptions;
- try {
- Files.write(jvmOptionsFile.toPath(), jvmOptionsContent.getBytes(Charset.forName("UTF-8")));
- } catch (IOException e) {
- throw new IllegalStateException("Cannot write Elasticsearch jvm options file", e);
- }
- }
-
- private ProcessBuilder create(JavaCommand javaCommand) {
+ private <T extends JvmOptions> ProcessBuilder create(JavaCommand<T> javaCommand) {
List<String> commands = new ArrayList<>();
commands.add(buildJavaPath());
- commands.addAll(javaCommand.getJavaOptions());
- // TODO warning - does it work if temp dir contains a whitespace ?
- // TODO move to CommandFactory ?
- commands.add(format("-Djava.io.tmpdir=%s", tempDir.getAbsolutePath()));
+ commands.addAll(javaCommand.getJvmOptions().getAll());
commands.addAll(buildClasspath(javaCommand));
commands.add(javaCommand.getClassName());
commands.add(buildPropertiesFile(javaCommand).getAbsolutePath());
*/
package org.sonar.application;
+import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.After;
+import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.DisableOnDebug;
import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestRule;
import org.junit.rules.Timeout;
import org.mockito.Mockito;
public class SchedulerImplTest {
- private static final EsCommand ES_COMMAND = new EsCommand(ELASTICSEARCH);
- private static final JavaCommand WEB_LEADER_COMMAND = new JavaCommand(WEB_SERVER);
- private static final JavaCommand WEB_FOLLOWER_COMMAND = new JavaCommand(WEB_SERVER);
- private static final JavaCommand CE_COMMAND = new JavaCommand(COMPUTE_ENGINE);
-
@Rule
public TestRule safeguardTimeout = new DisableOnDebug(Timeout.seconds(60));
@Rule
public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private EsCommand esCommand;
+ private JavaCommand webLeaderCommand;
+ private JavaCommand webFollowerCommand;
+ private JavaCommand ceCommand;
private AppReloader appReloader = mock(AppReloader.class);
private TestAppSettings settings = new TestAppSettings();
private TestAppState appState = new TestAppState();
private List<ProcessId> orderedStops = synchronizedList(new ArrayList<>());
+ @Before
+ public void setUp() throws Exception {
+ File tempDir = temporaryFolder.newFolder();
+ esCommand = new EsCommand(ELASTICSEARCH, tempDir);
+ webLeaderCommand = new JavaCommand(WEB_SERVER, tempDir);
+ webFollowerCommand = new JavaCommand(WEB_SERVER, tempDir);
+ ceCommand = new JavaCommand(COMPUTE_ENGINE, tempDir);
+ }
+
@After
public void tearDown() throws Exception {
processLauncher.close();
TestProcess web = processLauncher.waitForProcess(WEB_SERVER);
assertThat(web.isAlive()).isTrue();
assertThat(processLauncher.processes).hasSize(2);
- assertThat(processLauncher.commands).containsExactly(ES_COMMAND, WEB_LEADER_COMMAND);
+ assertThat(processLauncher.commands).containsExactly(esCommand, webLeaderCommand);
// web becomes operational -> CE is starting
web.operational = true;
TestProcess ce = processLauncher.waitForProcess(COMPUTE_ENGINE);
assertThat(ce.isAlive()).isTrue();
assertThat(processLauncher.processes).hasSize(3);
- assertThat(processLauncher.commands).containsExactly(ES_COMMAND, WEB_LEADER_COMMAND, CE_COMMAND);
+ assertThat(processLauncher.commands).containsExactly(esCommand, webLeaderCommand, ceCommand);
// all processes are up
processLauncher.processes.values().forEach(p -> assertThat(p.isAlive()).isTrue());
}
}
- private static class TestCommandFactory implements CommandFactory {
+ private class TestCommandFactory implements CommandFactory {
@Override
public EsCommand createEsCommand() {
- return ES_COMMAND;
+ return esCommand;
}
@Override
public JavaCommand createWebCommand(boolean leader) {
- return leader ? WEB_LEADER_COMMAND : WEB_FOLLOWER_COMMAND;
+ return leader ? webLeaderCommand : webFollowerCommand;
}
@Override
public JavaCommand createCeCommand() {
- return CE_COMMAND;
+ return ceCommand;
}
}
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
+import org.sonar.process.ProcessId;
import org.sonar.process.command.JavaCommand;
+import org.sonar.process.jmvoptions.JvmOptions;
import org.sonar.process.sharedmemoryfile.AllProcessesCommands;
-import org.sonar.process.ProcessId;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.MapEntry.entry;
File tempDir = temp.newFolder();
TestProcessBuilder processBuilder = new TestProcessBuilder();
ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> processBuilder);
- JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
+ JavaCommand<JvmOptions> command = new JavaCommand<>(ProcessId.ELASTICSEARCH, temp.newFolder());
command.addClasspath("lib/*.class");
command.addClasspath("lib/*.jar");
command.setArgument("foo", "bar");
command.setClassName("org.sonarqube.Main");
command.setEnvVariable("VAR1", "valueOfVar1");
- command.setWorkDir(temp.newFolder());
+ command.setJvmOptions(new JvmOptions<>()
+ .add("-Dfoo=bar")
+ .add("-Dfoo2=bar2"));
ProcessMonitor monitor = underTest.launch(command);
assertThat(processBuilder.started).isTrue();
assertThat(processBuilder.commands.get(0)).endsWith("java");
assertThat(processBuilder.commands).containsSequence(
- "-Djava.io.tmpdir=" + tempDir.getAbsolutePath(),
+ "-Dfoo=bar",
+ "-Dfoo2=bar2",
"-cp",
"lib/*.class" + System.getProperty("path.separator") + "lib/*.jar",
"org.sonarqube.Main");
File tempDir = temp.newFolder();
TestProcessBuilder processBuilder = new TestProcessBuilder();
ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> processBuilder);
- JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
+ JavaCommand<JvmOptions> command = new JavaCommand<>(ProcessId.ELASTICSEARCH, temp.newFolder());
command.setArgument("foo", "bar");
command.setArgument("baz", "woo");
+ command.setJvmOptions(new JvmOptions<>());
underTest.launch(command);
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Fail to launch process [es]");
- underTest.launch(new JavaCommand(ProcessId.ELASTICSEARCH));
+ underTest.launch(new JavaCommand(ProcessId.ELASTICSEARCH, temp.newFolder()));
}
private static class TestProcessBuilder implements ProcessLauncherImpl.ProcessBuilder {
*/
public static final String ENABLE_STOP_COMMAND = "sonar.enableStopCommand";
- public static final String WEB_ENFORCED_JVM_ARGS = "-Djava.awt.headless=true -Dfile.encoding=UTF-8";
-
- public static final String CE_ENFORCED_JVM_ARGS = "-Djava.awt.headless=true -Dfile.encoding=UTF-8";
-
public static final String HTTP_PROXY_HOST = "http.proxyHost";
public static final String HTTPS_PROXY_HOST = "https.proxyHost";
public static final String HTTP_PROXY_PORT = "http.proxyPort";
Properties defaults = new Properties();
defaults.put(SEARCH_HOST, InetAddress.getLoopbackAddress().getHostAddress());
defaults.put(SEARCH_PORT, "9001");
- defaults.put(SEARCH_JAVA_OPTS, "-Xms512m" +
- " -Xmx512m" +
- " -XX:+UseConcMarkSweepGC" +
- " -XX:CMSInitiatingOccupancyFraction=75" +
- " -XX:+UseCMSInitiatingOccupancyOnly" +
- " -XX:+AlwaysPreTouch" +
- " -server" +
- " -Xss1m" +
- " -Djava.awt.headless=true" +
- " -Dfile.encoding=UTF-8" +
- " -Djna.nosys=true" +
- " -Djdk.io.permissionsUseCanonicalPath=true" +
- " -Dio.netty.noUnsafe=true" +
- " -Dio.netty.noKeySetOptimization=true" +
- " -Dio.netty.recycler.maxCapacityPerThread=0" +
- " -Dlog4j.shutdownHookEnabled=false" +
- " -Dlog4j2.disable.jmx=true" +
- " -Dlog4j.skipJansi=true" +
- " -XX:+HeapDumpOnOutOfMemoryError");
+ defaults.put(SEARCH_JAVA_OPTS, "-Xms512m -Xmx512m -XX:+HeapDumpOnOutOfMemoryError");
defaults.put(SEARCH_JAVA_ADDITIONAL_OPTS, "");
defaults.put(PATH_DATA, "data");
import javax.annotation.Nullable;
import org.sonar.process.ProcessId;
+import static java.util.Objects.requireNonNull;
+
public abstract class AbstractCommand<T extends AbstractCommand> {
// unique key among the group of commands to launch
private final ProcessId id;
// program arguments
private final Map<String, String> arguments = new LinkedHashMap<>();
private final Map<String, String> envVariables = new HashMap<>(System.getenv());
- private File workDir;
+ private final File workDir;
- protected AbstractCommand(ProcessId id) {
- this.id = id;
+ protected AbstractCommand(ProcessId id, File workDir) {
+ this.id = requireNonNull(id, "ProcessId can't be null");
+ this.workDir = requireNonNull(workDir, "workDir can't be null");
}
public ProcessId getProcessId() {
return workDir;
}
- public T setWorkDir(File workDir) {
- this.workDir = workDir;
- return castThis();
- }
-
@SuppressWarnings("unchecked")
private T castThis() {
return (T) this;
import org.sonar.process.Props;
import org.sonar.process.es.EsLogging;
import org.sonar.process.es.EsSettings;
+import org.sonar.process.jmvoptions.CeJvmOptions;
+import org.sonar.process.jmvoptions.EsJvmOptions;
+import org.sonar.process.jmvoptions.JvmOptions;
+import org.sonar.process.jmvoptions.WebJvmOptions;
import static org.sonar.process.ProcessProperties.HTTPS_PROXY_HOST;
import static org.sonar.process.ProcessProperties.HTTPS_PROXY_PORT;
"socksProxyPort"};
private final Props props;
+ private final File tempDir;
- public CommandFactoryImpl(Props props) {
+ public CommandFactoryImpl(Props props, File tempDir) {
this.props = props;
+ this.tempDir = tempDir;
}
@Override
File logDir = new File(settingsMap.get("path.logs"));
File confDir = new File(settingsMap.get("path.conf"));
- EsCommand res = new EsCommand(ProcessId.ELASTICSEARCH)
- .setWorkDir(executable.getParentFile().getParentFile())
+ EsCommand res = new EsCommand(ProcessId.ELASTICSEARCH, executable.getParentFile().getParentFile())
.setExecutable(executable)
.setConfDir(confDir)
.setLog4j2Properties(new EsLogging().createProperties(props, logDir))
.setClusterName(settingsMap.get("cluster.name"))
.setHost(settingsMap.get("network.host"))
.setPort(Integer.valueOf(settingsMap.get("transport.tcp.port")))
- .addJvmOption(props.nonNullValue(ProcessProperties.SEARCH_JAVA_OPTS))
- .addJvmOption(props.nonNullValue(ProcessProperties.SEARCH_JAVA_ADDITIONAL_OPTS))
+ .setEsJvmOptions(new EsJvmOptions()
+ .addFromMandatoryProperty(props, ProcessProperties.SEARCH_JAVA_OPTS)
+ .addFromMandatoryProperty(props, ProcessProperties.SEARCH_JAVA_ADDITIONAL_OPTS))
.setEnvVariable("JAVA_HOME", System.getProperties().getProperty("java.home"));
settingsMap.forEach((key, value) -> res.addEsOption("-E" + key + "=" + value));
@Override
public JavaCommand createWebCommand(boolean leader) {
File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
- JavaCommand command = newJavaCommand(ProcessId.WEB_SERVER, homeDir)
- .addJavaOptions(ProcessProperties.WEB_ENFORCED_JVM_ARGS)
- .addJavaOptions(props.nonNullValue(ProcessProperties.WEB_JAVA_OPTS))
- .addJavaOptions(props.nonNullValue(ProcessProperties.WEB_JAVA_ADDITIONAL_OPTS))
+
+ WebJvmOptions jvmOptions = new WebJvmOptions(tempDir)
+ .addFromMandatoryProperty(props, ProcessProperties.WEB_JAVA_OPTS)
+ .addFromMandatoryProperty(props, ProcessProperties.WEB_JAVA_ADDITIONAL_OPTS);
+ addProxyJvmOptions(jvmOptions);
+
+ JavaCommand<WebJvmOptions> command = new JavaCommand<WebJvmOptions>(ProcessId.WEB_SERVER, homeDir)
+ .setArguments(props.rawProperties())
+ .setJvmOptions(jvmOptions)
// required for logback tomcat valve
.setEnvVariable(ProcessProperties.PATH_LOGS, props.nonNullValue(ProcessProperties.PATH_LOGS))
.setArgument("sonar.cluster.web.startupLeader", Boolean.toString(leader))
@Override
public JavaCommand createCeCommand() {
File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
- JavaCommand command = newJavaCommand(ProcessId.COMPUTE_ENGINE, homeDir)
- .addJavaOptions(ProcessProperties.CE_ENFORCED_JVM_ARGS)
- .addJavaOptions(props.nonNullValue(ProcessProperties.CE_JAVA_OPTS))
- .addJavaOptions(props.nonNullValue(ProcessProperties.CE_JAVA_ADDITIONAL_OPTS))
+
+ CeJvmOptions jvmOptions = new CeJvmOptions(tempDir)
+ .addFromMandatoryProperty(props, ProcessProperties.CE_JAVA_OPTS)
+ .addFromMandatoryProperty(props, ProcessProperties.CE_JAVA_ADDITIONAL_OPTS);
+ addProxyJvmOptions(jvmOptions);
+
+ JavaCommand<CeJvmOptions> command = new JavaCommand<CeJvmOptions>(ProcessId.COMPUTE_ENGINE, homeDir)
+ .setArguments(props.rawProperties())
+ .setJvmOptions(jvmOptions)
.setClassName("org.sonar.ce.app.CeServer")
.addClasspath("./lib/common/*")
.addClasspath("./lib/server/*")
return command;
}
- private JavaCommand newJavaCommand(ProcessId id, File homeDir) {
- JavaCommand command = new JavaCommand(id)
- .setWorkDir(homeDir)
- .setArguments(props.rawProperties());
-
+ private <T extends JvmOptions> void addProxyJvmOptions(JvmOptions<T> jvmOptions) {
for (String key : PROXY_PROPERTY_KEYS) {
- getPropsValue(key).ifPresent(val -> command.addJavaOption("-D" + key + "=" + val));
+ getPropsValue(key).ifPresent(val -> jvmOptions.add("-D" + key + "=" + val));
}
// defaults of HTTPS are the same than HTTP defaults
- setSystemPropertyToDefaultIfNotSet(command, HTTPS_PROXY_HOST, HTTP_PROXY_HOST);
- setSystemPropertyToDefaultIfNotSet(command, HTTPS_PROXY_PORT, HTTP_PROXY_PORT);
- return command;
+ setSystemPropertyToDefaultIfNotSet(jvmOptions, HTTPS_PROXY_HOST, HTTP_PROXY_HOST);
+ setSystemPropertyToDefaultIfNotSet(jvmOptions, HTTPS_PROXY_PORT, HTTP_PROXY_PORT);
}
- private void setSystemPropertyToDefaultIfNotSet(JavaCommand command,
+ private void setSystemPropertyToDefaultIfNotSet(JvmOptions jvmOptions,
String httpsProperty, String httpProperty) {
Optional<String> httpValue = getPropsValue(httpProperty);
Optional<String> httpsValue = getPropsValue(httpsProperty);
if (!httpsValue.isPresent() && httpValue.isPresent()) {
- command.addJavaOption("-D" + httpsProperty + "=" + httpValue.get());
+ jvmOptions.add("-D" + httpsProperty + "=" + httpValue.get());
}
}
import java.util.List;
import java.util.Properties;
import org.sonar.process.ProcessId;
+import org.sonar.process.jmvoptions.EsJvmOptions;
public class EsCommand extends AbstractCommand<EsCommand> {
private File executable;
private int port;
private Properties log4j2Properties;
private List<String> esOptions = new ArrayList<>();
- private List<String> jvmOptions = new ArrayList<>();
+ private EsJvmOptions esJvmOptions;
- public EsCommand(ProcessId id) {
- super(id);
+ public EsCommand(ProcessId id, File workDir) {
+ super(id, workDir);
}
public File getExecutable() {
return this;
}
- public List<String> getJvmOptions() {
- return jvmOptions;
- }
-
- public EsCommand addJvmOption(String s) {
- if (!s.isEmpty()) {
- jvmOptions.add(s);
- }
+ public EsCommand setEsJvmOptions(EsJvmOptions esJvmOptions) {
+ this.esJvmOptions = esJvmOptions;
return this;
}
+ public EsJvmOptions getEsJvmOptions() {
+ return esJvmOptions;
+ }
}
*/
package org.sonar.process.command;
+import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.sonar.process.ProcessId;
+import org.sonar.process.jmvoptions.JvmOptions;
-public class JavaCommand extends AbstractCommand<JavaCommand> {
+public class JavaCommand<T extends JvmOptions> extends AbstractCommand<JavaCommand<T>> {
// entry point
private String className;
- // for example -Xmx1G
- private final List<String> javaOptions = new ArrayList<>();
+ private JvmOptions<T> jvmOptions;
// relative path to JAR files
private final List<String> classpath = new ArrayList<>();
- public JavaCommand(ProcessId id) {
- super(id);
+ public JavaCommand(ProcessId id, File workDir) {
+ super(id, workDir);
}
- public List<String> getJavaOptions() {
- return javaOptions;
+ public JvmOptions<T> getJvmOptions() {
+ return jvmOptions;
}
- public JavaCommand addJavaOption(String s) {
- if (!s.isEmpty()) {
- javaOptions.add(s);
- }
- return this;
- }
+ public JavaCommand<T> setJvmOptions(JvmOptions<T> jvmOptions) {
+ this.jvmOptions = jvmOptions;
- public JavaCommand addJavaOptions(String s) {
- for (String opt : s.split(" ")) {
- addJavaOption(opt);
- }
return this;
}
return className;
}
- public JavaCommand setClassName(String className) {
+ public JavaCommand<T> setClassName(String className) {
this.className = className;
return this;
}
return classpath;
}
- public JavaCommand addClasspath(String s) {
+ public JavaCommand<T> addClasspath(String s) {
classpath.add(s);
return this;
}
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("JavaCommand{");
- sb.append("workDir=").append(getWorkDir());
- sb.append(", javaOptions=").append(javaOptions);
- sb.append(", className='").append(className).append('\'');
- sb.append(", classpath=").append(classpath);
- sb.append(", arguments=").append(getArguments());
- sb.append(", envVariables=").append(getEnvVariables());
- sb.append('}');
- return sb.toString();
+ return "JavaCommand{" + "workDir=" + getWorkDir() +
+ ", jvmOptions=" + jvmOptions +
+ ", className='" + className + '\'' +
+ ", classpath=" + classpath +
+ ", arguments=" + getArguments() +
+ ", envVariables=" + getEnvVariables() +
+ '}';
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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 3 of the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process.jmvoptions;
+
+import java.io.File;
+
+import static java.lang.String.format;
+
+public class CeJvmOptions extends JvmOptions<CeJvmOptions> {
+ private static final String[] MANDATORY_JVM_OPTIONS = {"-Djava.awt.headless=true", "-Dfile.encoding=UTF-8"};
+
+ public CeJvmOptions(File tmpDir) {
+ super(MANDATORY_JVM_OPTIONS);
+ add(format("-Djava.io.tmpdir=%s", tmpDir.getAbsolutePath()));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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 3 of the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process.jmvoptions;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.util.stream.Collectors;
+
+public class EsJvmOptions extends JvmOptions<EsJvmOptions> {
+ private static final String[] MANDATORY_OPTIONS = {
+ "-XX:+UseConcMarkSweepGC",
+ "-XX:CMSInitiatingOccupancyFraction=75",
+ "-XX:+UseCMSInitiatingOccupancyOnly",
+ "-XX:+AlwaysPreTouch",
+ "-server",
+ "-Xss1m",
+ "-Djava.awt.headless=true",
+ "-Dfile.encoding=UTF-8",
+ "-Djna.nosys=true",
+ "-Djdk.io.permissionsUseCanonicalPath=true",
+ "-Dio.netty.noUnsafe=true",
+ "-Dio.netty.noKeySetOptimization=true",
+ "-Dio.netty.recycler.maxCapacityPerThread=0",
+ "-Dlog4j.shutdownHookEnabled=false",
+ "-Dlog4j2.disable.jmx=true",
+ "-Dlog4j.skipJansi=true"
+ };
+ private static final String ELASTICSEARCH_JVM_OPTIONS_HEADER = "# This file has been automatically generated by SonarQube during startup.\n" +
+ "# Please use the sonar.search.javaOpts in sonar.properties to specify jvm options for Elasticsearch\n" +
+ "\n" +
+ "# DO NOT EDIT THIS FILE\n" +
+ "\n";
+
+ public EsJvmOptions() {
+ super(MANDATORY_OPTIONS);
+ }
+
+ public void writeToJvmOptionFile(File file) {
+ String jvmOptions = getAll().stream().collect(Collectors.joining("\n"));
+ String jvmOptionsContent = ELASTICSEARCH_JVM_OPTIONS_HEADER + jvmOptions;
+ try {
+ Files.write(file.toPath(), jvmOptionsContent.getBytes(Charset.forName("UTF-8")));
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot write Elasticsearch jvm options file", e);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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 3 of the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process.jmvoptions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.List;
+import org.sonar.process.Props;
+
+import static java.util.Objects.requireNonNull;
+
+public class JvmOptions<T extends JvmOptions> {
+ private static final String JVM_OPTION_NOT_NULL_ERROR_MESSAGE = "a JVM option can't be null";
+
+ private final LinkedHashSet<String> options = new LinkedHashSet<>();
+
+ public JvmOptions(String... mandatoryJvmOptions) {
+ Arrays.stream(requireNonNull(mandatoryJvmOptions, JVM_OPTION_NOT_NULL_ERROR_MESSAGE))
+ .forEach(this::add);
+ }
+
+ public T addFromMandatoryProperty(Props props, String propertyName) {
+ String value = props.nonNullValue(propertyName);
+ if (!value.isEmpty()) {
+ Arrays.stream(value.split(" (?=-)")).forEach(this::add);
+ }
+
+ return castThis();
+ }
+
+ /**
+ * Add an option.
+ * Argument is trimmed before being added.
+ *
+ * @throws IllegalArgumentException if argument is empty or does not start with {@code -}.
+ */
+ public T add(String str) {
+ requireNonNull(str, JVM_OPTION_NOT_NULL_ERROR_MESSAGE);
+ String value = str.trim();
+ if (value.isEmpty() || !value.startsWith("-")) {
+ throw new IllegalArgumentException("a JVM option can't be empty and must start with '-'");
+ }
+ options.add(value);
+
+ return castThis();
+ }
+
+ @SuppressWarnings("unchecked")
+ private T castThis() {
+ return (T) this;
+ }
+
+ public List<String> getAll() {
+ return new ArrayList<>(options);
+ }
+
+ @Override
+ public String toString() {
+ return options.toString();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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 3 of the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process.jmvoptions;
+
+import java.io.File;
+
+import static java.lang.String.format;
+
+public class WebJvmOptions extends JvmOptions<WebJvmOptions> {
+ private static final String[] MANDATORY_OPTIONS = {"-Djava.awt.headless=true", "-Dfile.encoding=UTF-8"};
+
+ public WebJvmOptions(File tmpDir) {
+ super(MANDATORY_OPTIONS);
+ add(format("-Djava.io.tmpdir=%s", tmpDir.getAbsolutePath()));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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 3 of the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.process.jmvoptions;
+
+import javax.annotation.ParametersAreNonnullByDefault;
package org.sonar.process.command;
import java.io.File;
+import java.io.IOException;
import java.util.Properties;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sonar.process.ProcessId;
@Rule
public TemporaryFolder temp = new TemporaryFolder();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void constructor_throws_NPE_of_ProcessId_is_null() throws IOException {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("ProcessId can't be null");
+
+ new AbstractCommand<AbstractCommand>(null, temp.newFolder()) {
+
+ };
+ }
+
+ @Test
+ public void constructor_throws_NPE_of_workDir_is_null() throws IOException {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("workDir can't be null");
+
+ new AbstractCommand<AbstractCommand>(ProcessId.WEB_SERVER, null) {
+
+ };
+ }
@Test
public void test_command_with_complete_information() throws Exception {
- AbstractCommand command = new AbstractCommand(ProcessId.ELASTICSEARCH) {
+ File workDir = temp.newFolder();
+ AbstractCommand command = new AbstractCommand(ProcessId.ELASTICSEARCH, workDir) {
};
command.setArguments(args);
command.setEnvVariable("JAVA_COMMAND_TEST", "1000");
- File workDir = temp.newFolder();
- command.setWorkDir(workDir);
assertThat(command.toString()).isNotNull();
assertThat(command.getWorkDir()).isSameAs(workDir);
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.process.ProcessId;
+import org.sonar.process.jmvoptions.JvmOptions;
import static org.assertj.core.api.Assertions.assertThat;
@Test
public void test_command_with_complete_information() throws Exception {
- JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
+ File workDir = temp.newFolder();
+ JavaCommand<JvmOptions> command = new JavaCommand<>(ProcessId.ELASTICSEARCH, workDir);
command.setArgument("first_arg", "val1");
Properties args = new Properties();
command.setClassName("org.sonar.ElasticSearch");
command.setEnvVariable("JAVA_COMMAND_TEST", "1000");
- File workDir = temp.newFolder();
- command.setWorkDir(workDir);
command.addClasspath("lib/*.jar");
command.addClasspath("conf/*.xml");
- command.addJavaOption("-Xmx128m");
+ JvmOptions<JvmOptions> jvmOptions = new JvmOptions<JvmOptions>() {};
+ command.setJvmOptions(jvmOptions);
assertThat(command.toString()).isNotNull();
assertThat(command.getClasspath()).containsOnly("lib/*.jar", "conf/*.xml");
- assertThat(command.getJavaOptions()).containsOnly("-Xmx128m");
+ assertThat(command.getJvmOptions()).isSameAs(jvmOptions);
assertThat(command.getWorkDir()).isSameAs(workDir);
assertThat(command.getClassName()).isEqualTo("org.sonar.ElasticSearch");
assertThat(command.getEnvVariables().size()).isEqualTo(System.getenv().size() + 1);
}
- @Test
- public void addJavaOptions_adds_jvm_options() {
- JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
- assertThat(command.getJavaOptions()).isEmpty();
-
- command.addJavaOptions("");
- assertThat(command.getJavaOptions()).isEmpty();
-
- command.addJavaOptions("-Xmx512m -Xms256m -Dfoo");
- assertThat(command.getJavaOptions()).containsOnly("-Xmx512m", "-Xms256m", "-Dfoo");
- }
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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 3 of the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process.jmvoptions;
+
+import java.io.File;
+import java.io.IOException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CeJvmOptionsTest {
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Test
+ public void constructor_sets_mandatory_JVM_options() throws IOException {
+ File tmpDir = temporaryFolder.newFolder();
+ CeJvmOptions underTest = new CeJvmOptions(tmpDir);
+
+ assertThat(underTest.getAll()).containsExactly(
+ "-Djava.awt.headless=true", "-Dfile.encoding=UTF-8", "-Djava.io.tmpdir=" + tmpDir.getAbsolutePath());
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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 3 of the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process.jmvoptions;
+
+import java.io.File;
+import java.io.IOException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.test.ExceptionCauseMatcher.hasType;
+
+public class EsJvmOptionsTest {
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void constructor_sets_mandatory_JVM_options() {
+ EsJvmOptions underTest = new EsJvmOptions();
+
+ assertThat(underTest.getAll()).containsExactly(
+ "-XX:+UseConcMarkSweepGC",
+ "-XX:CMSInitiatingOccupancyFraction=75",
+ "-XX:+UseCMSInitiatingOccupancyOnly",
+ "-XX:+AlwaysPreTouch",
+ "-server",
+ "-Xss1m",
+ "-Djava.awt.headless=true",
+ "-Dfile.encoding=UTF-8",
+ "-Djna.nosys=true",
+ "-Djdk.io.permissionsUseCanonicalPath=true",
+ "-Dio.netty.noUnsafe=true",
+ "-Dio.netty.noKeySetOptimization=true",
+ "-Dio.netty.recycler.maxCapacityPerThread=0",
+ "-Dlog4j.shutdownHookEnabled=false",
+ "-Dlog4j2.disable.jmx=true",
+ "-Dlog4j.skipJansi=true");
+ }
+
+ @Test
+ public void writeToJvmOptionFile_writes_all_JVM_options_to_file_with_warning_header() throws IOException {
+ File file = temporaryFolder.newFile();
+ EsJvmOptions underTest = new EsJvmOptions()
+ .add("-foo")
+ .add("-bar");
+
+ underTest.writeToJvmOptionFile(file);
+
+ assertThat(file).hasContent(
+ "# This file has been automatically generated by SonarQube during startup.\n" +
+ "# Please use the sonar.search.javaOpts in sonar.properties to specify jvm options for Elasticsearch\n" +
+ "\n" +
+ "# DO NOT EDIT THIS FILE\n" +
+ "\n" +
+ "-XX:+UseConcMarkSweepGC\n" +
+ "-XX:CMSInitiatingOccupancyFraction=75\n" +
+ "-XX:+UseCMSInitiatingOccupancyOnly\n" +
+ "-XX:+AlwaysPreTouch\n" +
+ "-server\n" +
+ "-Xss1m\n" +
+ "-Djava.awt.headless=true\n" +
+ "-Dfile.encoding=UTF-8\n" +
+ "-Djna.nosys=true\n" +
+ "-Djdk.io.permissionsUseCanonicalPath=true\n" +
+ "-Dio.netty.noUnsafe=true\n" +
+ "-Dio.netty.noKeySetOptimization=true\n" +
+ "-Dio.netty.recycler.maxCapacityPerThread=0\n" +
+ "-Dlog4j.shutdownHookEnabled=false\n" +
+ "-Dlog4j2.disable.jmx=true\n" +
+ "-Dlog4j.skipJansi=true\n" +
+ "-foo\n" +
+ "-bar");
+
+ }
+
+ @Test
+ public void writeToJvmOptionFile_throws_ISE_in_case_of_IOException() throws IOException {
+ File notAFile = temporaryFolder.newFolder();
+ EsJvmOptions underTest = new EsJvmOptions();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Cannot write Elasticsearch jvm options file");
+ expectedException.expectCause(hasType(IOException.class));
+
+ underTest.writeToJvmOptionFile(notAFile);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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 3 of the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process.jmvoptions;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.util.ArrayList;
+import java.util.Properties;
+import java.util.Random;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.sonar.process.Props;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(DataProviderRunner.class)
+public class JvmOptionsTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private final Random random = new Random();
+ private final String randomPropertyName = RandomStringUtils.randomAlphanumeric(3);
+ private final Properties properties = new Properties();
+ private final Props props = new Props(properties);
+ private final JvmOptions underTest = new JvmOptions();
+
+ @Test
+ public void constructor_without_arguments_creates_empty_JvmOptions() {
+ JvmOptions<JvmOptions> testJvmOptions = new JvmOptions<>();
+
+ assertThat(testJvmOptions.getAll()).isEmpty();
+ }
+
+ @Test
+ public void constructor_throws_NPE_if_argument_is_null() {
+ expectJvmOptionNotNullNPE();
+
+ new JvmOptions(null);
+ }
+
+ @Test
+ public void constructor_throws_NPE_if_any_argument_is_null() {
+ ArrayList<String> nullList = new ArrayList<>();
+ nullList.add(null);
+ String[] arguments = Stream.of(
+ Stream.of("-S1"),
+ IntStream.range(0, random.nextInt(2)).mapToObj(i -> "-B" + i),
+ nullList.stream(),
+ IntStream.range(0, random.nextInt(2)).mapToObj(i -> "-A" + i)).flatMap(s -> s)
+ .toArray(String[]::new);
+
+ expectJvmOptionNotNullNPE();
+
+ new JvmOptions(arguments);
+ }
+
+ @Test
+ @UseDataProvider("variousEmptyStrings")
+ public void constructor_throws_IAE_if_argument_is_empty(String emptyString) {
+ expectJvmOptionNotEmptyAndStartByDashIAE();
+
+ new JvmOptions(emptyString);
+ }
+
+ @Test
+ @UseDataProvider("variousEmptyStrings")
+ public void constructor_throws_IAE_if_any_argument_is_empty(String emptyString) {
+ String[] arguments = Stream.of(
+ Stream.of("-S1"),
+ IntStream.range(0, random.nextInt(2)).mapToObj(i -> "-B" + i),
+ Stream.of(emptyString),
+ IntStream.range(0, random.nextInt(2)).mapToObj(i -> "-A" + i))
+ .flatMap(s -> s)
+ .toArray(String[]::new);
+
+ expectJvmOptionNotEmptyAndStartByDashIAE();
+
+ new JvmOptions(arguments);
+ }
+
+ @Test
+ public void constructor_throws_IAE_if_argument_does_not_start_with_dash() {
+ expectJvmOptionNotEmptyAndStartByDashIAE();
+
+ new JvmOptions(RandomStringUtils.randomAlphanumeric(3));
+ }
+
+ @Test
+ public void constructor_throws_IAE_if_any_argument_does_not_start_with_dash() {
+ String[] arguments = Stream.of(
+ Stream.of("-S1"),
+ IntStream.range(0, random.nextInt(2)).mapToObj(i -> "-B" + i),
+ Stream.of(RandomStringUtils.randomAlphanumeric(3)),
+ IntStream.range(0, random.nextInt(2)).mapToObj(i -> "-A" + i))
+ .flatMap(s -> s)
+ .toArray(String[]::new);
+
+ expectJvmOptionNotEmptyAndStartByDashIAE();
+
+ new JvmOptions(arguments);
+ }
+
+ @Test
+ public void add_throws_NPE_if_argument_is_null() {
+ expectJvmOptionNotNullNPE();
+
+ underTest.add(null);
+ }
+
+ @Test
+ @UseDataProvider("variousEmptyStrings")
+ public void add_throws_IAE_if_argument_is_empty(String emptyString) {
+ expectJvmOptionNotEmptyAndStartByDashIAE();
+
+ underTest.add(emptyString);
+ }
+
+ @Test
+ public void add_throws_IAE_if_argument_does_not_start_with_dash() {
+ expectJvmOptionNotEmptyAndStartByDashIAE();
+
+ underTest.add(RandomStringUtils.randomAlphanumeric(3));
+ }
+
+ @Test
+ public void addFromMandatoryProperty_fails_with_IAE_if_property_does_not_exist() {
+ expectMissingPropertyIAE(this.randomPropertyName);
+
+ underTest.addFromMandatoryProperty(props, this.randomPropertyName);
+ }
+
+ @Test
+ @UseDataProvider("variousEmptyStrings")
+ public void addFromMandatoryProperty_fails_with_IAE_if_property_contains_an_empty_value(String emptyString) {
+ expectMissingPropertyIAE(this.randomPropertyName);
+
+ underTest.addFromMandatoryProperty(props, randomPropertyName);
+ }
+
+ @Test
+ @UseDataProvider("variousEmptyStrings")
+ public void addFromMandatoryProperty_adds_single_option_of_property_with_trimming(String emptyString) {
+ properties.put(randomPropertyName, emptyString + "-foo" + emptyString);
+
+ underTest.addFromMandatoryProperty(props, randomPropertyName);
+
+ assertThat(underTest.getAll()).containsOnly("-foo");
+ }
+
+ @Test
+ @UseDataProvider("variousEmptyStrings")
+ public void addFromMandatoryProperty_fails_with_IAE_if_property_does_not_start_with_dash_after_trimmed(String emptyString) {
+ properties.put(randomPropertyName, emptyString + "foo -bar");
+
+ expectJvmOptionNotEmptyAndStartByDashIAE();
+
+ underTest.addFromMandatoryProperty(props, randomPropertyName);
+ }
+
+ @Test
+ @UseDataProvider("variousEmptyStrings")
+ public void addFromMandatoryProperty_adds_options_of_property_with_trimming(String emptyString) {
+ properties.put(randomPropertyName, emptyString + "-foo" + emptyString + " -bar" + emptyString + " -duck" + emptyString);
+
+ underTest.addFromMandatoryProperty(props, randomPropertyName);
+
+ assertThat(underTest.getAll()).containsOnly("-foo", "-bar", "-duck");
+ }
+
+ @Test
+ public void addFromMandatoryProperty_supports_spaces_inside_options() {
+ properties.put(randomPropertyName, "-foo bar -duck");
+
+ underTest.addFromMandatoryProperty(props, randomPropertyName);
+
+ assertThat(underTest.getAll()).containsOnly("-foo bar", "-duck");
+ }
+
+ @Test
+ public void toString_prints_all_jvm_options() {
+ underTest.add("-foo").add("-bar");
+
+ assertThat(underTest.toString()).isEqualTo("[-foo, -bar]");
+ }
+
+ private void expectJvmOptionNotNullNPE() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("a JVM option can't be null");
+ }
+
+ private void expectJvmOptionNotEmptyAndStartByDashIAE() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("a JVM option can't be empty and must start with '-'");
+ }
+
+ public void expectMissingPropertyIAE(String randomPropertyName) {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Missing property: " + randomPropertyName);
+ }
+
+ @DataProvider()
+ public static Object[][] variousEmptyStrings() {
+ return new Object[][] {
+ {""},
+ {" "},
+ {" "}
+ };
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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 3 of the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process.jmvoptions;
+
+import java.io.File;
+import java.io.IOException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class WebJvmOptionsTest {
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Test
+ public void constructor_sets_mandatory_JVM_options() throws IOException {
+ File tmpDir = temporaryFolder.newFolder();
+ WebJvmOptions underTest = new WebJvmOptions(tmpDir);
+
+ assertThat(underTest.getAll()).containsExactly(
+ "-Djava.awt.headless=true", "-Dfile.encoding=UTF-8", "-Djava.io.tmpdir=" + tmpDir.getAbsolutePath());
+ }
+
+}
#--------------------------------------------------------------------------------------------------
# ELASTICSEARCH
# Elasticsearch is used to facilitate fast and accurate information retrieval.
-# It is executed in a dedicated Java process. Default heap size is 512m.
+# It is executed in a dedicated Java process. Default heap size is 512Mb.
# JVM options of Elasticsearch process
#sonar.search.javaOpts=-Xms512m \
# -Xmx512m \
-# -XX:+UseConcMarkSweepGC \
-# -XX:CMSInitiatingOccupancyFraction=75 \
-# -XX:+UseCMSInitiatingOccupancyOnly \
-# -XX:+AlwaysPreTouch \
-# -server \
-# -Xss1m \
-# -Djava.awt.headless=true \
-# -Dfile.encoding=UTF-8 \
-# -Djna.nosys=true \
-# -Djdk.io.permissionsUseCanonicalPath=true \
-# -Dio.netty.noUnsafe=true \
-# -Dio.netty.noKeySetOptimization=true \
-# -Dio.netty.recycler.maxCapacityPerThread=0 \
-# -Dlog4j.shutdownHookEnabled=false \
-# -Dlog4j2.disable.jmx=true \
-# -Dlog4j.skipJansi=true \
# -XX:+HeapDumpOnOutOfMemoryError
# Same as previous property, but allows to not repeat all other settings like -Xmx
try (AppState appState = new AppStateFactory(settings).create()) {
appState.registerSonarQubeVersion(getSonarqubeVersion());
AppReloader appReloader = new AppReloaderImpl(settingsLoader, fileSystem, appState, logging);
- CommandFactory commandFactory = new CommandFactoryImpl(settings.getProps());
fileSystem.reset();
+ CommandFactory commandFactory = new CommandFactoryImpl(settings.getProps(), fileSystem.getTempDir());
try (ProcessLauncher processLauncher = new ProcessLauncherImpl(fileSystem.getTempDir())) {
Scheduler scheduler = new SchedulerImpl(settings, appReloader, commandFactory, processLauncher, appState);
@Override
protected boolean matchesSafely(Throwable item) {
- if (!item.getClass().isAssignableFrom(type)) {
+ if (!type.isAssignableFrom(item.getClass())) {
return false;
}
if (expectedMessage == null) {
.addPlugin(pluginArtifact("posttask-plugin"))
- // reduce xmx and xms from 2g (default) to 1g
- .setServerProperty("sonar.search.javaOpts", "-Xms512m -Xmx512m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly" +
- " -XX:+AlwaysPreTouch -server -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true" +
- " -Djdk.io.permissionsUseCanonicalPath=true -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true" +
- " -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Dlog4j.skipJansi=true -XX:+HeapDumpOnOutOfMemoryError")
-
.addPlugin(xooPlugin())
.build();
// 1 second. Required for notification test.
.setServerProperty("sonar.notifications.delay", "1")
- // reduce xmx and xms from 2g (default) to 1g
- .setServerProperty("sonar.search.javaOpts", "-Xms512m -Xmx512m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly" +
- " -XX:+AlwaysPreTouch -server -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true" +
- " -Djdk.io.permissionsUseCanonicalPath=true -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true" +
- " -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Dlog4j.skipJansi=true -XX:+HeapDumpOnOutOfMemoryError")
-
.build();
}
// used by ProjectBuilderTest
.addPlugin(pluginArtifact("project-builder-plugin"))
- // reduce xmx and xms from 2g (default) to 1g
- .setServerProperty("sonar.search.javaOpts", "-Xms512m -Xmx512m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly" +
- " -XX:+AlwaysPreTouch -server -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true" +
- " -Djdk.io.permissionsUseCanonicalPath=true -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true" +
- " -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Dlog4j.skipJansi=true -XX:+HeapDumpOnOutOfMemoryError")
-
.build();
}
// Used by LogsTest
.setServerProperty("sonar.web.accessLogs.pattern", LogsTest.ACCESS_LOGS_PATTERN)
- // reduce xmx and xms from 2g (default) to 1g
- .setServerProperty("sonar.search.javaOpts", "-Xms512m -Xmx512m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly" +
- " -XX:+AlwaysPreTouch -server -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true" +
- " -Djdk.io.permissionsUseCanonicalPath=true -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true" +
- " -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Dlog4j.skipJansi=true -XX:+HeapDumpOnOutOfMemoryError")
-
.build();
}
.addPlugin(pluginArtifact("fake-billing-plugin"))
.addPlugin(pluginArtifact("ui-extensions-plugin"))
- // reduce xmx and xms from 2g (default) to 1g
- .setServerProperty("sonar.search.javaOpts", "-Xms512m -Xmx512m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly" +
- " -XX:+AlwaysPreTouch -server -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true" +
- " -Djdk.io.permissionsUseCanonicalPath=true -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true" +
- " -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Dlog4j.skipJansi=true -XX:+HeapDumpOnOutOfMemoryError")
-
.build();
}