]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8341 add INFO log in App when SQ is done starting
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Tue, 15 Nov 2016 14:38:33 +0000 (15:38 +0100)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 16 Nov 2016 17:45:35 +0000 (18:45 +0100)
server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/Monitor.java
server/sonar-process/src/main/java/org/sonar/process/Lifecycle.java
server/sonar-process/src/test/java/org/sonar/process/LifecycleTest.java
sonar-application/src/main/java/org/sonar/application/App.java

index 7f1d0b5853d601d18ddfd51ec51b2b47a41d7d0a..1f68dd5c537b1de6b2f8767bfbf58c4f9a7fd6d7 100644 (file)
@@ -24,6 +24,7 @@ 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;
@@ -52,7 +53,7 @@ public class Monitor {
   private final Thread shutdownHook = new Thread(new MonitorShutdownHook(), "Monitor Shutdown Hook");
 
   private final List<WatcherThread> watcherThreads = new CopyOnWriteArrayList<>();
-  private final Lifecycle lifecycle = new Lifecycle();
+  private final Lifecycle lifecycle;
 
   private final TerminatorThread terminator = new TerminatorThread();
   private final RestartRequestWatcherThread restartWatcher = new RestartRequestWatcherThread();
@@ -65,17 +66,22 @@ public class Monitor {
   @CheckForNull
   HardStopWatcherThread hardStopWatcher;
 
-  Monitor(int processNumber, FileSystem fileSystem, SystemExit exit, boolean watchForHardStop) {
+  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);
   }
 
   public static Monitor create(int processNumber, FileSystem fileSystem, boolean watchForHardStop) {
     return new Monitor(processNumber, fileSystem, new SystemExit(), watchForHardStop);
   }
 
+  public static Monitor create(int processNumber, FileSystem fileSystem, boolean watchForHardStop, Lifecycle.LifecycleListener listener) {
+    return new Monitor(processNumber, fileSystem, new SystemExit(), watchForHardStop, Objects.requireNonNull(listener));
+  }
+
   /**
    * Starts commands and blocks current thread until all processes are in state {@link State#STARTED}.
    * @throws java.lang.IllegalArgumentException if commands list is empty
index b6dd380211bc031390cb0bd06c76c9cb38a27812..5bd062b9340063abe7b60eb6b5e09f83d58790e6 100644 (file)
@@ -23,8 +23,11 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumMap;
 import java.util.EnumSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -46,8 +49,13 @@ public class Lifecycle {
 
   private static final Map<State, Set<State>> TRANSITIONS = buildTransitions();
 
+  private final List<LifecycleListener> listeners;
   private State state = INIT;
 
+  public Lifecycle(LifecycleListener... listeners) {
+    this.listeners = Arrays.stream(listeners).filter(Objects::nonNull).collect(Collectors.toList());
+  }
+
   private static Map<State, Set<State>> buildTransitions() {
     Map<State, Set<State>> res = new EnumMap<>(State.class);
     res.put(INIT, toSet(STARTING));
@@ -80,6 +88,7 @@ public class Lifecycle {
     if (TRANSITIONS.get(currentState).contains(to)) {
       this.state = to;
       res = true;
+      listeners.forEach(listener -> listener.successfulTransition(currentState, to));
     }
     LOG.trace("tryToMoveTo from {} to {} => {}", currentState, to, res);
     return res;
@@ -101,4 +110,11 @@ public class Lifecycle {
   public int hashCode() {
     return state.hashCode();
   }
+
+  public interface LifecycleListener {
+    /**
+     * Called when a transition from state {@code from} to state {@code to} was successful.
+     */
+    void successfulTransition(State from, State to);
+  }
 }
index 302fdaf85124b83249199381dff36e6cf4e9f327..2fd627eca43dc3b4dbcafb3081c746570c0d2df4 100644 (file)
  */
 package org.sonar.process;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
 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.*;
