]> source.dussan.org Git - sonarqube.git/commitdiff
Added test for MonitoredProcess Class
authorStephane Gamard <stephane.gamard@searchbox.com>
Wed, 13 Aug 2014 16:53:18 +0000 (18:53 +0200)
committerStephane Gamard <stephane.gamard@searchbox.com>
Wed, 13 Aug 2014 16:53:18 +0000 (18:53 +0200)
server/process/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java
server/process/sonar-process/src/test/java/org/sonar/process/DummyProcess.java [new file with mode: 0644]
server/process/sonar-process/src/test/java/org/sonar/process/MonitoredProcessTest.java [new file with mode: 0644]

index b620bec8b884b871551044d38a3c579edab50c7c..49ed4618ed0a509781bb04ef9f52cf6d4892918c 100644 (file)
@@ -32,15 +32,19 @@ public abstract class MonitoredProcess implements ProcessMXBean {
 
   public static final String NAME_PROPERTY = "pName";
   private static final long AUTOKILL_TIMEOUT_MS = 30000L;
-  private static final long AUTOKILL_CHECK_DELAY_MS = 5000L;
+  private static final long AUTOKILL_CHECK_DELAY_MS = 2000L;
   public static final String MISSING_NAME_ARGUMENT = "Missing Name argument";
 
   private Long lastPing;
   private final String name;
+  private boolean terminated = false;
+  private long timeout = AUTOKILL_TIMEOUT_MS;
+  private long checkDelay = AUTOKILL_CHECK_DELAY_MS;
+
   protected final Props props;
   private ScheduledFuture<?> pingTask = null;
-  private ScheduledExecutorService monitor;
 
+  private ScheduledExecutorService monitor;
   private final boolean isMonitored;
 
   protected MonitoredProcess(Props props) throws Exception {
@@ -61,6 +65,24 @@ public abstract class MonitoredProcess implements ProcessMXBean {
     ProcessUtils.addSelfShutdownHook(this);
   }
 
+  public MonitoredProcess setTimeout(long timeout) {
+    this.timeout = timeout;
+    return this;
+  }
+
+  private long getTimeout() {
+    return timeout;
+  }
+
+  public MonitoredProcess setCheckDelay(long checkDelay) {
+    this.checkDelay = checkDelay;
+    return this;
+  }
+
+  private long getCheckDelay() {
+    return checkDelay;
+  }
+
   public final void start() {
     if (monitor != null) {
       throw new IllegalStateException("Already started");
@@ -82,9 +104,9 @@ public abstract class MonitoredProcess implements ProcessMXBean {
       @Override
       public void run() {
         long time = System.currentTimeMillis();
-        if (time - lastPing > AUTOKILL_TIMEOUT_MS) {
+        if (time - lastPing > getTimeout()) {
           LoggerFactory.getLogger(getClass()).info(String.format(
-            "Did not receive any ping during %d seconds. Shutting down.", AUTOKILL_TIMEOUT_MS / 1000));
+            "Did not receive any ping during %d seconds. Shutting down.", getTimeout() / 1000));
           if (isMonitored) {
             terminate();
           }
@@ -93,7 +115,7 @@ public abstract class MonitoredProcess implements ProcessMXBean {
     };
     lastPing = System.currentTimeMillis();
     monitor = Executors.newScheduledThreadPool(1);
-    pingTask = monitor.scheduleAtFixedRate(breakOnMissingPing, AUTOKILL_CHECK_DELAY_MS, AUTOKILL_CHECK_DELAY_MS, TimeUnit.MILLISECONDS);
+    pingTask = monitor.scheduleAtFixedRate(breakOnMissingPing, getCheckDelay(), getCheckDelay(), TimeUnit.MILLISECONDS);
   }
 
   @Override
@@ -120,9 +142,14 @@ public abstract class MonitoredProcess implements ProcessMXBean {
         // do not propagate exception
       }
       logger.debug("Process[{}] terminated", name);
+      terminated = true;
     }
   }
 
+  public boolean isTerminated() {
+    return terminated && monitor == null;
+  }
+
   @Override
   public final boolean isReady() {
     try {
diff --git a/server/process/sonar-process/src/test/java/org/sonar/process/DummyProcess.java b/server/process/sonar-process/src/test/java/org/sonar/process/DummyProcess.java
new file mode 100644 (file)
index 0000000..15d3ec7
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.process;
+
+import org.apache.commons.io.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.Properties;
+
+public class DummyProcess extends MonitoredProcess {
+
+  public static final String NAME = "DummyName";
+  public static final String CHECKFILE_NAME = "check.tmp";
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(DummyProcess.class);
+
+  private boolean isReady = false;
+  private boolean isRunning = true;
+  private File checkFile;
+
+
+  protected DummyProcess(Props props, boolean monitored) throws Exception {
+    super(props, monitored);
+  }
+
+  protected DummyProcess(Props props) throws Exception {
+    this(props, false);
+  }
+
+  @Override
+  protected void doStart() {
+    isReady = true;
+    checkFile = new File(FileUtils.getTempDirectory(), CHECKFILE_NAME);
+    LOGGER.info("Starting Dummy OK Process");
+    while (isRunning) {
+      try {
+        Thread.sleep(100);
+      } catch (InterruptedException e) {
+        isRunning = false;
+      }
+    }
+  }
+
+  @Override
+  protected void doTerminate() {
+    LOGGER.info("Terminating Dummy OK Process");
+    this.isRunning = false;
+  }
+
+  @Override
+  protected boolean doIsReady() {
+    return isReady;
+  }
+
+  public static void main(String[] args) throws Exception {
+    Props props = new Props(new Properties());
+    props.set(MonitoredProcess.NAME_PROPERTY, DummyProcess.class.getSimpleName());
+    new DummyProcess(props).start();
+    System.exit(1);
+  }
+
+  public File getCheckFile() {
+    return checkFile;
+  }
+}
diff --git a/server/process/sonar-process/src/test/java/org/sonar/process/MonitoredProcessTest.java b/server/process/sonar-process/src/test/java/org/sonar/process/MonitoredProcessTest.java
new file mode 100644 (file)
index 0000000..515d3c1
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.process;
+
+import org.junit.Test;
+
+import java.util.Properties;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+
+public class MonitoredProcessTest {
+
+  @Test
+  public void fail_on_missing_name() throws Exception {
+    Properties properties = new Properties();
+
+    try {
+      new DummyProcess(new Props(properties), true);
+      fail();
+    } catch (Exception e) {
+      assertThat(e.getMessage()).isEqualTo("Missing Name argument");
+    }
+
+    properties.setProperty(MonitoredProcess.NAME_PROPERTY, DummyProcess.NAME);
+    DummyProcess dummyProcess = new DummyProcess(new Props(properties), true);
+    assertThat(dummyProcess).isNotNull();
+  }
+
+  @Test(timeout = 3000L)
+  public void monitor_dies_when_no_pings() throws Exception {
+    Properties properties = new Properties();
+    properties.setProperty(MonitoredProcess.NAME_PROPERTY, DummyProcess.NAME);
+    final DummyProcess dummyProcess = new DummyProcess(new Props(properties), true);
+    dummyProcess.setTimeout(1000L)
+      .setCheckDelay(500L);
+    Thread process = new Thread(new Runnable() {
+      @Override
+      public void run() {
+        dummyProcess.start();
+      }
+    });
+    assertThat(dummyProcess.isReady()).isFalse();
+    assertThat(dummyProcess.isTerminated()).isFalse();
+    process.start();
+    Thread.sleep(100);
+    assertThat(dummyProcess.isReady()).isTrue();
+    assertThat(dummyProcess.isTerminated()).isFalse();
+    process.join();
+    assertThat(dummyProcess.isReady()).isTrue();
+    assertThat(dummyProcess.isTerminated()).isTrue();
+
+    assertThat(dummyProcess.getCheckFile()).isNotNull();
+    assertThat(dummyProcess.getCheckFile().getName()).isEqualTo(DummyProcess.CHECKFILE_NAME);
+  }
+
+  @Test(timeout = 3000L)
+  public void monitor_dies_after_stopping_to_ping() throws Exception {
+    Properties properties = new Properties();
+    properties.setProperty(MonitoredProcess.NAME_PROPERTY, DummyProcess.NAME);
+    final DummyProcess dummyProcess = new DummyProcess(new Props(properties), true);
+    dummyProcess.setTimeout(1000L)
+      .setCheckDelay(500L);
+    Thread process = new Thread(new Runnable() {
+      @Override
+      public void run() {
+        dummyProcess.start();
+      }
+    });
+    assertThat(dummyProcess.isReady()).isFalse();
+    assertThat(dummyProcess.isTerminated()).isFalse();
+    process.start();
+    Thread.sleep(100);
+
+    int count = 0;
+    for (int i = 0; i < 3; i++) {
+      dummyProcess.ping();
+      assertThat(dummyProcess.isReady()).isTrue();
+      assertThat(dummyProcess.isTerminated()).isFalse();
+      Thread.sleep(300);
+      count++;
+    }
+    assertThat(count).isEqualTo(3);
+    process.join();
+    assertThat(dummyProcess.isReady()).isTrue();
+    assertThat(dummyProcess.isTerminated()).isTrue();
+
+    assertThat(dummyProcess.getCheckFile()).isNotNull();
+    assertThat(dummyProcess.getCheckFile().getName()).isEqualTo(DummyProcess.CHECKFILE_NAME);
+  }
+
+  @Test(timeout = 3000L)
+  public void monitor_explicitely_shutdown() throws Exception {
+    Properties properties = new Properties();
+    properties.setProperty(MonitoredProcess.NAME_PROPERTY, DummyProcess.NAME);
+    final DummyProcess dummyProcess = new DummyProcess(new Props(properties), true);
+    dummyProcess.setTimeout(Long.MAX_VALUE).setCheckDelay(500L);
+    Thread process = new Thread(new Runnable() {
+      @Override
+      public void run() {
+        dummyProcess.start();
+      }
+    });
+    assertThat(dummyProcess.isReady()).isFalse();
+    assertThat(dummyProcess.isTerminated()).isFalse();
+    process.start();
+    Thread.sleep(100);
+    assertThat(dummyProcess.isReady()).isTrue();
+    assertThat(dummyProcess.isTerminated()).isFalse();
+    dummyProcess.terminate();
+    Thread.sleep(100);
+    assertThat(dummyProcess.isReady()).isTrue();
+    assertThat(dummyProcess.isTerminated()).isTrue();
+
+    assertThat(dummyProcess.getCheckFile()).isNotNull();
+    assertThat(dummyProcess.getCheckFile().getName()).isEqualTo(DummyProcess.CHECKFILE_NAME);
+  }
+
+}
\ No newline at end of file