From: Julien HENRY Date: Thu, 14 Mar 2013 08:21:34 +0000 (+0100) Subject: SONAR-4202 Provide a way to stop Sonar Server programmatically X-Git-Tag: 3.6~889 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=86ffe2dbd5bc065d2c3312c48d4d0c5fd8fd1de1;p=sonarqube.git SONAR-4202 Provide a way to stop Sonar Server programmatically --- diff --git a/sonar-application/src/main/java/org/sonar/application/JettyEmbedder.java b/sonar-application/src/main/java/org/sonar/application/JettyEmbedder.java index 55c6639a85e..98da317bedf 100644 --- a/sonar-application/src/main/java/org/sonar/application/JettyEmbedder.java +++ b/sonar-application/src/main/java/org/sonar/application/JettyEmbedder.java @@ -20,8 +20,10 @@ package org.sonar.application; import org.apache.commons.io.FileUtils; +import org.mortbay.jetty.Handler; import org.mortbay.jetty.NCSARequestLog; import org.mortbay.jetty.Server; +import org.mortbay.jetty.handler.HandlerList; import org.mortbay.jetty.handler.RequestLogHandler; import org.mortbay.jetty.nio.SelectChannelConnector; import org.mortbay.jetty.webapp.WebAppContext; @@ -89,7 +91,18 @@ public class JettyEmbedder { private Server configureProgrammatically() throws URISyntaxException { configureServer(); WebAppContext context = new WebAppContext(getPath("/war/sonar-server"), contextPath); - server.addHandler(context); + String shutdownCookie = System.getProperty("sonar.shutdownToken"); + if (shutdownCookie != null && !"".equals(shutdownCookie)) { + System.out.println("Registering shutdown handler"); + ShutdownHandler shutdownHandler = new ShutdownHandler(server, shutdownCookie); + shutdownHandler.setExitJvm(true); + HandlerList handlers = new HandlerList(); + handlers.setHandlers(new Handler[] {shutdownHandler, context}); + server.setHandler(handlers); + } + else { + server.addHandler(context); + } return server; } diff --git a/sonar-application/src/main/java/org/sonar/application/ShutdownHandler.java b/sonar-application/src/main/java/org/sonar/application/ShutdownHandler.java new file mode 100644 index 00000000000..1ca1f485e12 --- /dev/null +++ b/sonar-application/src/main/java/org/sonar/application/ShutdownHandler.java @@ -0,0 +1,132 @@ +package org.sonar.application; + +import org.mortbay.jetty.Server; +import org.mortbay.jetty.handler.AbstractHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import java.io.IOException; + +/* ------------------------------------------------------------ */ +/** + * TODO Duplicate code from Jetty 8 waiting for upgrade. + * + * + * A handler that shuts the server down on a valid request. Used to do "soft" restarts from Java. If _exitJvm ist set to true a hard System.exit() call is being + * made. + * + * This handler is a contribution from Johannes Brodwall: https://bugs.eclipse.org/bugs/show_bug.cgi?id=357687 + * + * Usage: + * + *
+    Server server = new Server(8080);
+    HandlerList handlers = new HandlerList();
+    handlers.setHandlers(new Handler[]
+    { someOtherHandler, new ShutdownHandler(server,"secret password") });
+    server.setHandler(handlers);
+    server.start();
+   
+ * +
+   public static void attemptShutdown(int port, String shutdownCookie) {
+        try {
+            URL url = new URL("http://localhost:" + port + "/shutdown?token=" + shutdownCookie);
+            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+            connection.setRequestMethod("POST");
+            connection.getResponseCode();
+            logger.info("Shutting down " + url + ": " + connection.getResponseMessage());
+        } catch (SocketException e) {
+            logger.debug("Not running");
+            // Okay - the server is not running
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+  
+ */ +public class ShutdownHandler extends AbstractHandler { + + private final String _shutdownToken; + + private final Server _server; + + private boolean _exitJvm = false; + + /** + * Creates a listener that lets the server be shut down remotely (but only from localhost). + * + * @param server + * the Jetty instance that should be shut down + * @param shutdownToken + * a secret password to avoid unauthorized shutdown attempts + */ + public ShutdownHandler(Server server, String shutdownToken) { + this._server = server; + this._shutdownToken = shutdownToken; + } + + public void handle(String target, HttpServletRequest request, HttpServletResponse response, + int dispatch) throws IOException, ServletException { + if (!target.equals("/shutdown")) { + return; + } + + if (!request.getMethod().equals("POST")) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } + if (!hasCorrectSecurityToken(request)) { + System.err.println("Unauthorized shutdown attempt from " + getRemoteAddr(request)); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + if (!requestFromLocalhost(request)) { + System.err.println("Unauthorized shutdown attempt from " + getRemoteAddr(request)); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + + System.out.println("Shutting down by request from " + getRemoteAddr(request)); + + new Thread() { + public void run() { + try { + shutdownServer(); + } catch (InterruptedException e) { + // Ignored + } catch (Exception e) { + throw new RuntimeException("Shutting down server", e); + } + } + }.start(); + } + + private boolean requestFromLocalhost(HttpServletRequest request) { + return "127.0.0.1".equals(getRemoteAddr(request)); + } + + protected String getRemoteAddr(HttpServletRequest request) { + return request.getRemoteAddr(); + } + + private boolean hasCorrectSecurityToken(HttpServletRequest request) { + return _shutdownToken.equals(request.getParameter("token")); + } + + private void shutdownServer() throws Exception { + _server.stop(); + + if (_exitJvm) + { + System.exit(0); + } + } + + public void setExitJvm(boolean exitJvm) { + this._exitJvm = exitJvm; + } + +}