From: Sébastien Lesaint Date: Tue, 21 Feb 2017 13:51:34 +0000 (+0100) Subject: SONAR-8435 log "SonarQube is up" if all processes are operational X-Git-Tag: 6.4-RC1~883 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=679db32dc10eebf6e7d8cfddc44aa719696485ed;p=sonarqube.git 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 --- 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 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 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 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> TRANSITIONS = buildTransitions(); @@ -60,7 +61,8 @@ public class Lifecycle { Map> 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 { @@ -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: 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"); } }