2 // ========================================================================
3 // Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.server.handler;
21 import java.io.IOException;
22 import java.net.HttpURLConnection;
23 import java.net.InetSocketAddress;
24 import java.net.SocketException;
27 import javax.servlet.ServletException;
28 import javax.servlet.http.HttpServletRequest;
29 import javax.servlet.http.HttpServletResponse;
31 import org.eclipse.jetty.server.Connector;
32 import org.eclipse.jetty.server.NetworkConnector;
33 import org.eclipse.jetty.server.Request;
34 import org.eclipse.jetty.server.Server;
35 import org.eclipse.jetty.util.log.Log;
36 import org.eclipse.jetty.util.log.Logger;
38 /* ------------------------------------------------------------ */
40 * A handler that shuts the server down on a valid request. Used to do "soft" restarts from Java.
41 * If _exitJvm is set to true a hard System.exit() call is being made.
42 * If _sendShutdownAtStart is set to true, starting the server will try to shut down an existing server at the same port.
43 * If _sendShutdownAtStart is set to true, make a http call to
44 * "http://localhost:" + port + "/shutdown?token=" + shutdownCookie
45 * in order to shut down the server.
47 * This handler is a contribution from Johannes Brodwall: https://bugs.eclipse.org/bugs/show_bug.cgi?id=357687
52 Server server = new Server(8080);
53 HandlerList handlers = new HandlerList();
54 handlers.setHandlers(new Handler[]
55 { someOtherHandler, new ShutdownHandler("secret password", false, true) });
56 server.setHandler(handlers);
61 public static void attemptShutdown(int port, String shutdownCookie) {
63 URL url = new URL("http://localhost:" + port + "/shutdown?token=" + shutdownCookie);
64 HttpURLConnection connection = (HttpURLConnection)url.openConnection();
65 connection.setRequestMethod("POST");
66 connection.getResponseCode();
67 logger.info("Shutting down " + url + ": " + connection.getResponseMessage());
68 } catch (SocketException e) {
69 logger.debug("Not running");
70 // Okay - the server is not running
71 } catch (IOException e) {
72 throw new RuntimeException(e);
77 public class ShutdownHandler extends HandlerWrapper
79 private static final Logger LOG = Log.getLogger(ShutdownHandler.class);
81 private final String _shutdownToken;
82 private boolean _sendShutdownAtStart;
83 private boolean _exitJvm = false;
87 * Creates a listener that lets the server be shut down remotely (but only from localhost).
90 * the Jetty instance that should be shut down
91 * @param shutdownToken
92 * a secret password to avoid unauthorized shutdown attempts
95 public ShutdownHandler(Server server, String shutdownToken)
100 public ShutdownHandler(String shutdownToken)
102 this(shutdownToken,false,false);
106 * @param shutdownToken
107 * a secret password to avoid unauthorized shutdown attempts
108 * @param exitJVM If true, when the shutdown is executed, the handler class System.exit()
109 * @param sendShutdownAtStart If true, a shutdown is sent as a HTTP post
110 * during startup, which will shutdown any previously running instances of
111 * this server with an identically configured ShutdownHandler
113 public ShutdownHandler(String shutdownToken, boolean exitJVM, boolean sendShutdownAtStart)
115 this._shutdownToken = shutdownToken;
117 setSendShutdownAtStart(sendShutdownAtStart);
121 public void sendShutdown() throws IOException
123 URL url = new URL(getServerUrl() + "/shutdown?token=" + _shutdownToken);
126 HttpURLConnection connection = (HttpURLConnection)url.openConnection();
127 connection.setRequestMethod("POST");
128 connection.getResponseCode();
129 LOG.info("Shutting down " + url + ": " + connection.getResponseCode() + " " + connection.getResponseMessage());
131 catch (SocketException e)
133 LOG.debug("Not running");
134 // Okay - the server is not running
136 catch (IOException e)
138 throw new RuntimeException(e);
142 @SuppressWarnings("resource")
143 private String getServerUrl()
145 NetworkConnector connector=null;
146 for (Connector c: getServer().getConnectors())
148 if (c instanceof NetworkConnector)
150 connector=(NetworkConnector)c;
156 return "http://localhost";
158 return "http://localhost:" + connector.getPort();
163 protected void doStart() throws Exception
166 if (_sendShutdownAtStart)
171 public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
173 if (!target.equals("/shutdown"))
175 super.handle(target,baseRequest,request,response);
179 if (!request.getMethod().equals("POST"))
181 response.sendError(HttpServletResponse.SC_BAD_REQUEST);
184 if (!hasCorrectSecurityToken(request))
186 LOG.warn("Unauthorized tokenless shutdown attempt from " + request.getRemoteAddr());
187 response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
190 if (!requestFromLocalhost(baseRequest))
192 LOG.warn("Unauthorized non-loopback shutdown attempt from " + request.getRemoteAddr());
193 response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
197 LOG.info("Shutting down by request from " + request.getRemoteAddr());
198 doShutdown(baseRequest, response);
201 protected void doShutdown(Request baseRequest, HttpServletResponse response) throws IOException {
202 for (Connector connector : getServer().getConnectors()) {
203 connector.shutdown();
206 response.sendError(200, "Connectors closed, commencing full shutdown");
207 baseRequest.setHandled(true);
209 final Server server=getServer();
217 shutdownServer(server);
219 catch (InterruptedException e)
225 throw new RuntimeException("Shutting down server",e);
231 private boolean requestFromLocalhost(Request request)
233 InetSocketAddress addr = request.getRemoteInetSocketAddress();
238 return addr.getAddress().isLoopbackAddress();
241 private boolean hasCorrectSecurityToken(HttpServletRequest request)
243 String tok = request.getParameter("token");
244 if (LOG.isDebugEnabled())
245 LOG.debug("Token: {}", tok);
246 return _shutdownToken.equals(tok);
249 private void shutdownServer(Server server) throws Exception
259 public void setExitJvm(boolean exitJvm)
261 this._exitJvm = exitJvm;
264 public boolean isSendShutdownAtStart()
266 return _sendShutdownAtStart;
269 public void setSendShutdownAtStart(boolean sendShutdownAtStart)
271 _sendShutdownAtStart = sendShutdownAtStart;
274 public String getShutdownToken()
276 return _shutdownToken;
279 public boolean isExitJvm()