From 3166381a9020db85e45bfef8ebea9f841736af69 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Fri, 12 Sep 2014 15:29:57 +0200 Subject: [PATCH] SONAR-4898 simplify MonitoredProcess implementations --- .../process/monitor/RmiJmxConnector.java | 2 + .../process/monitor/TerminatorThread.java | 1 - .../org/sonar/process/MonitoredProcess.java | 12 ++- .../org/sonar/process/ProcessEntryPoint.java | 10 ++- .../sonar/process/ProcessEntryPointTest.java | 10 +++ .../org/sonar/process/test/HttpProcess.java | 18 ++-- .../sonar/process/test/StandardProcess.java | 5 ++ .../java/org/sonar/search/SearchServer.java | 13 +-- .../org/sonar/server/app/EmbeddedTomcat.java | 83 +++++++------------ .../java/org/sonar/server/app/WebServer.java | 5 ++ 10 files changed, 87 insertions(+), 72 deletions(-) diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/RmiJmxConnector.java b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/RmiJmxConnector.java index ec8dba2902a..876ccc2b22f 100644 --- a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/RmiJmxConnector.java +++ b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/RmiJmxConnector.java @@ -19,6 +19,7 @@ */ package org.sonar.process.monitor; +import org.slf4j.LoggerFactory; import org.sonar.process.JmxUtils; import org.sonar.process.LoopbackAddress; import org.sonar.process.ProcessMXBean; @@ -92,6 +93,7 @@ class RmiJmxConnector implements JmxConnector { execute(new Callable() { @Override public Void call() throws Exception { + LoggerFactory.getLogger(getClass()).info("Request termination of " + processRef); mbeans.get(processRef).terminate(); return null; } diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/TerminatorThread.java b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/TerminatorThread.java index f0943086877..f5b11b3148f 100644 --- a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/TerminatorThread.java +++ b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/TerminatorThread.java @@ -48,7 +48,6 @@ class TerminatorThread extends Thread { final ProcessRef processRef = processes.get(index); if (!processRef.isTerminated()) { processRef.setPingEnabled(false); - try { jmxConnector.terminate(processRef, timeouts.getTerminationTimeout()); } catch (Exception ignored) { diff --git a/server/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java b/server/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java index 6ee84d00744..11ddf54beec 100644 --- a/server/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java +++ b/server/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java @@ -22,10 +22,20 @@ package org.sonar.process; public interface MonitoredProcess extends Terminable { /** - * Starts and blocks until ready + * Starts process. No need to block until fully started and operational. */ void start(); + /** + * True if the process is started and operational (-> can accept requests), false if + * it's still starting. An exception is thrown is process failed to start (not starting + * nor started). + */ + boolean isReady(); + + /** + * Blocks until the process is terminated + */ void awaitTermination(); } 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 6990545a571..7137a41fa12 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 @@ -75,10 +75,17 @@ public class ProcessEntryPoint implements ProcessMXBean { try { monitoredProcess.start(); + boolean ready = false; + while (!ready) { + ready = monitoredProcess.isReady(); + Thread.sleep(200L); + } if (lifecycle.tryToMoveTo(State.STARTED)) { monitoredProcess.awaitTermination(); } - } catch (Exception ignored) { + } catch (Exception e) { + LoggerFactory.getLogger(getClass()).warn("Fail to start", e); + } finally { terminate(); } @@ -86,7 +93,6 @@ public class ProcessEntryPoint implements ProcessMXBean { @Override public boolean isReady() { - LoggerFactory.getLogger(getClass()).warn("Received JMX request isReady: " + (lifecycle.getState() == State.STARTED)); return lifecycle.getState() == State.STARTED; } 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 55bf8d679d1..03cd7409a49 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 @@ -193,6 +193,11 @@ public class ProcessEntryPointTest { } + @Override + public boolean isReady() { + return true; + } + @Override public void awaitTermination() { @@ -211,6 +216,11 @@ public class ProcessEntryPointTest { throw new IllegalStateException("ERROR"); } + @Override + public boolean isReady() { + return false; + } + @Override public void awaitTermination() { 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 307abed6d0b..f6ca587a588 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 @@ -42,6 +42,7 @@ import java.io.IOException; public class HttpProcess implements MonitoredProcess { private final Server server; + private boolean ready = false; // temp dir is specific to this process private final File tempDir = new File(System.getProperty("java.io.tmpdir")); @@ -70,16 +71,23 @@ public class HttpProcess implements MonitoredProcess { }); try { server.start(); - while (!server.isStarted()) { - Thread.sleep(100L); - } - writeTimeToFile("readyAt"); - } catch (Exception e) { throw new IllegalStateException("Fail to start Jetty", e); } } + @Override + public boolean isReady() { + if (ready) { + return true; + } + if (server.isStarted()) { + ready = true; + writeTimeToFile("readyAt"); + } + return false; + } + @Override public void awaitTermination() { try { 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 121784f633b..4c1a33221c6 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 @@ -50,6 +50,11 @@ public class StandardProcess implements MonitoredProcess { state = State.STARTED; } + @Override + public boolean isReady() { + return state == State.STARTED; + } + @Override public void awaitTermination() { try { 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 cf7eb1682df..d2aa111e7e5 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 @@ -143,20 +143,11 @@ public class SearchServer implements MonitoredProcess { .setTemplate("*") .addMapping("_default_", "{\"dynamic\": \"strict\"}") .get(); - - boolean ready = false; - while (!ready) { - ready = isReady(); - try { - Thread.sleep(300L); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } } } - boolean isReady() { + @Override + public boolean isReady() { return node.client().admin().cluster().prepareHealth() .setWaitForYellowStatus() .setTimeout(TimeValue.timeValueSeconds(3L)) diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/EmbeddedTomcat.java b/server/sonar-server/src/main/java/org/sonar/server/app/EmbeddedTomcat.java index a7146eda913..01a5305c53d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/app/EmbeddedTomcat.java +++ b/server/sonar-server/src/main/java/org/sonar/server/app/EmbeddedTomcat.java @@ -20,16 +20,7 @@ package org.sonar.server.app; import com.google.common.base.Throwables; -import com.google.common.util.concurrent.Uninterruptibles; -import org.apache.catalina.Container; -import org.apache.catalina.Executor; -import org.apache.catalina.Lifecycle; -import org.apache.catalina.LifecycleEvent; -import org.apache.catalina.LifecycleListener; -import org.apache.catalina.LifecycleState; -import org.apache.catalina.Server; -import org.apache.catalina.Service; -import org.apache.catalina.connector.Connector; +import org.apache.catalina.LifecycleException; import org.apache.catalina.core.StandardContext; import org.apache.catalina.startup.Tomcat; import org.apache.commons.io.FileUtils; @@ -37,13 +28,11 @@ import org.slf4j.LoggerFactory; import org.sonar.process.Props; import java.io.File; -import java.util.concurrent.TimeUnit; class EmbeddedTomcat { private final Props props; private Tomcat tomcat = null; - private Thread hook = null; private volatile StandardContext webappContext; EmbeddedTomcat(Props props) { @@ -51,53 +40,43 @@ class EmbeddedTomcat { } void start() { - if (tomcat != null || hook != null) { - throw new IllegalStateException("Server is already started"); - } - - try { - // '%2F' (slash /) and '%5C' (backslash \) are permitted as path delimiters in URLs - // See Ruby on Rails url_for - System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true"); + // '%2F' (slash /) and '%5C' (backslash \) are permitted as path delimiters in URLs + // See Ruby on Rails url_for + System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true"); - System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true"); + System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true"); - tomcat = new Tomcat(); - // Initialize directories - String basedir = tomcatBasedir().getAbsolutePath(); - tomcat.setBaseDir(basedir); - tomcat.getHost().setAppBase(basedir); - tomcat.getHost().setAutoDeploy(false); - tomcat.getHost().setCreateDirs(false); - tomcat.getHost().setDeployOnStartup(true); - Logging.configure(tomcat, props); - Connectors.configure(tomcat, props); - webappContext = Webapp.configure(tomcat, props); + tomcat = new Tomcat(); + // Initialize directories + String basedir = tomcatBasedir().getAbsolutePath(); + tomcat.setBaseDir(basedir); + tomcat.getHost().setAppBase(basedir); + tomcat.getHost().setAutoDeploy(false); + tomcat.getHost().setCreateDirs(false); + tomcat.getHost().setDeployOnStartup(true); + Logging.configure(tomcat, props); + Connectors.configure(tomcat, props); + webappContext = Webapp.configure(tomcat, props); + try { tomcat.start(); - waitForWebappReady(); - - } catch (Exception e) { + } catch (LifecycleException e) { Throwables.propagate(e); } } - private void waitForWebappReady() { - while (true) { - switch (webappContext.getState()) { - case NEW: - case INITIALIZING: - case INITIALIZED: - case STARTING_PREP: - case STARTING: - Uninterruptibles.sleepUninterruptibly(300L, TimeUnit.MILLISECONDS); - break; - case STARTED: - // ok - return; - default: - // problem, stopped or failed - throw new IllegalStateException("YYY Webapp did not start"); - } + boolean isReady() { + switch (webappContext.getState()) { + case NEW: + case INITIALIZING: + case INITIALIZED: + case STARTING_PREP: + case STARTING: + return false; + case STARTED: + return true; + default: + // problem, stopped or failed + throw new IllegalStateException("Webapp did not start"); } } 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 94bc1214f0f..87ffa5c5bee 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 @@ -40,6 +40,11 @@ public class WebServer implements MonitoredProcess { tomcat.start(); } + @Override + public boolean isReady() { + return tomcat.isReady(); + } + @Override public void terminate() { tomcat.terminate(); -- 2.39.5