summaryrefslogtreecommitdiffstatsabout
diff options
context:
space:
mode:
authorThomas Becker2011-09-30 05:54:35 (EDT)
committer Jesse McConnell2011-09-30 08:58:39 (EDT)
commit37845a1a4fcedd6f583d0de74d3dfbc629f6b0f2 (patch)
tree79b803be33d60c7fa45e35419c0519a6f837f660
parent53b9f1cc7e4275e17aa579f46ca20cc4211dced4 (diff)
downloadorg.eclipse.jetty.project-37845a1a4fcedd6f583d0de74d3dfbc629f6b0f2.zip
org.eclipse.jetty.project-37845a1a4fcedd6f583d0de74d3dfbc629f6b0f2.tar.gz
org.eclipse.jetty.project-37845a1a4fcedd6f583d0de74d3dfbc629f6b0f2.tar.bz2
357687: Add ShutdownHandler + UnitTest
-rw-r--r--jetty-server/pom.xml6
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java125
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java67
-rw-r--r--pom.xml5
4 files changed, 203 insertions, 0 deletions
diff --git a/jetty-server/pom.xml b/jetty-server/pom.xml
index 13f653d..edd48f0 100644
--- a/jetty-server/pom.xml
+++ b/jetty-server/pom.xml
@@ -107,5 +107,11 @@
<version>${project.version}</version>
<optional>true</optional>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>1.8.5</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java
new file mode 100644
index 0000000..a0edc40
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java
@@ -0,0 +1,125 @@
+// ========================================================================
+// Copyright (c) 2009-2009 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+
+package org.eclipse.jetty.server.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * A handler that shuts the server down on a valid request. Used to do "soft" restarts from Java. 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>
+ */
+public class ShutdownHandler extends AbstractHandler
+{
+ private static final Logger LOG = Log.getLogger(ShutdownHandler.class);
+
+ private final String shutdownToken;
+
+ private final Server jettyServer;
+
+ 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.jettyServer = server;
+ this.shutdownToken = shutdownToken;
+ }
+
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ if (!target.equals("/shutdown"))
+ {
+ return;
+ }
+
+ if (!request.getMethod().equals("POST"))
+ {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ if (!hasCorrectSecurityToken(request))
+ {
+ LOG.warn("Unauthorized shutdown attempt from " + getRemoteAddr(request));
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ return;
+ }
+ if (!requestFromLocalhost(request))
+ {
+ LOG.warn("Unauthorized shutdown attempt from " + getRemoteAddr(request));
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ return;
+ }
+
+ LOG.info("Shutting down by request from " + getRemoteAddr(request));
+ try
+ {
+ shutdownServer();
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Shutting down server",e);
+ }
+ }
+
+ 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"));
+ }
+
+ void shutdownServer() throws Exception
+ {
+ jettyServer.stop();
+ if (exitJvm)
+ System.exit(0);
+ }
+
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java
new file mode 100644
index 0000000..0f274ec
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java
@@ -0,0 +1,67 @@
+package org.eclipse.jetty.server.handler;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Server;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class ShutdownHandlerTest
+{
+ @Mock private HttpServletRequest request;
+ @Mock private HttpServletResponse response;
+
+ private Server server = new Server(0);
+ private String shutdownToken = "asdlnsldgnklns";
+
+ // class under test
+ private ShutdownHandler shutdownHandler;
+
+ @Before
+ public void startServer() throws Exception
+ {
+ MockitoAnnotations.initMocks(this);
+ server.start();
+ shutdownHandler = new ShutdownHandler(server,shutdownToken);
+ }
+
+ @Test
+ public void shutdownServerWithCorrectTokenAndIPTest() throws Exception
+ {
+ setDefaultExpectations();
+ shutdownHandler.handle("/shutdown",null,request,response);
+ assertEquals("Server should be stopped","STOPPED",server.getState());
+ }
+
+ @Test
+ public void wrongTokenTest() throws Exception
+ {
+ setDefaultExpectations();
+ when(request.getParameter("token")).thenReturn("anothertoken");
+ shutdownHandler.handle("/shutdown",null,request,response);
+ assertEquals("Server should be running","STARTED",server.getState());
+ }
+
+ @Test
+ public void shutdownRequestNotFromLocalhostTest() throws Exception
+ {
+ setDefaultExpectations();
+ when(request.getRemoteAddr()).thenReturn("192.168.3.3");
+ shutdownHandler.handle("/shutdown",null,request,response);
+ assertEquals("Server should be running","STARTED",server.getState());
+ }
+
+ private void setDefaultExpectations()
+ {
+ when(request.getMethod()).thenReturn("POST");
+ when(request.getParameter("token")).thenReturn(shutdownToken);
+ when(request.getRemoteAddr()).thenReturn("127.0.0.1");
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 92439b3..e37c2c4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -407,6 +407,11 @@
<artifactId>junit</artifactId>
<version>${junit-version}</version>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>1.8.5</version>
+ </dependency>
</dependencies>
</dependencyManagement>
<!--