]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4202 Provide a way to stop Sonar Server programmatically
authorJulien HENRY <julien.henry@sonarsource.com>
Thu, 14 Mar 2013 08:21:34 +0000 (09:21 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Thu, 14 Mar 2013 08:21:54 +0000 (09:21 +0100)
sonar-application/src/main/java/org/sonar/application/JettyEmbedder.java
sonar-application/src/main/java/org/sonar/application/ShutdownHandler.java [new file with mode: 0644]

index 55c6639a85e409b95e113833a4348cf5a286611b..98da317bedf9d1d9eff4bed0095b1f2c52dab65b 100644 (file)
 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 (file)
index 0000000..1ca1f48
--- /dev/null
@@ -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:
+ *
+ * <pre>
+    Server server = new Server(8080);
+    HandlerList handlers = new HandlerList();
+    handlers.setHandlers(new Handler[]
+    { someOtherHandler, new ShutdownHandler(server,&quot;secret password&quot;) });
+    server.setHandler(handlers);
+    server.start();
+   </pre>
+ *
+   <pre>
+   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);
+        }
+    }
+  </pre>
+ */
+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;
+  }
+
+}