import org.sonar.application.config.ClusterSettings;
import org.sonar.application.process.JavaCommand;
import org.sonar.application.process.JavaCommandFactory;
-import org.sonar.application.process.JavaProcessLauncher;
+import org.sonar.application.process.ProcessLauncher;
import org.sonar.application.process.Lifecycle;
import org.sonar.application.process.ProcessEventListener;
import org.sonar.application.process.ProcessLifecycleListener;
private final AppSettings settings;
private final AppReloader appReloader;
private final JavaCommandFactory javaCommandFactory;
- private final JavaProcessLauncher javaProcessLauncher;
+ private final ProcessLauncher processLauncher;
private final AppState appState;
private final NodeLifecycle nodeLifecycle = new NodeLifecycle();
private long processWatcherDelayMs = SQProcess.DEFAULT_WATCHER_DELAY_MS;
public SchedulerImpl(AppSettings settings, AppReloader appReloader, JavaCommandFactory javaCommandFactory,
- JavaProcessLauncher javaProcessLauncher,
+ ProcessLauncher processLauncher,
AppState appState) {
this.settings = settings;
this.appReloader = appReloader;
this.javaCommandFactory = javaCommandFactory;
- this.javaProcessLauncher = javaProcessLauncher;
+ this.processLauncher = processLauncher;
this.appState = appState;
this.appState.addListener(this);
}
try {
process.start(() -> {
JavaCommand command = commandSupplier.get();
- return javaProcessLauncher.launch(command);
+ return processLauncher.launch(command);
});
} catch (RuntimeException e) {
// failed to start command -> stop everything
+++ /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.application.process;
-
-import java.io.Closeable;
-
-public interface JavaProcessLauncher extends Closeable {
-
- @Override
- void close();
-
- /**
- * Launch Java command. An {@link IllegalStateException} is thrown
- * on error.
- */
- ProcessMonitor launch(JavaCommand javaCommand);
-}
+++ /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.application.process;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.process.AllProcessesCommands;
-import org.sonar.process.ProcessCommands;
-
-import static java.lang.String.format;
-import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
-import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_KEY;
-import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;
-import static org.sonar.process.ProcessEntryPoint.PROPERTY_TERMINATION_TIMEOUT;
-
-public class JavaProcessLauncherImpl implements JavaProcessLauncher {
- private static final Logger LOG = LoggerFactory.getLogger(JavaProcessLauncherImpl.class);
-
- private final File tempDir;
- private final AllProcessesCommands allProcessesCommands;
- private final Supplier<ProcessBuilder> processBuilderSupplier;
-
- public JavaProcessLauncherImpl(File tempDir) {
- this(tempDir, new AllProcessesCommands(tempDir), JavaLangProcessBuilder::new);
- }
-
- JavaProcessLauncherImpl(File tempDir, AllProcessesCommands allProcessesCommands, Supplier<ProcessBuilder> processBuilderSupplier) {
- this.tempDir = tempDir;
- this.allProcessesCommands = allProcessesCommands;
- this.processBuilderSupplier = processBuilderSupplier;
- }
-
- @Override
- public void close() {
- allProcessesCommands.close();
- }
-
- @Override
- public ProcessMonitor launch(JavaCommand javaCommand) {
- Process process = null;
- ProcessCommands commands;
- try {
- commands = allProcessesCommands.createAfterClean(javaCommand.getProcessId().getIpcIndex());
-
- ProcessBuilder processBuilder = create(javaCommand);
- LOG.info("Launch process[{}]: {}", javaCommand.getProcessId().getKey(), String.join(" ", processBuilder.command()));
- process = processBuilder.start();
- return new ProcessMonitorImpl(process, commands);
-
- } catch (Exception e) {
- // just in case
- if (process != null) {
- process.destroyForcibly();
- }
- throw new IllegalStateException(format("Fail to launch process [%s]", javaCommand.getProcessId().getKey()), e);
- }
- }
-
- private ProcessBuilder create(JavaCommand 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 JavaCommandFactory ?
- commands.add(format("-Djava.io.tmpdir=%s", tempDir.getAbsolutePath()));
- commands.addAll(buildClasspath(javaCommand));
- commands.add(javaCommand.getClassName());
- commands.add(buildPropertiesFile(javaCommand).getAbsolutePath());
-
- ProcessBuilder processBuilder = processBuilderSupplier.get();
- processBuilder.command(commands);
- processBuilder.directory(javaCommand.getWorkDir());
- processBuilder.environment().putAll(javaCommand.getEnvVariables());
- processBuilder.redirectErrorStream(true);
- return processBuilder;
- }
-
- private static String buildJavaPath() {
- String separator = System.getProperty("file.separator");
- return new File(new File(System.getProperty("java.home")), "bin" + separator + "java").getAbsolutePath();
- }
-
- private static List<String> buildClasspath(JavaCommand javaCommand) {
- String pathSeparator = System.getProperty("path.separator");
- return Arrays.asList("-cp", String.join(pathSeparator, javaCommand.getClasspath()));
- }
-
- private File buildPropertiesFile(JavaCommand javaCommand) {
- File propertiesFile = null;
- try {
- propertiesFile = File.createTempFile("sq-process", "properties", tempDir);
- Properties props = new Properties();
- props.putAll(javaCommand.getArguments());
- props.setProperty(PROPERTY_PROCESS_KEY, javaCommand.getProcessId().getKey());
- props.setProperty(PROPERTY_PROCESS_INDEX, Integer.toString(javaCommand.getProcessId().getIpcIndex()));
- // FIXME is it the responsibility of child process to have this timeout (too) ?
- props.setProperty(PROPERTY_TERMINATION_TIMEOUT, "60000");
- props.setProperty(PROPERTY_SHARED_PATH, tempDir.getAbsolutePath());
- try (OutputStream out = new FileOutputStream(propertiesFile)) {
- props.store(out, format("Temporary properties file for command [%s]", javaCommand.getProcessId().getKey()));
- }
- return propertiesFile;
- } catch (Exception e) {
- throw new IllegalStateException("Cannot write temporary settings to " + propertiesFile, e);
- }
- }
-
- /**
- * An interface of the methods of {@link java.lang.ProcessBuilder} that we use in {@link JavaProcessLauncherImpl}.
- * <p>Allows testing creating processes without actualling creating them at OS level</p>
- */
- public interface ProcessBuilder {
- List<String> command();
-
- ProcessBuilder command(List<String> commands);
-
- ProcessBuilder directory(File dir);
-
- Map<String, String> environment();
-
- ProcessBuilder redirectErrorStream(boolean b);
-
- Process start() throws IOException;
- }
-
- private static class JavaLangProcessBuilder implements ProcessBuilder {
- private final java.lang.ProcessBuilder builder = new java.lang.ProcessBuilder();
-
- /**
- * @see java.lang.ProcessBuilder#command()
- */
- @Override
- public List<String> command() {
- return builder.command();
- }
-
- /**
- * @see java.lang.ProcessBuilder#command(List)
- */
- @Override
- public ProcessBuilder command(List<String> commands) {
- builder.command(commands);
- return this;
- }
-
- /**
- * @see java.lang.ProcessBuilder#directory(File)
- */
- @Override
- public ProcessBuilder directory(File dir) {
- builder.directory(dir);
- return this;
- }
-
- /**
- * @see java.lang.ProcessBuilder#environment()
- */
- @Override
- public Map<String, String> environment() {
- return builder.environment();
- }
-
- /**
- * @see java.lang.ProcessBuilder#redirectErrorStream(boolean)
- */
- @Override
- public ProcessBuilder redirectErrorStream(boolean b) {
- builder.redirectErrorStream(b);
- return this;
- }
-
- /**
- * @see java.lang.ProcessBuilder#start()
- */
- @Override
- public Process start() throws IOException {
- return builder.start();
- }
- }
-}
--- /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.application.process;
+
+import java.io.Closeable;
+
+public interface ProcessLauncher extends Closeable {
+
+ @Override
+ void close();
+
+ /**
+ * Launch a Java command.
+ *
+ * @throws IllegalStateException if an error occurs
+ */
+ ProcessMonitor launch(JavaCommand javaCommand);
+}
--- /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.application.process;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.process.AllProcessesCommands;
+import org.sonar.process.ProcessCommands;
+
+import static java.lang.String.format;
+import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
+import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_KEY;
+import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;
+import static org.sonar.process.ProcessEntryPoint.PROPERTY_TERMINATION_TIMEOUT;
+
+public class ProcessLauncherImpl implements ProcessLauncher {
+ private static final Logger LOG = LoggerFactory.getLogger(ProcessLauncherImpl.class);
+
+ private final File tempDir;
+ private final AllProcessesCommands allProcessesCommands;
+ private final Supplier<ProcessBuilder> processBuilderSupplier;
+
+ public ProcessLauncherImpl(File tempDir) {
+ this(tempDir, new AllProcessesCommands(tempDir), JavaLangProcessBuilder::new);
+ }
+
+ ProcessLauncherImpl(File tempDir, AllProcessesCommands allProcessesCommands, Supplier<ProcessBuilder> processBuilderSupplier) {
+ this.tempDir = tempDir;
+ this.allProcessesCommands = allProcessesCommands;
+ this.processBuilderSupplier = processBuilderSupplier;
+ }
+
+ @Override
+ public void close() {
+ allProcessesCommands.close();
+ }
+
+ @Override
+ public ProcessMonitor launch(JavaCommand javaCommand) {
+ Process process = null;
+ ProcessCommands commands;
+ try {
+ commands = allProcessesCommands.createAfterClean(javaCommand.getProcessId().getIpcIndex());
+
+ ProcessBuilder processBuilder = create(javaCommand);
+ LOG.info("Launch process[{}]: {}", javaCommand.getProcessId().getKey(), String.join(" ", processBuilder.command()));
+ process = processBuilder.start();
+ return new ProcessMonitorImpl(process, commands);
+
+ } catch (Exception e) {
+ // just in case
+ if (process != null) {
+ process.destroyForcibly();
+ }
+ throw new IllegalStateException(format("Fail to launch process [%s]", javaCommand.getProcessId().getKey()), e);
+ }
+ }
+
+ private ProcessBuilder create(JavaCommand 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 JavaCommandFactory ?
+ commands.add(format("-Djava.io.tmpdir=%s", tempDir.getAbsolutePath()));
+ commands.addAll(buildClasspath(javaCommand));
+ commands.add(javaCommand.getClassName());
+ commands.add(buildPropertiesFile(javaCommand).getAbsolutePath());
+
+ ProcessBuilder processBuilder = processBuilderSupplier.get();
+ processBuilder.command(commands);
+ processBuilder.directory(javaCommand.getWorkDir());
+ processBuilder.environment().putAll(javaCommand.getEnvVariables());
+ processBuilder.redirectErrorStream(true);
+ return processBuilder;
+ }
+
+ private static String buildJavaPath() {
+ String separator = System.getProperty("file.separator");
+ return new File(new File(System.getProperty("java.home")), "bin" + separator + "java").getAbsolutePath();
+ }
+
+ private static List<String> buildClasspath(JavaCommand javaCommand) {
+ String pathSeparator = System.getProperty("path.separator");
+ return Arrays.asList("-cp", String.join(pathSeparator, javaCommand.getClasspath()));
+ }
+
+ private File buildPropertiesFile(JavaCommand javaCommand) {
+ File propertiesFile = null;
+ try {
+ propertiesFile = File.createTempFile("sq-process", "properties", tempDir);
+ Properties props = new Properties();
+ props.putAll(javaCommand.getArguments());
+ props.setProperty(PROPERTY_PROCESS_KEY, javaCommand.getProcessId().getKey());
+ props.setProperty(PROPERTY_PROCESS_INDEX, Integer.toString(javaCommand.getProcessId().getIpcIndex()));
+ // FIXME is it the responsibility of child process to have this timeout (too) ?
+ props.setProperty(PROPERTY_TERMINATION_TIMEOUT, "60000");
+ props.setProperty(PROPERTY_SHARED_PATH, tempDir.getAbsolutePath());
+ try (OutputStream out = new FileOutputStream(propertiesFile)) {
+ props.store(out, format("Temporary properties file for command [%s]", javaCommand.getProcessId().getKey()));
+ }
+ return propertiesFile;
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot write temporary settings to " + propertiesFile, e);
+ }
+ }
+
+ /**
+ * An interface of the methods of {@link java.lang.ProcessBuilder} that we use in {@link ProcessLauncherImpl}.
+ * <p>Allows testing creating processes without actualling creating them at OS level</p>
+ */
+ public interface ProcessBuilder {
+ List<String> command();
+
+ ProcessBuilder command(List<String> commands);
+
+ ProcessBuilder directory(File dir);
+
+ Map<String, String> environment();
+
+ ProcessBuilder redirectErrorStream(boolean b);
+
+ Process start() throws IOException;
+ }
+
+ private static class JavaLangProcessBuilder implements ProcessBuilder {
+ private final java.lang.ProcessBuilder builder = new java.lang.ProcessBuilder();
+
+ /**
+ * @see java.lang.ProcessBuilder#command()
+ */
+ @Override
+ public List<String> command() {
+ return builder.command();
+ }
+
+ /**
+ * @see java.lang.ProcessBuilder#command(List)
+ */
+ @Override
+ public ProcessBuilder command(List<String> commands) {
+ builder.command(commands);
+ return this;
+ }
+
+ /**
+ * @see java.lang.ProcessBuilder#directory(File)
+ */
+ @Override
+ public ProcessBuilder directory(File dir) {
+ builder.directory(dir);
+ return this;
+ }
+
+ /**
+ * @see java.lang.ProcessBuilder#environment()
+ */
+ @Override
+ public Map<String, String> environment() {
+ return builder.environment();
+ }
+
+ /**
+ * @see java.lang.ProcessBuilder#redirectErrorStream(boolean)
+ */
+ @Override
+ public ProcessBuilder redirectErrorStream(boolean b) {
+ builder.redirectErrorStream(b);
+ return this;
+ }
+
+ /**
+ * @see java.lang.ProcessBuilder#start()
+ */
+ @Override
+ public Process start() throws IOException {
+ return builder.start();
+ }
+ }
+}
import org.sonar.application.config.TestAppSettings;
import org.sonar.application.process.JavaCommand;
import org.sonar.application.process.JavaCommandFactory;
-import org.sonar.application.process.JavaProcessLauncher;
+import org.sonar.application.process.ProcessLauncher;
import org.sonar.application.process.ProcessMonitor;
import org.sonar.process.ProcessId;
import org.sonar.process.ProcessProperties;
private AppReloader appReloader = mock(AppReloader.class);
private TestAppSettings settings = new TestAppSettings();
private TestJavaCommandFactory javaCommandFactory = new TestJavaCommandFactory();
- private TestJavaProcessLauncher processLauncher = new TestJavaProcessLauncher();
+ private TestProcessLauncher processLauncher = new TestProcessLauncher();
private TestAppState appState = new TestAppState();
private List<ProcessId> orderedStops = synchronizedList(new ArrayList<>());
}
}
- private class TestJavaProcessLauncher implements JavaProcessLauncher {
+ private class TestProcessLauncher implements ProcessLauncher {
private final EnumMap<ProcessId, TestProcess> processes = new EnumMap<>(ProcessId.class);
private final List<JavaCommand> commands = synchronizedList(new ArrayList<>());
private ProcessId makeStartupFail = null;
+++ /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.application.process;
-
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-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.AllProcessesCommands;
-import org.sonar.process.ProcessId;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.data.MapEntry.entry;
-import static org.mockito.Mockito.RETURNS_MOCKS;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class JavaProcessLauncherImplTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private AllProcessesCommands commands = mock(AllProcessesCommands.class, RETURNS_MOCKS);
-
- @Test
- public void launch_forks_a_new_process() throws Exception {
- File tempDir = temp.newFolder();
- TestProcessBuilder processBuilder = new TestProcessBuilder();
- JavaProcessLauncher underTest = new JavaProcessLauncherImpl(tempDir, commands, () -> processBuilder);
- JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
- 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());
-
- ProcessMonitor monitor = underTest.launch(command);
-
- assertThat(monitor).isNotNull();
- assertThat(processBuilder.started).isTrue();
- assertThat(processBuilder.commands.get(0)).endsWith("java");
- assertThat(processBuilder.commands).containsSequence(
- "-Djava.io.tmpdir=" + tempDir.getAbsolutePath(),
- "-cp",
- "lib/*.class" + System.getProperty("path.separator") + "lib/*.jar",
- "org.sonarqube.Main");
- assertThat(processBuilder.dir).isEqualTo(command.getWorkDir());
- assertThat(processBuilder.redirectErrorStream).isTrue();
- assertThat(processBuilder.environment)
- .contains(entry("VAR1", "valueOfVar1"))
- .containsAllEntriesOf(command.getEnvVariables());
- }
-
- @Test
- public void properties_are_passed_to_command_via_a_temporary_properties_file() throws Exception {
- File tempDir = temp.newFolder();
- TestProcessBuilder processBuilder = new TestProcessBuilder();
- JavaProcessLauncher underTest = new JavaProcessLauncherImpl(tempDir, commands, () -> processBuilder);
- JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
- command.setArgument("foo", "bar");
- command.setArgument("baz", "woo");
-
- underTest.launch(command);
-
- String propsFilePath = processBuilder.commands.get(processBuilder.commands.size() - 1);
- File file = new File(propsFilePath);
- assertThat(file).exists().isFile();
- try (FileReader reader = new FileReader(file)) {
- Properties props = new Properties();
- props.load(reader);
- assertThat(props).containsOnly(
- entry("foo", "bar"),
- entry("baz", "woo"),
- entry("process.terminationTimeout", "60000"),
- entry("process.key", ProcessId.ELASTICSEARCH.getKey()),
- entry("process.index", String.valueOf(ProcessId.ELASTICSEARCH.getIpcIndex())),
- entry("process.sharedDir", tempDir.getAbsolutePath()));
- }
- }
-
- @Test
- public void throw_ISE_if_command_fails() throws IOException {
- File tempDir = temp.newFolder();
- JavaProcessLauncherImpl.ProcessBuilder processBuilder = mock(JavaProcessLauncherImpl.ProcessBuilder.class, RETURNS_MOCKS);
- when(processBuilder.start()).thenThrow(new IOException("error"));
- JavaProcessLauncher underTest = new JavaProcessLauncherImpl(tempDir, commands, () -> processBuilder);
-
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("Fail to launch process [es]");
-
- underTest.launch(new JavaCommand(ProcessId.ELASTICSEARCH));
- }
-
- private static class TestProcessBuilder implements JavaProcessLauncherImpl.ProcessBuilder {
- private List<String> commands = null;
- private File dir = null;
- private Boolean redirectErrorStream = null;
- private final Map<String, String> environment = new HashMap<>();
- private boolean started = false;
-
- @Override
- public List<String> command() {
- return commands;
- }
-
- @Override
- public TestProcessBuilder command(List<String> commands) {
- this.commands = commands;
- return this;
- }
-
- @Override
- public TestProcessBuilder directory(File dir) {
- this.dir = dir;
- return this;
- }
-
- @Override
- public Map<String, String> environment() {
- return environment;
- }
-
- @Override
- public TestProcessBuilder redirectErrorStream(boolean b) {
- this.redirectErrorStream = b;
- return this;
- }
-
- @Override
- public Process start() throws IOException {
- this.started = true;
- return mock(Process.class);
- }
- }
-}
--- /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.application.process;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+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.AllProcessesCommands;
+import org.sonar.process.ProcessId;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.data.MapEntry.entry;
+import static org.mockito.Mockito.RETURNS_MOCKS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ProcessLauncherImplTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private AllProcessesCommands commands = mock(AllProcessesCommands.class, RETURNS_MOCKS);
+
+ @Test
+ public void launch_forks_a_new_process() throws Exception {
+ File tempDir = temp.newFolder();
+ TestProcessBuilder processBuilder = new TestProcessBuilder();
+ ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> processBuilder);
+ JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
+ 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());
+
+ ProcessMonitor monitor = underTest.launch(command);
+
+ assertThat(monitor).isNotNull();
+ assertThat(processBuilder.started).isTrue();
+ assertThat(processBuilder.commands.get(0)).endsWith("java");
+ assertThat(processBuilder.commands).containsSequence(
+ "-Djava.io.tmpdir=" + tempDir.getAbsolutePath(),
+ "-cp",
+ "lib/*.class" + System.getProperty("path.separator") + "lib/*.jar",
+ "org.sonarqube.Main");
+ assertThat(processBuilder.dir).isEqualTo(command.getWorkDir());
+ assertThat(processBuilder.redirectErrorStream).isTrue();
+ assertThat(processBuilder.environment)
+ .contains(entry("VAR1", "valueOfVar1"))
+ .containsAllEntriesOf(command.getEnvVariables());
+ }
+
+ @Test
+ public void properties_are_passed_to_command_via_a_temporary_properties_file() throws Exception {
+ File tempDir = temp.newFolder();
+ TestProcessBuilder processBuilder = new TestProcessBuilder();
+ ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> processBuilder);
+ JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
+ command.setArgument("foo", "bar");
+ command.setArgument("baz", "woo");
+
+ underTest.launch(command);
+
+ String propsFilePath = processBuilder.commands.get(processBuilder.commands.size() - 1);
+ File file = new File(propsFilePath);
+ assertThat(file).exists().isFile();
+ try (FileReader reader = new FileReader(file)) {
+ Properties props = new Properties();
+ props.load(reader);
+ assertThat(props).containsOnly(
+ entry("foo", "bar"),
+ entry("baz", "woo"),
+ entry("process.terminationTimeout", "60000"),
+ entry("process.key", ProcessId.ELASTICSEARCH.getKey()),
+ entry("process.index", String.valueOf(ProcessId.ELASTICSEARCH.getIpcIndex())),
+ entry("process.sharedDir", tempDir.getAbsolutePath()));
+ }
+ }
+
+ @Test
+ public void throw_ISE_if_command_fails() throws IOException {
+ File tempDir = temp.newFolder();
+ ProcessLauncherImpl.ProcessBuilder processBuilder = mock(ProcessLauncherImpl.ProcessBuilder.class, RETURNS_MOCKS);
+ when(processBuilder.start()).thenThrow(new IOException("error"));
+ ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> processBuilder);
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Fail to launch process [es]");
+
+ underTest.launch(new JavaCommand(ProcessId.ELASTICSEARCH));
+ }
+
+ private static class TestProcessBuilder implements ProcessLauncherImpl.ProcessBuilder {
+ private List<String> commands = null;
+ private File dir = null;
+ private Boolean redirectErrorStream = null;
+ private final Map<String, String> environment = new HashMap<>();
+ private boolean started = false;
+
+ @Override
+ public List<String> command() {
+ return commands;
+ }
+
+ @Override
+ public TestProcessBuilder command(List<String> commands) {
+ this.commands = commands;
+ return this;
+ }
+
+ @Override
+ public TestProcessBuilder directory(File dir) {
+ this.dir = dir;
+ return this;
+ }
+
+ @Override
+ public Map<String, String> environment() {
+ return environment;
+ }
+
+ @Override
+ public TestProcessBuilder redirectErrorStream(boolean b) {
+ this.redirectErrorStream = b;
+ return this;
+ }
+
+ @Override
+ public Process start() throws IOException {
+ this.started = true;
+ return mock(Process.class);
+ }
+ }
+}
import org.sonar.application.config.AppSettingsLoaderImpl;
import org.sonar.application.process.JavaCommandFactory;
import org.sonar.application.process.JavaCommandFactoryImpl;
-import org.sonar.application.process.JavaProcessLauncher;
-import org.sonar.application.process.JavaProcessLauncherImpl;
+import org.sonar.application.process.ProcessLauncher;
+import org.sonar.application.process.ProcessLauncherImpl;
import org.sonar.application.process.StopRequestWatcher;
import org.sonar.application.process.StopRequestWatcherImpl;
import org.sonar.process.SystemExit;
JavaCommandFactory javaCommandFactory = new JavaCommandFactoryImpl(settings);
fileSystem.reset();
- try (JavaProcessLauncher javaProcessLauncher = new JavaProcessLauncherImpl(fileSystem.getTempDir())) {
- Scheduler scheduler = new SchedulerImpl(settings, appReloader, javaCommandFactory, javaProcessLauncher, appState);
+ try (ProcessLauncher processLauncher = new ProcessLauncherImpl(fileSystem.getTempDir())) {
+ Scheduler scheduler = new SchedulerImpl(settings, appReloader, javaCommandFactory, processLauncher, appState);
// intercepts CTRL-C
Runtime.getRuntime().addShutdownHook(new ShutdownHook(scheduler));