+import static org.sonar.process.Lifecycle.State.INIT;
+import static org.sonar.process.Lifecycle.State.RESTARTING;
+import static org.sonar.process.Lifecycle.State.STARTED;
+import static org.sonar.process.Lifecycle.State.STARTING;
+import static org.sonar.process.Lifecycle.State.STOPPING;
+import static org.sonar.process.Lifecycle.State.values;
 
 public class LifecycleTest {
 
@@ -44,14 +53,18 @@ public class LifecycleTest {
 
   @Test
   public void try_to_move_does_not_support_jumping_states() {
-    Lifecycle lifecycle = new Lifecycle();
+    TestLifeCycleListener listener = new TestLifeCycleListener();
+    Lifecycle lifecycle = new Lifecycle(listener);
     assertThat(lifecycle.getState()).isEqualTo(INIT);
+    assertThat(listener.getTransitions()).isEmpty();
 
     assertThat(lifecycle.tryToMoveTo(STARTED)).isFalse();
     assertThat(lifecycle.getState()).isEqualTo(INIT);
+    assertThat(listener.getTransitions()).isEmpty();
 
     assertThat(lifecycle.tryToMoveTo(STARTING)).isTrue();
     assertThat(lifecycle.getState()).isEqualTo(STARTING);
+    assertThat(listener.getTransitions()).containsOnly(new Transition(INIT, STARTING));
   }
 
   @Test
@@ -64,45 +77,100 @@ public class LifecycleTest {
   @Test
   public void can_move_to_STOPPING_from_STARTING_and_STARTED_only() {
     for (State state : values()) {
-      boolean tryToMoveTo = newLifeCycle(state).tryToMoveTo(STOPPING);
+      TestLifeCycleListener listener = new TestLifeCycleListener();
+      boolean tryToMoveTo = newLifeCycle(state, listener).tryToMoveTo(STOPPING);
       if (state == STARTING || state == STARTED) {
         assertThat(tryToMoveTo).describedAs("from state " + state).isTrue();
+        assertThat(listener.getTransitions()).containsOnly(new Transition(state, STOPPING));
       } else {
         assertThat(tryToMoveTo).describedAs("from state " + state).isFalse();
+        assertThat(listener.getTransitions()).isEmpty();
       }
     }
   }
 
   @Test
   public void can_move_to_STARTING_from_RESTARTING() {
-    assertThat(newLifeCycle(RESTARTING).tryToMoveTo(STARTING)).isTrue();
+    TestLifeCycleListener listener = new TestLifeCycleListener();
+    assertThat(newLifeCycle(RESTARTING, listener).tryToMoveTo(STARTING)).isTrue();
+    assertThat(listener.getTransitions()).containsOnly(new Transition(RESTARTING, STARTING));
   }
 
-  private static Lifecycle newLifeCycle(State state) {
+  private static Lifecycle newLifeCycle(State state, TestLifeCycleListener... listeners) {
     switch (state) {
       case INIT:
-        return new Lifecycle();
+        return new Lifecycle(listeners);
       case STARTING:
-        return newLifeCycle(INIT, state);
+        return newLifeCycle(INIT, state, listeners);
       case STARTED:
-        return newLifeCycle(STARTING, state);
+        return newLifeCycle(STARTING, state, listeners);
       case RESTARTING:
-        return newLifeCycle(STARTED, state);
+        return newLifeCycle(STARTED, state, listeners);
       case STOPPING:
-        return newLifeCycle(STARTED, state);
+        return newLifeCycle(STARTED, state, listeners);
       case HARD_STOPPING:
-        return newLifeCycle(STARTING, state);
+        return newLifeCycle(STARTING, state, listeners);
       case STOPPED:
-        return newLifeCycle(STOPPING, state);
+        return newLifeCycle(STOPPING, state, listeners);
       default:
         throw new IllegalArgumentException("Unsupported state " + state);
     }
   }
 
-  private static Lifecycle newLifeCycle(State from, State to) {
+  private static Lifecycle newLifeCycle(State from, State to, TestLifeCycleListener... listeners) {
     Lifecycle lifecycle;
-    lifecycle = newLifeCycle(from);
+    lifecycle = newLifeCycle(from, listeners);
     assertThat(lifecycle.tryToMoveTo(to)).isTrue();
+    Arrays.stream(listeners).forEach(TestLifeCycleListener::clear);
     return lifecycle;
   }
+
+  private static final class TestLifeCycleListener implements Lifecycle.LifecycleListener {
+    private final List<Transition> transitions = new ArrayList<>();
+
+    @Override
+    public void successfulTransition(State from, State to) {
+      transitions.add(new Transition(from, to));
+    }
+
+    public List<Transition> getTransitions() {
+      return transitions;
+    }
+
+    public void clear() {
+      this.transitions.clear();
+    }
+  }
+
+  private static final class Transition {
+    private final State from;
+    private final State to;
+
+    private Transition(State from, State to) {
+      this.from = from;
+      this.to = to;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      Transition that = (Transition) o;
+      return from == that.from && to == that.to;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(from, to);
+    }
+
+    @Override
+    public String toString() {
+      return "Transition{" + from + " => " + to + '}';
+    }
+  }
 }
index 35276c6c71bb38d652dd492849b0003d3c5fe7e5..b956ed11dbededb9198b7817aa061d28a467c643 100644 (file)
@@ -24,6 +24,9 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
 import org.apache.commons.io.FilenameUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.process.Lifecycle;
 import org.sonar.process.ProcessId;
 import org.sonar.process.ProcessProperties;
 import org.sonar.process.Props;
@@ -31,6 +34,7 @@ import org.sonar.process.Stoppable;
 import org.sonar.process.monitor.JavaCommand;
 import org.sonar.process.monitor.Monitor;
 
+import static org.sonar.process.Lifecycle.State;
 import static org.sonar.process.ProcessId.APP;
 import static org.sonar.process.ProcessProperties.HTTPS_PROXY_HOST;
 import static org.sonar.process.ProcessProperties.HTTPS_PROXY_PORT;
@@ -45,7 +49,7 @@ public class App implements Stoppable {
   /**
    * Properties about proxy that must be set as system properties
    */
-  private static final String[] PROXY_PROPERTY_KEYS = new String[]{
+  private static final String[] PROXY_PROPERTY_KEYS = new String[] {
     HTTP_PROXY_HOST,
     HTTP_PROXY_PORT,
     "http.nonProxyHosts",
@@ -58,7 +62,7 @@ public class App implements Stoppable {
   private final Monitor monitor;
 
   public App(AppFileSystem appFileSystem, boolean watchForHardStop) {
-    this(Monitor.create(APP.getIpcIndex(), appFileSystem, watchForHardStop));
+    this(Monitor.create(APP.getIpcIndex(), appFileSystem, watchForHardStop, new AppLifecycleListener()));
   }
 
   App(Monitor monitor) {
@@ -185,4 +189,14 @@ public class App implements Stoppable {
     monitor.stop();
   }
 
+  private static class AppLifecycleListener implements Lifecycle.LifecycleListener {
+    private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
+
+    @Override
+    public void successfulTransition(State from, State to) {
+      if (to == State.STARTED) {
+        LOGGER.info("SonarQube is up");
+      }
+    }
+  }
 }