]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8435 log "SonarQube is up" if all processes are operational
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Tue, 21 Feb 2017 13:51:34 +0000 (14:51 +0100)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Fri, 24 Feb 2017 20:08:18 +0000 (21:08 +0100)
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

18 files changed:
it/it-tests/src/test/java/it/serverSystem/ClusterTest.java
server/sonar-ce/src/main/java/org/sonar/ce/app/CeServer.java
server/sonar-ce/src/test/java/org/sonar/ce/app/CeServerTest.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationEngineImpl.java
server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/Monitor.java
server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/MonitorTest.java
server/sonar-process/src/main/java/org/sonar/process/Lifecycle.java
server/sonar-process/src/main/java/org/sonar/process/Monitored.java
server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java
server/sonar-process/src/test/java/org/sonar/process/LifecycleTest.java
server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java
server/sonar-process/src/test/java/org/sonar/process/test/HttpProcess.java
server/sonar-process/src/test/java/org/sonar/process/test/InfiniteTerminationProcess.java
server/sonar-process/src/test/java/org/sonar/process/test/StandardProcess.java
server/sonar-search/src/main/java/org/sonar/search/SearchServer.java
server/sonar-search/src/test/java/org/sonar/search/SearchServerTest.java
server/sonar-server/src/main/java/org/sonar/server/app/WebServer.java
sonar-application/src/main/java/org/sonar/application/App.java

index c9e37f83611b89abb583588f86f1c21f64f7c8a0..56ecaf34183524364bfe00bf9c60865aa7efdc32 100644 (file)
@@ -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();
 
index 1ae2b34143e2720dce88384e1f498f4e0a5daa67..ee1288ebca370fd297d5a8293698f93562e9c380 100644 (file)
@@ -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;
   }
index 0a4f46caab025301f35f342eafaee1c3452c26d5..35ff680c91962ce557e523fabe0950fd10bfffcc 100644 (file)
@@ -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
index d062656a12a6b951f3a2a143a3df0ab2356dcd8b..27e6ece8f6193b8f04b56991f620509d2fe445aa 100644 (file)
@@ -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;
index 44a29b36235cb75831a382e3617696a5f60fab72..42dd1a20487f6ea769fa4e632dfa279130c01699 100644 (file)
@@ -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 {
index 13f8821d1565b960e4543f45e0a85541c9b81ebb..189b14824d93a8300cbf461a9c2ed72614094d82 100644 (file)
@@ -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();
   }
 
   /**
index 984aaafdb241c0f2a40cab13b1b4564ed8913844..25ca71f59fca143d2a9feafec26daf55e35284ee 100644 (file)
@@ -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));
index 837c658cf3b91ba4ff2f977ee504a2fefb435652..b37d929e54dc6d6010fa6922556dd57f3067793a 100644 (file)
@@ -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
   }
 
   /**
index dffa067fd2c48dc3af4d4c6e86e007bb04729b27..15190a41dccfcc6830822ac8eef0b787501b11be 100644 (file)
@@ -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;
   }
index dbb233e474ec05fdbab78270e0b6e55af8b9cbf2..fc773dde56a29a26a3c9f681fa293866d6b2cc23 100644 (file)
@@ -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 {
@@ -89,6 +90,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();
@@ -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:
index 02c07db7c6617d861fc00f56352a007a92bd8628..3b045b6a0dc1255f1bc6881592b791b8966a5427 100644 (file)
@@ -174,7 +174,7 @@ public class ProcessEntryPointTest {
 
     @Override
     public Status getStatus() {
-      return Status.UP;
+      return Status.OPERATIONAL;
     }
 
     @Override
index d11b173400ecbc1d63f4b52913f7a49f6610cd87..93110668414deea7f4c4bf50223890d6c756280f 100644 (file)
@@ -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
index 47edc81a47cfa0067a348ddc69761b1a4ca14a51..b69c998eca60b7b8a8683b3f86f5492b1b3f0f64 100644 (file)
@@ -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
index 2c7cf0aaeff14a625496b25e8f0451a8193afdd7..e5086d87bf0bad575fb9eec15e76ba74945bea90 100644 (file)
@@ -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
index b35ff952f27c877a2a43575d9b678d3432cdfd67..6929b44212a515b21a31fddba31cedde5eda1423 100644 (file)
@@ -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
index d1ed8480767767b6f10ca6a60786b8456d767ad7..ddfcd68fbc5dc3b1988d62e212bf2941b780e9a5 100644 (file)
@@ -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()
index cdfdc58e168a04fcb29d4f60e2baf94a9db1367d..3b6b5d1629b7003d075b1d2d7c7ca872c1d85d50 100644 (file)
 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();
index dd9ae03fcae50d8129ef93f41e447bf86acd8303..a2e96cbd3ab09b5ac23cf4d36b4cdbce2bb95ab9 100644 (file)
@@ -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");
       }
     }