aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-process-monitor
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-process-monitor')
-rw-r--r--server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaCommand.java11
-rw-r--r--server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaProcessLauncher.java30
-rw-r--r--server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/Monitor.java78
-rw-r--r--server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaCommandTest.java3
-rw-r--r--server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaProcessLauncherTest.java12
-rw-r--r--server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/MonitorTest.java92
6 files changed, 147 insertions, 79 deletions
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaCommand.java b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaCommand.java
index 9187e0305ea..8a5551039d5 100644
--- a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaCommand.java
+++ b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaCommand.java
@@ -52,8 +52,6 @@ public class JavaCommand {
private final Map<String, String> envVariables = new HashMap<>(System.getenv());
- private File tempDir = null;
-
private int processIndex = -1;
public JavaCommand(String key) {
@@ -78,15 +76,6 @@ public class JavaCommand {
return this;
}
- public File getTempDir() {
- return tempDir;
- }
-
- public JavaCommand setTempDir(File tempDir) {
- this.tempDir = tempDir;
- return this;
- }
-
public List<String> getJavaOptions() {
return javaOptions;
}
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaProcessLauncher.java b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaProcessLauncher.java
index 6a2eb83bb44..9bf969cd944 100644
--- a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaProcessLauncher.java
+++ b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaProcessLauncher.java
@@ -19,13 +19,6 @@
*/
package org.sonar.process.monitor;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.LoggerFactory;
-import org.sonar.process.DefaultProcessCommands;
-import org.sonar.process.ProcessCommands;
-import org.sonar.process.ProcessEntryPoint;
-import org.sonar.process.ProcessUtils;
-
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
@@ -33,20 +26,33 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.LoggerFactory;
+import org.sonar.process.AllProcessesCommands;
+import org.sonar.process.ProcessCommands;
+import org.sonar.process.ProcessEntryPoint;
+import org.sonar.process.ProcessUtils;
public class JavaProcessLauncher {
private final Timeouts timeouts;
+ private final File tempDir;
+ private final AllProcessesCommands allProcessesCommands;
- public JavaProcessLauncher(Timeouts timeouts) {
+ public JavaProcessLauncher(Timeouts timeouts, File tempDir, AllProcessesCommands allProcessesCommands) {
this.timeouts = timeouts;
+ this.tempDir = tempDir;
+ this.allProcessesCommands = allProcessesCommands;
+ }
+
+ public void close() {
+ allProcessesCommands.close();
}
ProcessRef launch(JavaCommand command) {
Process process = null;
try {
- // cleanup existing monitor files
- ProcessCommands commands = new DefaultProcessCommands(command.getTempDir(), command.getProcessIndex());
+ ProcessCommands commands = allProcessesCommands.getProcessCommand(command.getProcessIndex(), true);
ProcessBuilder processBuilder = create(command);
LoggerFactory.getLogger(getClass()).info("Launch process[{}]: {}",
@@ -69,7 +75,7 @@ public class JavaProcessLauncher {
commands.add(buildJavaPath());
commands.addAll(javaCommand.getJavaOptions());
// TODO warning - does it work if temp dir contains a whitespace ?
- commands.add(String.format("-Djava.io.tmpdir=%s", javaCommand.getTempDir().getAbsolutePath()));
+ commands.add(String.format("-Djava.io.tmpdir=%s", tempDir.getAbsolutePath()));
commands.addAll(buildClasspath(javaCommand));
commands.add(javaCommand.getClassName());
commands.add(buildPropertiesFile(javaCommand).getAbsolutePath());
@@ -101,7 +107,7 @@ public class JavaProcessLauncher {
props.setProperty(ProcessEntryPoint.PROPERTY_PROCESS_KEY, javaCommand.getKey());
props.setProperty(ProcessEntryPoint.PROPERTY_PROCESS_INDEX, Integer.toString(javaCommand.getProcessIndex()));
props.setProperty(ProcessEntryPoint.PROPERTY_TERMINATION_TIMEOUT, String.valueOf(timeouts.getTerminationTimeout()));
- props.setProperty(ProcessEntryPoint.PROPERTY_SHARED_PATH, javaCommand.getTempDir().getAbsolutePath());
+ props.setProperty(ProcessEntryPoint.PROPERTY_SHARED_PATH, tempDir.getAbsolutePath());
OutputStream out = new FileOutputStream(propertiesFile);
props.store(out, String.format("Temporary properties file for command [%s]", javaCommand.getKey()));
out.close();
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/Monitor.java b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/Monitor.java
index 749389d1054..7dcf8ede0d0 100644
--- a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/Monitor.java
+++ b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/Monitor.java
@@ -19,6 +19,7 @@
*/
package org.sonar.process.monitor;
+import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -27,6 +28,7 @@ import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.sonar.process.AllProcessesCommands;
import org.sonar.process.Lifecycle;
import org.sonar.process.Lifecycle.State;
import org.sonar.process.ProcessCommands;
@@ -37,9 +39,11 @@ public class Monitor {
private static final Logger LOG = LoggerFactory.getLogger(Monitor.class);
private static final Timeouts TIMEOUTS = new Timeouts();
private static final long WATCH_DELAY_MS = 500L;
+ private static final int CURRENT_PROCESS_NUMBER = 0;
private static int restartorInstanceCounter = 0;
+ private final AllProcessesCommands allProcessesCommands;
private final JavaProcessLauncher launcher;
private final SystemExit systemExit;
@@ -53,15 +57,18 @@ public class Monitor {
private List<JavaCommand> javaCommands;
@CheckForNull
private RestartorThread restartor;
+ @CheckForNull
+ HardStopWatcherThread hardStopWatcher;
static int nextProcessId = 1;
- Monitor(JavaProcessLauncher launcher, SystemExit exit) {
- this.launcher = launcher;
+ Monitor(File tempDir, SystemExit exit) {
+ this.allProcessesCommands = new AllProcessesCommands(tempDir);
+ this.launcher = new JavaProcessLauncher(TIMEOUTS, tempDir, allProcessesCommands);
this.systemExit = exit;
}
- public static Monitor create() {
- return new Monitor(new JavaProcessLauncher(TIMEOUTS), new SystemExit());
+ public static Monitor create(File tempDir) {
+ return new Monitor(tempDir, new SystemExit());
}
/**
@@ -108,6 +115,12 @@ public class Monitor {
}
}
+ public void watchForHardStop() {
+ ProcessCommands processCommand = this.allProcessesCommands.getProcessCommand(CURRENT_PROCESS_NUMBER, true);
+ this.hardStopWatcher = new HardStopWatcherThread(processCommand);
+ this.hardStopWatcher.start();
+ }
+
private void monitor(ProcessRef processRef) {
// physically watch if process is alive
WatcherThread watcherThread = new WatcherThread(processRef, this);
@@ -145,7 +158,9 @@ public class Monitor {
boolean waitForOneRestart() {
boolean restartRequested = awaitChildProcessesTermination();
+ trace("finished waiting, restartRequested=" + restartRequested);
if (restartRequested) {
+ trace("awaitTermination(restartor)=" + restartor);
awaitTermination(restartor);
}
return restartRequested;
@@ -176,8 +191,8 @@ public class Monitor {
* Blocks until all processes are terminated.
*/
public void stop() {
- trace("start stop async...");
- stopAsync();
+ trace("start hard stop async...");
+ stopAsync(State.HARD_STOPPING);
trace("await termination of terminator...");
awaitTermination(terminator);
cleanAfterTermination();
@@ -187,17 +202,18 @@ public class Monitor {
private void cleanAfterTermination() {
trace("go to STOPPED...");
- // safeguard if TerminatorThread is buggy and stop restartWatcher
if (lifecycle.tryToMoveTo(State.STOPPED)) {
trace("await termination of restartWatcher...");
- // wait for restartWatcher to cleanly stop
- awaitTermination(restartWatcher);
+ // wait for restartWatcher and hardStopWatcher to cleanly stop
+ awaitTermination(restartWatcher, hardStopWatcher);
trace("restartWatcher done");
// removing shutdown hook to avoid called stop() unnecessarily unless already in shutdownHook
if (!systemExit.isInShutdownHook()) {
trace("removing shutdown hook...");
Runtime.getRuntime().removeShutdownHook(shutdownHook);
}
+ // cleanly close JavaLauncher
+ launcher.close();
}
}
@@ -207,7 +223,11 @@ public class Monitor {
* this call will be blocking until the previous request finishes.
*/
public void stopAsync() {
- if (lifecycle.tryToMoveTo(State.STOPPING)) {
+ stopAsync(State.STOPPING);
+ }
+
+ private void stopAsync(State stoppingState) {
+ if (lifecycle.tryToMoveTo(stoppingState)) {
terminator.start();
}
}
@@ -288,12 +308,38 @@ public class Monitor {
}
+ public class HardStopWatcherThread extends Thread {
+ private final ProcessCommands processCommands;
+
+ public HardStopWatcherThread(ProcessCommands processCommands) {
+ super("Hard stop watcher");
+ this.processCommands = processCommands;
+ }
+
+ @Override
+ public void run() {
+ while (lifecycle.getState() != Lifecycle.State.STOPPED) {
+ if (processCommands.askedForStop()) {
+ trace("Stopping process");
+ Monitor.this.stop();
+ } else {
+ try {
+ Thread.sleep(WATCH_DELAY_MS);
+ } catch (InterruptedException ignored) {
+ // keep watching
+ }
+ }
+ }
+ }
+
+ }
+
private void stopProcesses() {
- List<WatcherThread> watcherThreads = new ArrayList<>(this.watcherThreads);
+ List<WatcherThread> watcherThreadsCopy = new ArrayList<>(this.watcherThreads);
// create a copy and reverse it to terminate in reverse order of startup (dependency order)
- Collections.reverse(watcherThreads);
+ Collections.reverse(watcherThreadsCopy);
- for (WatcherThread watcherThread : watcherThreads) {
+ for (WatcherThread watcherThread : watcherThreadsCopy) {
ProcessRef ref = watcherThread.getProcessRef();
if (!ref.isStopped()) {
LOG.info("{} is stopping", ref);
@@ -339,6 +385,12 @@ public class Monitor {
}
}
+ private static void awaitTermination(Thread... threads) {
+ for (Thread thread : threads) {
+ awaitTermination(thread);
+ }
+ }
+
private static void awaitTermination(@Nullable Thread t) {
if (t == null) {
return;
diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaCommandTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaCommandTest.java
index cc376db2d35..5e36ef6c797 100644
--- a/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaCommandTest.java
+++ b/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaCommandTest.java
@@ -44,8 +44,6 @@ public class JavaCommandTest {
command.setClassName("org.sonar.ElasticSearch");
command.setEnvVariable("JAVA_COMMAND_TEST", "1000");
- File tempDir = temp.newFolder();
- command.setTempDir(tempDir);
File workDir = temp.newFolder();
command.setWorkDir(workDir);
command.addClasspath("lib/*.jar");
@@ -56,7 +54,6 @@ public class JavaCommandTest {
assertThat(command.getClasspath()).containsOnly("lib/*.jar", "conf/*.xml");
assertThat(command.getJavaOptions()).containsOnly("-Xmx128m");
assertThat(command.getWorkDir()).isSameAs(workDir);
- assertThat(command.getTempDir()).isSameAs(tempDir);
assertThat(command.getClassName()).isEqualTo("org.sonar.ElasticSearch");
// copy current env variables
diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaProcessLauncherTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaProcessLauncherTest.java
index a4c2c82ca64..cdd723c6fb4 100644
--- a/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaProcessLauncherTest.java
+++ b/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaProcessLauncherTest.java
@@ -19,17 +19,25 @@
*/
package org.sonar.process.monitor;
+import java.io.File;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.process.AllProcessesCommands;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public class JavaProcessLauncherTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
@Test
- public void fail_to_launch() {
+ public void fail_to_launch() throws Exception {
+ File tempDir = temp.newFolder();
JavaCommand command = new JavaCommand("test");
- JavaProcessLauncher launcher = new JavaProcessLauncher(new Timeouts());
+ JavaProcessLauncher launcher = new JavaProcessLauncher(new Timeouts(), tempDir, new AllProcessesCommands(tempDir));
try {
// command is not correct (missing options), java.lang.ProcessBuilder#start()
// throws an exception
diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/MonitorTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/MonitorTest.java
index a9937311e1e..038d9aa64e1 100644
--- a/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/MonitorTest.java
+++ b/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/MonitorTest.java
@@ -32,6 +32,7 @@ import org.apache.commons.lang.StringUtils;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.internal.Longs;
import org.junit.After;
+import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
@@ -90,6 +91,14 @@ public class MonitorTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
+ private File tempDir;
+
+ @Before
+ public void setUp() throws Exception {
+ tempDir = temp.newFolder();
+
+ }
+
/**
* Safeguard
*/
@@ -104,8 +113,8 @@ public class MonitorTest {
}
@Test
- public void fail_to_start_if_no_commands() {
- monitor = newDefaultMonitor();
+ public void fail_to_start_if_no_commands() throws Exception {
+ monitor = newDefaultMonitor(tempDir);
try {
monitor.start(Collections.<JavaCommand>emptyList());
fail();
@@ -116,7 +125,7 @@ public class MonitorTest {
@Test
public void fail_to_start_multiple_times() throws Exception {
- monitor = newDefaultMonitor();
+ monitor = newDefaultMonitor(tempDir);
monitor.start(Arrays.asList(newStandardProcessCommand()));
boolean failed = false;
try {
@@ -130,8 +139,8 @@ public class MonitorTest {
@Test
public void start_then_stop_gracefully() throws Exception {
- monitor = newDefaultMonitor();
- HttpProcessClient client = new HttpProcessClient("test");
+ monitor = newDefaultMonitor(tempDir);
+ HttpProcessClient client = new HttpProcessClient(tempDir, "test");
// blocks until started
monitor.start(Arrays.asList(client.newCommand()));
@@ -148,9 +157,9 @@ public class MonitorTest {
@Test
public void start_then_stop_sequence_of_commands() throws Exception {
- monitor = newDefaultMonitor();
- HttpProcessClient p1 = new HttpProcessClient("p1");
- HttpProcessClient p2 = new HttpProcessClient("p2");
+ monitor = newDefaultMonitor(tempDir);
+ HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1");
+ HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2");
monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
// start p2 when p1 is fully started (ready)
@@ -173,9 +182,9 @@ public class MonitorTest {
@Test
public void stop_all_processes_if_monitor_shutdowns() throws Exception {
- monitor = newDefaultMonitor();
- HttpProcessClient p1 = new HttpProcessClient("p1");
- HttpProcessClient p2 = new HttpProcessClient("p2");
+ monitor = newDefaultMonitor(tempDir);
+ HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1");
+ HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2");
monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
assertThat(p1).isReady();
assertThat(p2).isReady();
@@ -190,9 +199,9 @@ public class MonitorTest {
@Test
public void restart_all_processes_if_one_asks_for_restart() throws Exception {
- monitor = newDefaultMonitor();
- HttpProcessClient p1 = new HttpProcessClient("p1");
- HttpProcessClient p2 = new HttpProcessClient("p2");
+ monitor = newDefaultMonitor(tempDir);
+ HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1");
+ HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2");
monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
assertThat(p1).isReady();
@@ -221,9 +230,9 @@ public class MonitorTest {
@Test
public void stop_all_processes_if_one_shutdowns() throws Exception {
- monitor = newDefaultMonitor();
- HttpProcessClient p1 = new HttpProcessClient("p1");
- HttpProcessClient p2 = new HttpProcessClient("p2");
+ monitor = newDefaultMonitor(tempDir);
+ HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1");
+ HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2");
monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
assertThat(p1.isReady()).isTrue();
assertThat(p2.isReady()).isTrue();
@@ -242,9 +251,9 @@ public class MonitorTest {
@Test
public void stop_all_processes_if_one_fails_to_start() throws Exception {
- monitor = newDefaultMonitor();
- HttpProcessClient p1 = new HttpProcessClient("p1");
- HttpProcessClient p2 = new HttpProcessClient("p2", -1);
+ monitor = newDefaultMonitor(tempDir);
+ HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1");
+ HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2", -1);
try {
monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
fail();
@@ -260,11 +269,11 @@ public class MonitorTest {
}
@Test
- public void test_too_many_processes() {
+ public void test_too_many_processes() throws Exception {
while (Monitor.getNextProcessId() < ProcessCommands.MAX_PROCESSES - 1) {
}
try {
- newDefaultMonitor();
+ newDefaultMonitor(tempDir);
} catch (IllegalStateException e) {
assertThat(e).hasMessageStartingWith("The maximum number of processes launched has been reached ");
} finally {
@@ -274,11 +283,10 @@ public class MonitorTest {
@Test
public void fail_to_start_if_bad_class_name() throws Exception {
- monitor = newDefaultMonitor();
+ monitor = newDefaultMonitor(tempDir);
JavaCommand command = new JavaCommand("test")
.addClasspath(testJar.getAbsolutePath())
- .setClassName("org.sonar.process.test.Unknown")
- .setTempDir(temp.newFolder());
+ .setClassName("org.sonar.process.test.Unknown");
try {
monitor.start(Arrays.asList(command));
@@ -289,9 +297,19 @@ public class MonitorTest {
}
}
- private Monitor newDefaultMonitor() {
- Timeouts timeouts = new Timeouts();
- return new Monitor(new JavaProcessLauncher(timeouts), exit);
+ @Test
+ public void watchForHardStop_adds_a_hardStopWatcher_thread_and_starts_it() throws Exception {
+ Monitor monitor = newDefaultMonitor(tempDir);
+ assertThat(monitor.hardStopWatcher).isNull();
+
+ monitor.watchForHardStop();
+
+ assertThat(monitor.hardStopWatcher).isNotNull();
+ assertThat(monitor.hardStopWatcher.isAlive()).isTrue();
+ }
+
+ private Monitor newDefaultMonitor(File tempDir) throws IOException {
+ return new Monitor(tempDir, exit);
}
/**
@@ -302,16 +320,16 @@ public class MonitorTest {
private final String commandKey;
private final File tempDir;
- private HttpProcessClient(String commandKey) throws IOException {
- this(commandKey, NetworkUtils.freePort());
+ private HttpProcessClient(File tempDir, String commandKey) throws IOException {
+ this(tempDir, commandKey, NetworkUtils.freePort());
}
/**
* Use httpPort=-1 to make server fail to start
*/
- private HttpProcessClient(String commandKey, int httpPort) throws IOException {
+ private HttpProcessClient(File tempDir, String commandKey, int httpPort) throws IOException {
+ this.tempDir = tempDir;
this.commandKey = commandKey;
- this.tempDir = temp.newFolder(commandKey);
this.httpPort = httpPort;
}
@@ -319,8 +337,7 @@ public class MonitorTest {
return new JavaCommand(commandKey)
.addClasspath(testJar.getAbsolutePath())
.setClassName("org.sonar.process.test.HttpProcess")
- .setArgument("httpPort", String.valueOf(httpPort))
- .setTempDir(tempDir);
+ .setArgument("httpPort", String.valueOf(httpPort));
}
/**
@@ -386,7 +403,7 @@ public class MonitorTest {
private List<Long> readTimeFromFile(String filename) {
try {
- File file = new File(tempDir, filename);
+ File file = new File(tempDir, httpPort + "-" + filename);
if (file.isFile() && file.exists()) {
String[] split = StringUtils.split(FileUtils.readFileToString(file), ',');
List<Long> res = new ArrayList<>(split.length);
@@ -402,7 +419,7 @@ public class MonitorTest {
}
private boolean fileExists(String filename) {
- File file = new File(tempDir, filename);
+ File file = new File(tempDir, httpPort + "-" + filename);
return file.isFile() && file.exists();
}
}
@@ -542,8 +559,7 @@ public class MonitorTest {
private JavaCommand newStandardProcessCommand() throws IOException {
return new JavaCommand("standard")
.addClasspath(testJar.getAbsolutePath())
- .setClassName("org.sonar.process.test.StandardProcess")
- .setTempDir(temp.newFolder());
+ .setClassName("org.sonar.process.test.StandardProcess");
}
}