aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2017-02-21 14:51:34 +0100
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>2017-02-24 21:08:18 +0100
commit679db32dc10eebf6e7d8cfddc44aa719696485ed (patch)
tree94b3022c04611200761432d6ab45d56584702044
parentf4b7d2d78416ba858d2c2f7998f3c320c95ad1e4 (diff)
downloadsonarqube-679db32dc10eebf6e7d8cfddc44aa719696485ed.tar.gz
sonarqube-679db32dc10eebf6e7d8cfddc44aa719696485ed.zip
SONAR-8435 log "SonarQube is up" if all processes are operational
which implies: 1/ to distinguish from Monitored#getStatus() = UP and OPERATIONAL 2/ to have an option of Monitor to wait on process's status to be OPERATIONAL 3/ every Monitored implementation must return OPERATIONAL rather than UP if then don't make a distinction between the two
-rw-r--r--it/it-tests/src/test/java/it/serverSystem/ClusterTest.java2
-rw-r--r--server/sonar-ce/src/main/java/org/sonar/ce/app/CeServer.java4
-rw-r--r--server/sonar-ce/src/test/java/org/sonar/ce/app/CeServerTest.java18
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationEngineImpl.java3
-rw-r--r--server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/Monitor.java107
-rw-r--r--server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/MonitorTest.java8
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/Lifecycle.java6
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/Monitored.java6
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java51
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/LifecycleTest.java26
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java2
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/test/HttpProcess.java4
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/test/InfiniteTerminationProcess.java2
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/test/StandardProcess.java2
-rw-r--r--server/sonar-search/src/main/java/org/sonar/search/SearchServer.java5
-rw-r--r--server/sonar-search/src/test/java/org/sonar/search/SearchServerTest.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/app/WebServer.java18
-rw-r--r--sonar-application/src/main/java/org/sonar/application/App.java10
18 files changed, 209 insertions, 67 deletions
diff --git a/it/it-tests/src/test/java/it/serverSystem/ClusterTest.java b/it/it-tests/src/test/java/it/serverSystem/ClusterTest.java
index c9e37f83611..56ecaf34183 100644
--- a/it/it-tests/src/test/java/it/serverSystem/ClusterTest.java
+++ b/it/it-tests/src/test/java/it/serverSystem/ClusterTest.java
@@ -107,7 +107,7 @@ public class ClusterTest {
.setServerProperty("sonar.cluster.ce.disabled", "true")
// override the default watcher provided by Orchestrator
// which waits for Compute Engine to be up
- .setStartupLogWatcher(log -> log.contains("Process[web] is up"))
+ .setStartupLogWatcher(log -> log.contains("SonarQube is up"))
.build();
web.start();
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/app/CeServer.java b/server/sonar-ce/src/main/java/org/sonar/ce/app/CeServer.java
index 1ae2b34143e..ee1288ebca3 100644
--- a/server/sonar-ce/src/main/java/org/sonar/ce/app/CeServer.java
+++ b/server/sonar-ce/src/main/java/org/sonar/ce/app/CeServer.java
@@ -79,10 +79,10 @@ public class CeServer implements Monitored {
@Override
public Status getStatus() {
- checkState(ceMainThread != null, "isUp() can not be called before start()");
+ checkState(ceMainThread != null, "getStatus() can not be called before start()");
if (ceMainThread.isStarted()) {
- return Status.UP;
+ return Status.OPERATIONAL;
}
return Status.DOWN;
}
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/app/CeServerTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/app/CeServerTest.java
index 0a4f46caab0..35ff680c919 100644
--- a/server/sonar-ce/src/test/java/org/sonar/ce/app/CeServerTest.java
+++ b/server/sonar-ce/src/test/java/org/sonar/ce/app/CeServerTest.java
@@ -92,17 +92,17 @@ public class CeServerTest {
}
@Test
- public void isUp_throws_ISE_when_called_before_start() throws IOException {
+ public void getStatus_throws_ISE_when_called_before_start() throws IOException {
CeServer ceServer = newCeServer();
expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("isUp() can not be called before start()");
+ expectedException.expectMessage("getStatus() can not be called before start()");
ceServer.getStatus();
}
@Test
- public void isUp_does_not_return_true_until_ComputeEngine_startup_returns() throws InterruptedException, IOException {
+ public void getStatus_does_not_return_OPERATIONAL_until_ComputeEngine_startup_returns() throws InterruptedException, IOException {
BlockingStartupComputeEngine computeEngine = new BlockingStartupComputeEngine(null);
CeServer ceServer = newCeServer(computeEngine);
@@ -116,11 +116,11 @@ public class CeServerTest {
while (ceServer.getStatus() == Monitored.Status.DOWN) {
// wait for isReady to change to true, otherwise test will fail with timeout
}
- assertThat(ceServer.getStatus()).isEqualTo(Monitored.Status.UP);
+ assertThat(ceServer.getStatus()).isEqualTo(Monitored.Status.OPERATIONAL);
}
@Test
- public void isUp_returns_true_when_ComputeEngine_startup_throws_any_Exception_or_Error() throws InterruptedException, IOException {
+ public void getStatus_returns_OPERATIONAL_when_ComputeEngine_startup_throws_any_Exception_or_Error() throws InterruptedException, IOException {
Throwable startupException = new Throwable("Faking failing ComputeEngine#startup()");
BlockingStartupComputeEngine computeEngine = new BlockingStartupComputeEngine(startupException);
@@ -134,13 +134,13 @@ public class CeServerTest {
computeEngine.releaseStartup();
while (ceServer.getStatus() == Monitored.Status.DOWN) {
- // wait for isReady to change to true, otherwise test will fail with timeout
+ // wait for isReady to change to not DOWN, otherwise test will fail with timeout
}
- assertThat(ceServer.getStatus()).isEqualTo(Monitored.Status.UP);
+ assertThat(ceServer.getStatus()).isEqualTo(Monitored.Status.OPERATIONAL);
}
@Test
- public void isUp_returns_true_when_waiting_for_WebServer_failed() throws InterruptedException {
+ public void getStatus_returns_OPERATIONAL_when_waiting_for_WebServer_failed() throws InterruptedException {
final CountDownLatch webServerWatcherCalled = new CountDownLatch(1);
CeServer ceServer = newCeServer(() -> {
webServerWatcherCalled.countDown();
@@ -149,7 +149,7 @@ public class CeServerTest {
ceServer.start();
ceServer.awaitStop();
- assertThat(ceServer.getStatus()).isEqualTo(Monitored.Status.UP);
+ assertThat(ceServer.getStatus()).isEqualTo(Monitored.Status.OPERATIONAL);
}
@Test
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationEngineImpl.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationEngineImpl.java
index d062656a12a..27e6ece8f61 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationEngineImpl.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationEngineImpl.java
@@ -31,7 +31,8 @@ public class MigrationEngineImpl implements MigrationEngine {
private final MigrationContainerPopulator populator;
private final MigrationSteps migrationSteps;
- public MigrationEngineImpl(MigrationHistory migrationHistory, ComponentContainer serverContainer, MigrationContainerPopulator populator, MigrationSteps migrationSteps) {
+ public MigrationEngineImpl(MigrationHistory migrationHistory, ComponentContainer serverContainer,
+ MigrationContainerPopulator populator, MigrationSteps migrationSteps) {
this.migrationHistory = migrationHistory;
this.serverContainer = serverContainer;
this.populator = populator;
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 44a29b36235..42dd1a20487 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
@@ -24,7 +24,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.CheckForNull;
import org.slf4j.Logger;
@@ -36,6 +35,7 @@ import org.sonar.process.ProcessId;
import org.sonar.process.ProcessUtils;
import org.sonar.process.SystemExit;
+import static java.util.Objects.requireNonNull;
import static org.sonar.process.DefaultProcessCommands.reset;
public class Monitor {
@@ -51,6 +51,7 @@ public class Monitor {
private final SystemExit systemExit;
private final boolean watchForHardStop;
private final Thread shutdownHook = new Thread(new MonitorShutdownHook(), "Monitor Shutdown Hook");
+ private final boolean waitForOperational;
private final List<WatcherThread> watcherThreads = new CopyOnWriteArrayList<>();
private final Lifecycle lifecycle;
@@ -66,27 +67,76 @@ public class Monitor {
@CheckForNull
HardStopWatcherThread hardStopWatcher;
- Monitor(int processNumber, FileSystem fileSystem, SystemExit exit, boolean watchForHardStop, Lifecycle.LifecycleListener... listeners) {
- this.processNumber = processNumber;
- this.fileSystem = fileSystem;
- this.systemExit = exit;
- this.watchForHardStop = watchForHardStop;
- this.lifecycle = new Lifecycle(listeners);
+ private Monitor(Builder builder) {
+ this.processNumber = builder.processNumber;
+ this.fileSystem = requireNonNull(builder.fileSystem, "FileSystem can't be null²");
+ this.systemExit = builder.exit == null ? new SystemExit() : builder.exit;
+ this.watchForHardStop = builder.watchForHardStop;
+ this.waitForOperational = builder.waitForOperational;
+ this.lifecycle = builder.listeners == null ? new Lifecycle() : new Lifecycle(builder.listeners.stream().toArray(Lifecycle.LifecycleListener[]::new));
}
- public static Monitor create(int processNumber, FileSystem fileSystem, boolean watchForHardStop) {
- return new Monitor(processNumber, fileSystem, new SystemExit(), watchForHardStop);
+ public static Builder newMonitorBuilder() {
+ return new Builder();
}
- public static Monitor create(int processNumber, FileSystem fileSystem, boolean watchForHardStop, Lifecycle.LifecycleListener listener) {
- return new Monitor(processNumber, fileSystem, new SystemExit(), watchForHardStop, Objects.requireNonNull(listener));
+ public static class Builder {
+ private int processNumber;
+ private FileSystem fileSystem;
+ private SystemExit exit;
+ private boolean watchForHardStop;
+ private boolean waitForOperational = false;
+ private List<Lifecycle.LifecycleListener> listeners;
+
+ private Builder() {
+ // use static factory method
+ }
+
+ public Builder setProcessNumber(int processNumber) {
+ this.processNumber = processNumber;
+ return this;
+ }
+
+ public Builder setFileSystem(FileSystem fileSystem) {
+ this.fileSystem = fileSystem;
+ return this;
+ }
+
+ public Builder setExit(SystemExit exit) {
+ this.exit = exit;
+ return this;
+ }
+
+ public Builder setWatchForHardStop(boolean watchForHardStop) {
+ this.watchForHardStop = watchForHardStop;
+ return this;
+ }
+
+ public Builder setWaitForOperational() {
+ this.waitForOperational = true;
+ return this;
+ }
+
+ public Builder addListener(Lifecycle.LifecycleListener listener) {
+ if (this.listeners == null) {
+ this.listeners = new ArrayList<>(1);
+ }
+ this.listeners.add(requireNonNull(listener, "LifecycleListener can't be null"));
+ return this;
+ }
+
+ public Monitor build() {
+ return new Monitor(this);
+ }
}
/**
- * Starts commands and blocks current thread until all processes are in state {@link State#STARTED}.
- * @throws java.lang.IllegalArgumentException if commands list is empty
- * @throws java.lang.IllegalStateException if already started or if at least one process failed to start. In this case
- * all processes are terminated. No need to execute {@link #stop()}
+ * Starts commands and blocks current thread until all processes are in state {@link State#STARTED} (or
+ * {@link State#OPERATIONAL} if {@link #waitForOperational} is {@code true}).
+ *
+ * @throws IllegalArgumentException if commands list is empty
+ * @throws IllegalStateException if already started or if at least one process failed to start. In this case
+ * all processes are terminated. No need to execute {@link #stop()}
*/
public void start(List<JavaCommand> commands) throws InterruptedException {
if (commands.isEmpty()) {
@@ -103,7 +153,7 @@ public class Monitor {
// start watching for restart requested by child process
restartWatcher.start();
- javaCommands = commands;
+ javaCommands = new ArrayList<>(commands);
startProcesses();
}
@@ -120,6 +170,7 @@ public class Monitor {
startAndMonitorProcesses();
stopIfAnyProcessDidNotStart();
+ waitForOperationalProcesses();
}
}
@@ -143,7 +194,7 @@ public class Monitor {
}
}
- private void startAndMonitorProcesses() throws InterruptedException{
+ private void startAndMonitorProcesses() throws InterruptedException {
File tempDir = fileSystem.getTempDir();
this.launcher = new JavaProcessLauncher(TIMEOUTS, tempDir);
for (JavaCommand command : javaCommands) {
@@ -186,6 +237,25 @@ public class Monitor {
}
}
+ private void waitForOperationalProcesses() throws InterruptedException {
+ if (!waitForOperational) {
+ return;
+ }
+
+ for (WatcherThread watcherThread : watcherThreads) {
+ waitForOperationalProcess(watcherThread.getProcessRef());
+ }
+ lifecycle.tryToMoveTo(State.OPERATIONAL);
+ }
+
+ private static void waitForOperationalProcess(ProcessRef processRef) throws InterruptedException {
+ LOG.debug("Waiting for {} to be operational", processRef);
+ while (!processRef.getCommands().isOperational()) {
+ Thread.sleep(WATCH_DELAY_MS);
+ }
+ LOG.debug("{} is operational", processRef);
+ }
+
/**
* Blocks until all processes are terminated
*/
@@ -332,7 +402,8 @@ public class Monitor {
@Override
public void run() {
while (lifecycle.getState() != Lifecycle.State.STOPPED) {
- if (lifecycle.getState() == Lifecycle.State.STARTED && didAnyProcessRequestRestart()) {
+ Lifecycle.State state = lifecycle.getState();
+ if ((state == Lifecycle.State.STARTED || state == Lifecycle.State.OPERATIONAL) && didAnyProcessRequestRestart()) {
restartAsync();
}
try {
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 13f8821d156..189b14824d9 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
@@ -52,6 +52,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.sonar.process.monitor.Monitor.newMonitorBuilder;
import static org.sonar.process.monitor.MonitorTest.HttpProcessClientAssert.assertThat;
public class MonitorTest {
@@ -322,7 +323,12 @@ public class MonitorTest {
private Monitor newDefaultMonitor(File tempDir, boolean watchForHardStop) throws IOException {
when(fileSystem.getTempDir()).thenReturn(tempDir);
- return new Monitor(1, fileSystem, exit, watchForHardStop);
+ return newMonitorBuilder()
+ .setProcessNumber(1)
+ .setFileSystem(fileSystem)
+ .setExit(exit)
+ .setWatchForHardStop(watchForHardStop)
+ .build();
}
/**
diff --git a/server/sonar-process/src/main/java/org/sonar/process/Lifecycle.java b/server/sonar-process/src/main/java/org/sonar/process/Lifecycle.java
index 984aaafdb24..25ca71f59fc 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/Lifecycle.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/Lifecycle.java
@@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory;
import static org.sonar.process.Lifecycle.State.HARD_STOPPING;
import static org.sonar.process.Lifecycle.State.INIT;
+import static org.sonar.process.Lifecycle.State.OPERATIONAL;
import static org.sonar.process.Lifecycle.State.RESTARTING;
import static org.sonar.process.Lifecycle.State.STARTED;
import static org.sonar.process.Lifecycle.State.STARTING;
@@ -44,7 +45,7 @@ public class Lifecycle {
private static final Logger LOG = LoggerFactory.getLogger(Lifecycle.class);
public enum State {
- INIT, STARTING, STARTED, RESTARTING, STOPPING, HARD_STOPPING, STOPPED
+ INIT, STARTING, STARTED, OPERATIONAL, RESTARTING, STOPPING, HARD_STOPPING, STOPPED
}
private static final Map<State, Set<State>> TRANSITIONS = buildTransitions();
@@ -60,7 +61,8 @@ public class Lifecycle {
Map<State, Set<State>> res = new EnumMap<>(State.class);
res.put(INIT, toSet(STARTING));
res.put(STARTING, toSet(STARTED, STOPPING, HARD_STOPPING));
- res.put(STARTED, toSet(RESTARTING, STOPPING, HARD_STOPPING));
+ res.put(STARTED, toSet(OPERATIONAL, RESTARTING, STOPPING, HARD_STOPPING));
+ res.put(OPERATIONAL, toSet(RESTARTING, STOPPING, HARD_STOPPING));
res.put(RESTARTING, toSet(STARTING, HARD_STOPPING));
res.put(STOPPING, toSet(STOPPED));
res.put(HARD_STOPPING, toSet(STOPPED));
diff --git a/server/sonar-process/src/main/java/org/sonar/process/Monitored.java b/server/sonar-process/src/main/java/org/sonar/process/Monitored.java
index 837c658cf3b..b37d929e54d 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/Monitored.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/Monitored.java
@@ -27,13 +27,13 @@ public interface Monitored {
void start();
/**
- * {@link Status#UP} if the process is done starting, {@link Status#FAILED} if the process failed to start,
- * {@link Status#DOWN} otherwise.
+ * @return {@link Status#UP} if the process is done starting, {@link Status#OPERATIONAL} if the service is operation,
+ * {@link Status#FAILED} if the process failed to start, {@link Status#DOWN} otherwise.
*/
Status getStatus();
enum Status {
- UP, DOWN, FAILED
+ DOWN, UP, OPERATIONAL, FAILED
}
/**
diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java b/server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java
index dffa067fd2c..15190a41dcc 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java
@@ -95,22 +95,7 @@ public class ProcessEntryPoint implements Stoppable {
Logger logger = LoggerFactory.getLogger(getClass());
try {
- logger.info("Starting " + getKey());
- Runtime.getRuntime().addShutdownHook(shutdownHook);
- stopWatcher.start();
-
- monitored.start();
- Monitored.Status status = waitForNotDownStatus();
- if (status == Monitored.Status.UP) {
- // notify monitor that process is ready
- commands.setUp();
-
- if (lifecycle.tryToMoveTo(Lifecycle.State.STARTED)) {
- monitored.awaitStop();
- }
- } else {
- stop();
- }
+ launch(logger);
} catch (Exception e) {
logger.warn("Fail to start " + getKey(), e);
} finally {
@@ -118,6 +103,30 @@ public class ProcessEntryPoint implements Stoppable {
}
}
+ private void launch(Logger logger) throws InterruptedException {
+ logger.info("Starting " + getKey());
+ Runtime.getRuntime().addShutdownHook(shutdownHook);
+ stopWatcher.start();
+
+ monitored.start();
+ Monitored.Status status = waitForNotDownStatus();
+ if (status == Monitored.Status.UP || status == Monitored.Status.OPERATIONAL) {
+ // notify monitor that process is ready
+ commands.setUp();
+
+ if (lifecycle.tryToMoveTo(Lifecycle.State.STARTED)) {
+ Monitored.Status newStatus = waitForOperational(status);
+ if (newStatus == Monitored.Status.OPERATIONAL && lifecycle.tryToMoveTo(Lifecycle.State.OPERATIONAL)) {
+ commands.setOperational();
+ }
+
+ monitored.awaitStop();
+ }
+ } else {
+ stop();
+ }
+ }
+
private Monitored.Status waitForNotDownStatus() throws InterruptedException {
Monitored.Status status = Monitored.Status.DOWN;
while (status == Monitored.Status.DOWN) {
@@ -127,6 +136,16 @@ public class ProcessEntryPoint implements Stoppable {
return status;
}
+ private Monitored.Status waitForOperational(Monitored.Status currentStatus) throws InterruptedException {
+ Monitored.Status status = currentStatus;
+ // wait for operation or stop waiting if going to OPERATIONAL failed
+ while (status != Monitored.Status.OPERATIONAL && status != Monitored.Status.FAILED) {
+ status = monitored.getStatus();
+ Thread.sleep(20L);
+ }
+ return status;
+ }
+
boolean isStarted() {
return lifecycle.getState() == Lifecycle.State.STARTED;
}
diff --git a/server/sonar-process/src/test/java/org/sonar/process/LifecycleTest.java b/server/sonar-process/src/test/java/org/sonar/process/LifecycleTest.java
index dbb233e474e..fc773dde56a 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/LifecycleTest.java
+++ b/server/sonar-process/src/test/java/org/sonar/process/LifecycleTest.java
@@ -28,6 +28,7 @@ import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.process.Lifecycle.State;
import static org.sonar.process.Lifecycle.State.INIT;
+import static org.sonar.process.Lifecycle.State.OPERATIONAL;
import static org.sonar.process.Lifecycle.State.RESTARTING;
import static org.sonar.process.Lifecycle.State.STARTED;
import static org.sonar.process.Lifecycle.State.STARTING;
@@ -75,11 +76,11 @@ public class LifecycleTest {
}
@Test
- public void can_move_to_STOPPING_from_STARTING_and_STARTED_only() {
+ public void can_move_to_STOPPING_from_STARTING_STARTED_OPERATIONAL_only() {
for (State state : values()) {
TestLifeCycleListener listener = new TestLifeCycleListener();
boolean tryToMoveTo = newLifeCycle(state, listener).tryToMoveTo(STOPPING);
- if (state == STARTING || state == STARTED) {
+ if (state == STARTING || state == STARTED || state == OPERATIONAL) {
assertThat(tryToMoveTo).describedAs("from state " + state).isTrue();
assertThat(listener.getTransitions()).containsOnly(new Transition(state, STOPPING));
} else {
@@ -90,6 +91,21 @@ public class LifecycleTest {
}
@Test
+ public void can_move_to_OPERATIONAL_from_STARTED_only() {
+ for (State state : values()) {
+ TestLifeCycleListener listener = new TestLifeCycleListener();
+ boolean tryToMoveTo = newLifeCycle(state, listener).tryToMoveTo(OPERATIONAL);
+ if (state == STARTED) {
+ assertThat(tryToMoveTo).describedAs("from state " + state).isTrue();
+ assertThat(listener.getTransitions()).containsOnly(new Transition(state, OPERATIONAL));
+ } else {
+ assertThat(tryToMoveTo).describedAs("from state " + state).isFalse();
+ assertThat(listener.getTransitions()).isEmpty();
+ }
+ }
+ }
+
+ @Test
public void can_move_to_STARTING_from_RESTARTING() {
TestLifeCycleListener listener = new TestLifeCycleListener();
assertThat(newLifeCycle(RESTARTING, listener).tryToMoveTo(STARTING)).isTrue();
@@ -104,10 +120,12 @@ public class LifecycleTest {
return newLifeCycle(INIT, state, listeners);
case STARTED:
return newLifeCycle(STARTING, state, listeners);
- case RESTARTING:
+ case OPERATIONAL:
return newLifeCycle(STARTED, state, listeners);
+ case RESTARTING:
+ return newLifeCycle(OPERATIONAL, state, listeners);
case STOPPING:
- return newLifeCycle(STARTED, state, listeners);
+ return newLifeCycle(OPERATIONAL, state, listeners);
case HARD_STOPPING:
return newLifeCycle(STARTING, state, listeners);
case STOPPED:
diff --git a/server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java b/server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java
index 02c07db7c66..3b045b6a0dc 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java
+++ b/server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java
@@ -174,7 +174,7 @@ public class ProcessEntryPointTest {
@Override
public Status getStatus() {
- return Status.UP;
+ return Status.OPERATIONAL;
}
@Override
diff --git a/server/sonar-process/src/test/java/org/sonar/process/test/HttpProcess.java b/server/sonar-process/src/test/java/org/sonar/process/test/HttpProcess.java
index d11b173400e..93110668414 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/test/HttpProcess.java
+++ b/server/sonar-process/src/test/java/org/sonar/process/test/HttpProcess.java
@@ -90,13 +90,13 @@ public class HttpProcess implements Monitored {
@Override
public Status getStatus() {
if (ready) {
- return Status.UP;
+ return Status.OPERATIONAL;
}
if (server.isStarted()) {
ready = true;
writeTimeToFile("readyAt");
}
- return ready ? Status.UP : Status.DOWN;
+ return ready ? Status.OPERATIONAL : Status.DOWN;
}
@Override
diff --git a/server/sonar-process/src/test/java/org/sonar/process/test/InfiniteTerminationProcess.java b/server/sonar-process/src/test/java/org/sonar/process/test/InfiniteTerminationProcess.java
index 47edc81a47c..b69c998eca6 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/test/InfiniteTerminationProcess.java
+++ b/server/sonar-process/src/test/java/org/sonar/process/test/InfiniteTerminationProcess.java
@@ -52,7 +52,7 @@ public class InfiniteTerminationProcess implements Monitored {
@Override
public Status getStatus() {
- return state == State.STARTED ? Status.UP : Status.DOWN;
+ return state == State.STARTED ? Status.OPERATIONAL : Status.DOWN;
}
@Override
diff --git a/server/sonar-process/src/test/java/org/sonar/process/test/StandardProcess.java b/server/sonar-process/src/test/java/org/sonar/process/test/StandardProcess.java
index 2c7cf0aaeff..e5086d87bf0 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/test/StandardProcess.java
+++ b/server/sonar-process/src/test/java/org/sonar/process/test/StandardProcess.java
@@ -52,7 +52,7 @@ public class StandardProcess implements Monitored {
@Override
public Status getStatus() {
- return state == State.STARTED ? Status.UP : Status.DOWN;
+ return state == State.STARTED ? Status.OPERATIONAL : Status.DOWN;
}
@Override
diff --git a/server/sonar-search/src/main/java/org/sonar/search/SearchServer.java b/server/sonar-search/src/main/java/org/sonar/search/SearchServer.java
index b35ff952f27..6929b44212a 100644
--- a/server/sonar-search/src/main/java/org/sonar/search/SearchServer.java
+++ b/server/sonar-search/src/main/java/org/sonar/search/SearchServer.java
@@ -62,7 +62,10 @@ public class SearchServer implements Monitored {
.setTimeout(TimeValue.timeValueSeconds(30L))
.get()
.getStatus() != ClusterHealthStatus.RED;
- return esStatus ? Status.UP : Status.DOWN;
+ if (esStatus) {
+ return Status.OPERATIONAL;
+ }
+ return Status.DOWN;
}
@Override
diff --git a/server/sonar-search/src/test/java/org/sonar/search/SearchServerTest.java b/server/sonar-search/src/test/java/org/sonar/search/SearchServerTest.java
index d1ed8480767..ddfcd68fbc5 100644
--- a/server/sonar-search/src/test/java/org/sonar/search/SearchServerTest.java
+++ b/server/sonar-search/src/test/java/org/sonar/search/SearchServerTest.java
@@ -83,7 +83,7 @@ public class SearchServerTest {
underTest = new SearchServer(props);
underTest.start();
- assertThat(underTest.getStatus()).isEqualTo(Monitored.Status.UP);
+ assertThat(underTest.getStatus()).isEqualTo(Monitored.Status.OPERATIONAL);
Settings settings = Settings.builder().put("cluster.name", A_CLUSTER_NAME).build();
client = TransportClient.builder().settings(settings).build()
diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/WebServer.java b/server/sonar-server/src/main/java/org/sonar/server/app/WebServer.java
index cdfdc58e168..3b6b5d1629b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/app/WebServer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/app/WebServer.java
@@ -20,22 +20,32 @@
package org.sonar.server.app;
import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import org.sonar.process.DefaultProcessCommands;
import org.sonar.process.MinimumViableSystem;
import org.sonar.process.Monitored;
import org.sonar.process.ProcessEntryPoint;
+import org.sonar.process.ProcessId;
import org.sonar.process.Props;
public class WebServer implements Monitored {
+ public static final String PROPERTY_SHARED_PATH = "process.sharedDir";
+ private final File sharedDir;
private final EmbeddedTomcat tomcat;
WebServer(Props props) {
new MinimumViableSystem()
.checkWritableTempDir()
.checkRequiredJavaOptions(ImmutableMap.of("file.encoding", "UTF-8"));
+ this.sharedDir = getSharedDir(props);
this.tomcat = new EmbeddedTomcat(props);
}
+ private static File getSharedDir(Props props) {
+ return props.nonNullValueAsFile(PROPERTY_SHARED_PATH);
+ }
+
@Override
public void start() {
tomcat.start();
@@ -47,13 +57,19 @@ public class WebServer implements Monitored {
case DOWN:
return Status.DOWN;
case UP:
- return Status.UP;
+ return isOperational() ? Status.OPERATIONAL : Status.UP;
case FAILED:
default:
return Status.FAILED;
}
}
+ private boolean isOperational() {
+ try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(sharedDir, ProcessId.WEB_SERVER.getIpcIndex())) {
+ return processCommands.isOperational();
+ }
+ }
+
@Override
public void stop() {
tomcat.terminate();
diff --git a/sonar-application/src/main/java/org/sonar/application/App.java b/sonar-application/src/main/java/org/sonar/application/App.java
index dd9ae03fcae..a2e96cbd3ab 100644
--- a/sonar-application/src/main/java/org/sonar/application/App.java
+++ b/sonar-application/src/main/java/org/sonar/application/App.java
@@ -62,7 +62,13 @@ public class App implements Stoppable {
private final Monitor monitor;
public App(AppFileSystem appFileSystem, boolean watchForHardStop) {
- this(Monitor.create(APP.getIpcIndex(), appFileSystem, watchForHardStop, new AppLifecycleListener()));
+ this(Monitor.newMonitorBuilder()
+ .setProcessNumber(APP.getIpcIndex())
+ .setFileSystem(appFileSystem)
+ .setWatchForHardStop(watchForHardStop)
+ .setWaitForOperational()
+ .addListener(new AppLifecycleListener())
+ .build());
}
App(Monitor monitor) {
@@ -192,7 +198,7 @@ public class App implements Stoppable {
@Override
public void successfulTransition(State from, State to) {
- if (to == State.STARTED) {
+ if (to == State.OPERATIONAL) {
LOGGER.info("SonarQube is up");
}
}