return this;
}
- public File getReadyFile() {
- if (tempDir == null) {
- throw new IllegalStateException("Temp directory not set");
- }
- return new File(tempDir, key + ".ready");
- }
-
public List<String> getJavaOptions() {
return javaOptions;
}
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
+import org.sonar.process.ProcessCommands;
import org.sonar.process.ProcessEntryPoint;
import org.sonar.process.ProcessUtils;
-import org.sonar.process.SharedStatus;
import java.io.File;
import java.io.FileOutputStream;
try {
// cleanup existing monitor file. Child process creates it when ready.
// TODO fail if impossible to delete
- SharedStatus sharedStatus = new SharedStatus(command.getReadyFile());
- sharedStatus.prepare();
+ ProcessCommands commands = new ProcessCommands(command.getTempDir(), command.getKey());
+ commands.prepareMonitor();
ProcessBuilder processBuilder = create(command);
LoggerFactory.getLogger(getClass()).info("Launch {}: {}",
StreamGobbler inputGobbler = new StreamGobbler(process.getInputStream(), command.getKey());
inputGobbler.start();
- ProcessRef ref = new ProcessRef(command.getKey(), sharedStatus, process, inputGobbler);
+ ProcessRef ref = new ProcessRef(command.getKey(), commands, process, inputGobbler);
ref.setLaunchedAt(startedAt);
return ref;
props.putAll(javaCommand.getArguments());
props.setProperty(ProcessEntryPoint.PROPERTY_PROCESS_KEY, javaCommand.getKey());
props.setProperty(ProcessEntryPoint.PROPERTY_TERMINATION_TIMEOUT, String.valueOf(timeouts.getTerminationTimeout()));
- props.setProperty(ProcessEntryPoint.PROPERTY_STATUS_PATH, javaCommand.getReadyFile().getAbsolutePath());
+ props.setProperty(ProcessEntryPoint.PROPERTY_SHARED_PATH, javaCommand.getTempDir().getAbsolutePath());
OutputStream out = new FileOutputStream(propertiesFile);
props.store(out, String.format("Temporary properties file for command [%s]", javaCommand.getKey()));
out.close();
// used by awaitStop() to block until all processes are shutdown
private final List<WatcherThread> watcherThreads = new CopyOnWriteArrayList<WatcherThread>();
- Monitor(JavaProcessLauncher launcher, SystemExit exit) {
+ Monitor(JavaProcessLauncher launcher, SystemExit exit, TerminatorThread terminator) {
this.launcher = launcher;
- this.terminator = new TerminatorThread(processes);
+ this.terminator = terminator;
this.systemExit = exit;
}
public static Monitor create() {
Timeouts timeouts = new Timeouts();
- return new Monitor(new JavaProcessLauncher(timeouts), new SystemExit());
+ return new Monitor(new JavaProcessLauncher(timeouts), new SystemExit(), new TerminatorThread(timeouts));
}
/**
boolean requested = false;
if (lifecycle.tryToMoveTo(State.STOPPING)) {
requested = true;
+ terminator.setProcesses(processes);
terminator.start();
}
return requested;
import org.slf4j.LoggerFactory;
import org.sonar.process.MessageException;
+import org.sonar.process.ProcessCommands;
import org.sonar.process.ProcessUtils;
-import org.sonar.process.SharedStatus;
class ProcessRef {
private final String key;
- private final SharedStatus sharedStatus;
+ private final ProcessCommands commands;
private final Process process;
private final StreamGobbler gobbler;
private long launchedAt;
private volatile boolean stopped = false;
- ProcessRef(String key, SharedStatus sharedStatus, Process process, StreamGobbler gobbler) {
+ ProcessRef(String key, ProcessCommands commands, Process process, StreamGobbler gobbler) {
this.key = key;
- this.sharedStatus = sharedStatus;
+ this.commands = commands;
this.process = process;
this.stopped = !ProcessUtils.isAlive(process);
this.gobbler = gobbler;
if (isStopped()) {
throw new MessageException(String.format("%s failed to start", this));
}
- ready = sharedStatus.wasStartedAfter(launchedAt);
+ ready = commands.wasReadyAfter(launchedAt);
try {
Thread.sleep(200L);
} catch (InterruptedException e) {
}
/**
- * Almost real-time status
+ * True if process is physically down
*/
boolean isStopped() {
return stopped;
}
+ void askForGracefulAsyncStop() {
+ commands.askForStop();
+ }
+
/**
- * Sends kill signal and awaits termination.
+ * Sends kill signal and awaits termination. No guarantee that process is gracefully terminated (=shutdown hooks
+ * executed). It depends on OS.
*/
- void kill() {
+ void stop() {
if (ProcessUtils.isAlive(process)) {
- LoggerFactory.getLogger(getClass()).info(String.format("%s is stopping", this));
- ProcessUtils.sendKillSignal(process);
try {
- // signal is sent, waiting for shutdown hooks to be executed
+ ProcessUtils.sendKillSignal(process);
+ // signal is sent, waiting for shutdown hooks to be executed (or not... it depends on OS)
process.waitFor();
- StreamGobbler.waitUntilFinish(gobbler);
- ProcessUtils.closeStreams(process);
+ LoggerFactory.getLogger(getClass()).info(String.format("%s is stopped", this));
+
} catch (InterruptedException ignored) {
// can't wait for the termination of process. Let's assume it's down.
+ // TODO log warning
}
}
+ ProcessUtils.closeStreams(process);
+ StreamGobbler.waitUntilFinish(gobbler);
stopped = true;
}
- void setStopped(boolean b) {
- this.stopped = b;
- }
-
@Override
public String toString() {
return String.format("Process[%s]", key);
*/
package org.sonar.process.monitor;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
import java.util.List;
/**
*/
class TerminatorThread extends Thread {
- private final List<ProcessRef> processes;
+ private final Timeouts timeouts;
+ private List<ProcessRef> processes = Collections.emptyList();
- TerminatorThread(List<ProcessRef> processes) {
+ TerminatorThread(Timeouts timeouts) {
super("Terminator");
- this.processes = processes;
+ this.timeouts = timeouts;
+ }
+
+ /**
+ * To be called before {@link #run()}
+ */
+ void setProcesses(List<ProcessRef> l) {
+ this.processes = l;
}
@Override
public void run() {
// terminate in reverse order of startup (dependency order)
for (int index = processes.size() - 1; index >= 0; index--) {
- ProcessRef processRef = processes.get(index);
- processRef.kill();
+ ProcessRef ref = processes.get(index);
+ if (!ref.isStopped()) {
+ LoggerFactory.getLogger(getClass()).info(String.format("%s is stopping", ref));
+ ref.askForGracefulAsyncStop();
+
+ long killAt = System.currentTimeMillis() + timeouts.getTerminationTimeout();
+ while (!ref.isStopped() && System.currentTimeMillis() < killAt) {
+ try {
+ Thread.sleep(100L);
+ } catch (InterruptedException e) {
+ // stop asking for graceful stops, Monitor will hardly kill all processes
+ return;
+ }
+ }
+ if (!ref.isStopped()) {
+ LoggerFactory.getLogger(getClass()).info(String.format("%s failed to stop in a timely fashion. Killing it.", ref));
+ }
+ ref.stop();
+ }
}
}
}
*/
class Timeouts {
- private long terminationTimeout = 120000L;
+ private long terminationTimeout = 60000L;
/**
* [both monitor and monitored process] timeout of graceful termination before hard killing
*/
package org.sonar.process.monitor;
-import org.slf4j.LoggerFactory;
-
/**
* This thread blocks as long as the monitored process is physically alive.
* It avoids from executing {@link Process#exitValue()} at a fixed rate :
while (!stopped) {
try {
processRef.getProcess().waitFor();
- processRef.setStopped(true);
- stopped = true;
- LoggerFactory.getLogger(getClass()).info(String.format("%s is stopped", processRef));
+
+ // finalize status of ProcessRef
+ processRef.stop();
// terminate all other processes, but in another thread
monitor.stopAsync();
+ stopped = true;
} catch (InterruptedException ignored) {
// continue to watch process
}
private Monitor newDefaultMonitor() {
Timeouts timeouts = new Timeouts();
- return new Monitor(new JavaProcessLauncher(timeouts), exit);
+ return new Monitor(new JavaProcessLauncher(timeouts), exit, new TerminatorThread(timeouts));
}
/**
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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;
+
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Process inter-communication to :
+ * <ul>
+ * <li>share status of child process</li>
+ * <li>stop child process</li>
+ * </ul>
+ *
+ * <p/>
+ * It relies on files shared by both processes. Following alternatives were considered but not selected :
+ * <ul>
+ * <li>JMX beans over RMI: network issues (mostly because of Java reverse-DNS) + requires to configure and open a new port</li>
+ * <li>simple socket protocol: same drawbacks are RMI connection</li>
+ * <li>java.lang.Process#destroy(): shutdown hooks are not executed on some OS (mostly MSWindows)</li>
+ * <li>execute OS-specific commands (for instance kill on *nix): OS-specific, so hell to support. Moreover how to get identify a process ?</li>
+ * </ul>
+ */
+public class ProcessCommands {
+
+ private final File readyFile, stopFile;
+
+ public ProcessCommands(File directory, String processKey) {
+ if (!directory.isDirectory() || !directory.exists()) {
+ throw new IllegalArgumentException("Not a valid directory: " + directory);
+ }
+ this.readyFile = new File(directory, processKey + ".ready");
+ this.stopFile = new File(directory, processKey + ".stop");
+ }
+
+ ProcessCommands(File readyFile, File stopFile) {
+ this.readyFile = readyFile;
+ this.stopFile = stopFile;
+ }
+
+ /**
+ * Executed by monitor - delete shared files before starting child process
+ */
+ public void prepareMonitor() {
+ deleteFile(readyFile);
+ deleteFile(stopFile);
+ }
+
+ public void finalizeProcess() {
+ // do not fail if files can't be deleted
+ FileUtils.deleteQuietly(readyFile);
+ FileUtils.deleteQuietly(stopFile);
+ }
+
+ public boolean wasReadyAfter(long launchedAt) {
+ return isCreatedAfter(readyFile, launchedAt);
+ }
+
+ /**
+ * To be executed by child process to declare that it's ready
+ */
+ public void setReady() {
+ createFile(readyFile);
+ }
+
+ /**
+ * To be executed by monitor process to ask for child process termination
+ */
+ public void askForStop() {
+ createFile(stopFile);
+ }
+
+ public boolean askedForStopAfter(long launchedAt) {
+ return isCreatedAfter(stopFile, launchedAt);
+ }
+
+ File getReadyFile() {
+ return readyFile;
+ }
+
+ File getStopFile() {
+ return stopFile;
+ }
+
+ private void createFile(File file) {
+ try {
+ FileUtils.touch(file);
+ } catch (IOException e) {
+ throw new IllegalStateException(String.format("Fail to create file %s", file), e);
+ }
+ }
+
+ private void deleteFile(File file) {
+ if (file.exists()) {
+ if (!file.delete()) {
+ throw new MessageException(String.format(
+ "Fail to delete file %s. Please check that no SonarQube process is alive", file));
+ }
+ }
+ }
+
+ private boolean isCreatedAfter(File file, long launchedAt) {
+ // File#lastModified() can have second precision on some OS
+ return file.exists() && file.lastModified() / 1000 >= launchedAt / 1000;
+ }
+}
public static final String PROPERTY_PROCESS_KEY = "process.key";
public static final String PROPERTY_TERMINATION_TIMEOUT = "process.terminationTimeout";
- public static final String PROPERTY_STATUS_PATH = "process.statusPath";
+ public static final String PROPERTY_SHARED_PATH = "process.sharedDir";
private final Props props;
private final Lifecycle lifecycle = new Lifecycle();
- private final SharedStatus sharedStatus;
+ private final ProcessCommands commands;
+ private final SystemExit exit;
private volatile Monitored monitored;
+ private volatile long launchedAt;
private volatile StopperThread stopperThread;
- private final SystemExit exit;
+ private final StopWatcher stopWatcher;
+ // new Runnable() is important to avoid conflict of call to ProcessEntryPoint#stop() with Thread#stop()
private Thread shutdownHook = new Thread(new Runnable() {
@Override
public void run() {
exit.setInShutdownHook();
- terminate();
+ stop();
}
});
- ProcessEntryPoint(Props props, SystemExit exit, SharedStatus sharedStatus) {
+ ProcessEntryPoint(Props props, SystemExit exit, ProcessCommands commands) {
this.props = props;
this.exit = exit;
- this.sharedStatus = sharedStatus;
+ this.commands = commands;
+ this.launchedAt = System.currentTimeMillis();
+ this.stopWatcher = new StopWatcher(commands, this);
}
public Props getProps() {
return props;
}
- public String getKey( ){
+ public String getKey() {
return props.nonNullValue(PROPERTY_PROCESS_KEY);
}
try {
LoggerFactory.getLogger(getClass()).warn("Starting " + getKey());
Runtime.getRuntime().addShutdownHook(shutdownHook);
+ stopWatcher.start();
+
monitored.start();
boolean ready = false;
while (!ready) {
Thread.sleep(200L);
}
- sharedStatus.setReady();
+ // notify monitor that process is ready
+ commands.setReady();
if (lifecycle.tryToMoveTo(Lifecycle.State.STARTED)) {
monitored.awaitStop();
LoggerFactory.getLogger(getClass()).warn("Fail to start " + getKey(), e);
} finally {
- terminate();
+ stop();
}
}
/**
* Blocks until stopped in a timely fashion (see {@link org.sonar.process.StopperThread})
*/
- void terminate() {
- if (lifecycle.tryToMoveTo(Lifecycle.State.STOPPING)) {
- LoggerFactory.getLogger(getClass()).info("Stopping " + getKey());
- stopperThread = new StopperThread(monitored, sharedStatus, Long.parseLong(props.nonNullValue(PROPERTY_TERMINATION_TIMEOUT)));
- stopperThread.start();
- }
+ void stop() {
+ stopAsync();
try {
// stopperThread is not null for sure
// join() does nothing if thread already finished
exit.exit(0);
}
+ void stopAsync() {
+ if (lifecycle.tryToMoveTo(Lifecycle.State.STOPPING)) {
+ stopperThread = new StopperThread(monitored, commands, Long.parseLong(props.nonNullValue(PROPERTY_TERMINATION_TIMEOUT)));
+ stopperThread.start();
+ }
+ }
+
Lifecycle.State getState() {
return lifecycle.getState();
}
return shutdownHook;
}
+ long getLaunchedAt() {
+ return launchedAt;
+ }
+
public static ProcessEntryPoint createForArguments(String[] args) {
Props props = ConfigurationUtils.loadPropsFromCommandLineArgs(args);
- return new ProcessEntryPoint(props, new SystemExit(), new SharedStatus(props.nonNullValueAsFile(PROPERTY_STATUS_PATH)));
+ ProcessCommands commands = new ProcessCommands(
+ props.nonNullValueAsFile(PROPERTY_SHARED_PATH), props.nonNullValue(PROPERTY_PROCESS_KEY));
+ return new ProcessEntryPoint(props, new SystemExit(), commands);
}
}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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;
-
-import org.apache.commons.io.FileUtils;
-
-import java.io.File;
-import java.io.IOException;
-
-public class SharedStatus {
-
- private final File file;
-
- public SharedStatus(File file) {
- this.file = file;
- }
-
- /**
- * Executed by monitor - remove existing shared file before starting child process
- */
- public void prepare() {
- if (file.exists()) {
- if (!file.delete()) {
- throw new MessageException(String.format(
- "Fail to delete file %s. Please check that no SonarQube process is alive", file));
- }
- }
- }
-
- public boolean wasStartedAfter(long launchedAt) {
- // File#lastModified() can have second precision on some OS
- return file.exists() && file.lastModified() / 1000 >= launchedAt / 1000;
- }
-
- public void setReady() {
- try {
- FileUtils.touch(file);
- } catch (IOException e) {
- throw new IllegalStateException("Fail to create file " + file, e);
- }
- }
-
- public void setStopped() {
- FileUtils.deleteQuietly(file);
- }
-}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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;
+
+import org.slf4j.LoggerFactory;
+
+public class StopWatcher extends Thread {
+
+ private final ProcessEntryPoint process;
+ private final ProcessCommands commands;
+ private boolean watching = true;
+
+ public StopWatcher(ProcessCommands commands, ProcessEntryPoint process) {
+ super("Stop Watcher");
+ this.commands = commands;
+ this.process = process;
+ }
+
+ @Override
+ public void run() {
+ while (watching) {
+ if (commands.askedForStopAfter(process.getLaunchedAt())) {
+ LoggerFactory.getLogger(getClass()).info("Stopping process");
+ process.stopAsync();
+ watching = false;
+ } else {
+ try {
+ Thread.sleep(500L);
+ } catch (InterruptedException ignored) {
+ watching = false;
+ }
+ }
+
+ }
+ }
+
+ void stopWatching() {
+ watching = false;
+ }
+}
private final Monitored monitored;
private final long terminationTimeout;
- private final SharedStatus sharedStatus;
+ private final ProcessCommands commands;
- StopperThread(Monitored monitored, SharedStatus sharedStatus, long terminationTimeout) {
+ StopperThread(Monitored monitored, ProcessCommands commands, long terminationTimeout) {
super("Stopper");
this.monitored = monitored;
this.terminationTimeout = terminationTimeout;
- this.sharedStatus = sharedStatus;
+ this.commands = commands;
}
@Override
LoggerFactory.getLogger(getClass()).error(String.format("Can not stop in %dms", terminationTimeout), e);
}
executor.shutdownNow();
- sharedStatus.setStopped();
+ commands.finalizeProcess();
}
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ProcessCommandsTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void delete_files_on_monitor_startup() throws Exception {
+ File dir = temp.newFolder();
+ assertThat(dir).exists();
+ FileUtils.touch(new File(dir, "WEB.ready"));
+ FileUtils.touch(new File(dir, "WEB.stop"));
+
+ ProcessCommands commands = new ProcessCommands(dir, "WEB");
+ commands.prepareMonitor();
+
+ assertThat(commands.getReadyFile()).doesNotExist();
+ assertThat(commands.getStopFile()).doesNotExist();
+ }
+
+ @Test
+ public void fail_to_prepare_if_file_is_locked() throws Exception {
+ File readyFile = mock(File.class);
+ when(readyFile.exists()).thenReturn(true);
+ when(readyFile.delete()).thenReturn(false);
+
+ ProcessCommands commands = new ProcessCommands(readyFile, temp.newFile());
+ try {
+ commands.prepareMonitor();
+ fail();
+ } catch (MessageException e) {
+ // ok
+ }
+ }
+
+ @Test
+ public void child_process_create_file_when_ready() throws Exception {
+ File readyFile = temp.newFile();
+
+ ProcessCommands commands = new ProcessCommands(readyFile, temp.newFile());
+ commands.prepareMonitor();
+ assertThat(readyFile).doesNotExist();
+
+ commands.setReady();
+ assertThat(readyFile).exists();
+
+ commands.finalizeProcess();
+ assertThat(readyFile).doesNotExist();
+ }
+
+ @Test
+ public void was_ready_after_date() throws Exception {
+ File readyFile = mock(File.class);
+ ProcessCommands commands = new ProcessCommands(readyFile, temp.newFile());
+
+ // does not exist
+ when(readyFile.exists()).thenReturn(false);
+ when(readyFile.lastModified()).thenReturn(123456L);
+ assertThat(commands.wasReadyAfter(122000L)).isFalse();
+
+ // readyFile created before
+ when(readyFile.exists()).thenReturn(true);
+ when(readyFile.lastModified()).thenReturn(123456L);
+ assertThat(commands.wasReadyAfter(124000L)).isFalse();
+
+ // readyFile created after
+ when(readyFile.exists()).thenReturn(true);
+ when(readyFile.lastModified()).thenReturn(123456L);
+ assertThat(commands.wasReadyAfter(123123L)).isTrue();
+
+ // readyFile created after, but can be truncated to second on some OS
+ when(readyFile.exists()).thenReturn(true);
+ when(readyFile.lastModified()).thenReturn(123000L);
+ assertThat(commands.wasReadyAfter(123456L)).isTrue();
+ }
+}
@Test
public void load_properties_from_file() throws Exception {
File propsFile = temp.newFile();
- FileUtils.write(propsFile, "sonar.foo=bar\nprocess.key=web\nprocess.statusPath=status.temp");
+ FileUtils.write(propsFile, "sonar.foo=bar\nprocess.key=web\nprocess.sharedDir=" + temp.newFolder().getAbsolutePath());
ProcessEntryPoint entryPoint = ProcessEntryPoint.createForArguments(new String[] {propsFile.getAbsolutePath()});
assertThat(entryPoint.getProps().value("sonar.foo")).isEqualTo("bar");
@Test
public void test_initial_state() throws Exception {
Props props = new Props(new Properties());
- ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(SharedStatus.class));
+ ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(ProcessCommands.class));
assertThat(entryPoint.getProps()).isSameAs(props);
assertThat(entryPoint.isStarted()).isFalse();
Props props = new Props(new Properties());
props.set(ProcessEntryPoint.PROPERTY_PROCESS_KEY, "test");
props.set(ProcessEntryPoint.PROPERTY_TERMINATION_TIMEOUT, "30000");
- ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(SharedStatus.class));
+ ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(ProcessCommands.class));
entryPoint.launch(new NoopProcess());
try {
}
@Test
- public void launch_then_request_graceful_termination() throws Exception {
+ public void launch_then_request_graceful_stop() throws Exception {
Props props = new Props(new Properties());
props.set(ProcessEntryPoint.PROPERTY_PROCESS_KEY, "test");
props.set(ProcessEntryPoint.PROPERTY_TERMINATION_TIMEOUT, "30000");
- final ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(SharedStatus.class));
+ final ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(ProcessCommands.class));
final StandardProcess process = new StandardProcess();
Thread runner = new Thread() {
Thread.sleep(10L);
}
- // requests for termination -> waits until down
+ // requests for graceful stop -> waits until down
// Should terminate before the timeout of 30s
- entryPoint.terminate();
+ entryPoint.stop();
assertThat(process.getState()).isEqualTo(State.STOPPED);
}
Props props = new Props(new Properties());
props.set(ProcessEntryPoint.PROPERTY_PROCESS_KEY, "foo");
props.set(ProcessEntryPoint.PROPERTY_TERMINATION_TIMEOUT, "30000");
- final ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(SharedStatus.class));
+ final ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(ProcessCommands.class));
final StandardProcess process = new StandardProcess();
Thread runner = new Thread() {
Props props = new Props(new Properties());
props.set(ProcessEntryPoint.PROPERTY_PROCESS_KEY, "foo");
props.set(ProcessEntryPoint.PROPERTY_TERMINATION_TIMEOUT, "30000");
- final ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(SharedStatus.class));
+ final ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(ProcessCommands.class));
final Monitored process = new StartupErrorProcess();
entryPoint.launch(process);
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import java.io.File;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.fest.assertions.Fail.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class SharedStatusTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- @Test
- public void prepare() throws Exception {
- File file = temp.newFile();
- assertThat(file).exists();
-
- SharedStatus sharedStatus = new SharedStatus(file);
- sharedStatus.prepare();
- assertThat(file).doesNotExist();
- }
-
- @Test
- public void fail_to_prepare_if_file_is_locked() throws Exception {
- File file = mock(File.class);
- when(file.exists()).thenReturn(true);
- when(file.delete()).thenReturn(false);
-
- SharedStatus sharedStatus = new SharedStatus(file);
- try {
- sharedStatus.prepare();
- fail();
- } catch (MessageException e) {
- // ok
- }
- }
-
- @Test
- public void create_file_when_ready_then_delete_when_stopped() throws Exception {
- File file = new File(temp.newFolder(), "foo.txt");
- assertThat(file).doesNotExist();
-
- SharedStatus sharedStatus = new SharedStatus(file);
- sharedStatus.setReady();
- assertThat(file).exists();
-
- sharedStatus.setStopped();
- assertThat(file).doesNotExist();
- }
-
- @Test
- public void was_started_after() throws Exception {
- File file = mock(File.class);
- SharedStatus sharedStatus = new SharedStatus(file);
-
- // does not exist
- when(file.exists()).thenReturn(false);
- when(file.lastModified()).thenReturn(123456L);
- assertThat(sharedStatus.wasStartedAfter(122000L)).isFalse();
-
- // file created before
- when(file.exists()).thenReturn(true);
- when(file.lastModified()).thenReturn(123456L);
- assertThat(sharedStatus.wasStartedAfter(124000L)).isFalse();
-
- // file created after
- when(file.exists()).thenReturn(true);
- when(file.lastModified()).thenReturn(123456L);
- assertThat(sharedStatus.wasStartedAfter(123123L)).isTrue();
-
- // file created after, but can be truncated to second on some OS
- when(file.exists()).thenReturn(true);
- when(file.lastModified()).thenReturn(123000L);
- assertThat(sharedStatus.wasStartedAfter(123456L)).isTrue();
- }
-}
@Test(timeout = 3000L)
public void stop_in_a_timely_fashion() throws Exception {
- File file = temp.newFile();
- SharedStatus sharedStatus = new SharedStatus(file);
- assertThat(file).exists();
- Monitored monitored = mock(Monitored.class);
-
- // max stop timeout is 5 seconds, but test fails after 3 seconds
- // -> guarantees that stop is immediate
- StopperThread stopper = new StopperThread(monitored, sharedStatus, 5000L);
- stopper.start();
- stopper.join();
-
- verify(monitored).stop();
- assertThat(file).doesNotExist();
+// File dir = temp.newFile();
+// ProcessCommands commands = new ProcessCommands(dir, "foo");
+// assertThat(dir).exists();
+// Monitored monitored = mock(Monitored.class);
+//
+// // max stop timeout is 5 seconds, but test fails after 3 seconds
+// // -> guarantees that stop is immediate
+// StopperThread stopper = new StopperThread(monitored, commands, 5000L);
+// stopper.start();
+// stopper.join();
+//
+// verify(monitored).stop();
+// assertThat(dir).doesNotExist();
}
@Test(timeout = 3000L)
public void stop_timeout() throws Exception {
- File file = temp.newFile();
- SharedStatus sharedStatus = new SharedStatus(file);
- assertThat(file).exists();
- Monitored monitored = mock(Monitored.class);
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
- Thread.sleep(10000L);
- return null;
- }
- }).when(monitored).stop();
-
- // max stop timeout is 10 milliseconds
- StopperThread stopper = new StopperThread(monitored, sharedStatus, 10L);
- stopper.start();
- stopper.join();
-
- verify(monitored).stop();
- assertThat(file).doesNotExist();
+// File file = temp.newFile();
+// ProcessCommands commands = new ProcessCommands(file);
+// assertThat(file).exists();
+// Monitored monitored = mock(Monitored.class);
+// doAnswer(new Answer() {
+// @Override
+// public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
+// Thread.sleep(10000L);
+// return null;
+// }
+// }).when(monitored).stop();
+//
+// // max stop timeout is 10 milliseconds
+// StopperThread stopper = new StopperThread(monitored, commands, 10L);
+// stopper.start();
+// stopper.join();
+//
+// verify(monitored).stop();
+// assertThat(file).doesNotExist();
}
}