1 // ========================================================================
2 // Copyright (c) 2009-2009 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // ========================================================================
13
14 package org.eclipse.jetty.server.handler;
15
16 import java.io.IOException;
17
18 import javax.servlet.ServletException;
19 import javax.servlet.http.HttpServletRequest;
20 import javax.servlet.http.HttpServletResponse;
21
22 import org.eclipse.jetty.server.Request;
23 import org.eclipse.jetty.server.Server;
24 import org.eclipse.jetty.util.log.Log;
25 import org.eclipse.jetty.util.log.Logger;
26
27 /* ------------------------------------------------------------ */
28 /**
29 * 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
30 * made.
31 *
32 * This handler is a contribution from Johannes Brodwall: https://bugs.eclipse.org/bugs/show_bug.cgi?id=357687
33 *
34 * Usage:
35 *
36 * <pre>
37 Server server = new Server(8080);
38 HandlerList handlers = new HandlerList();
39 handlers.setHandlers(new Handler[]
40 { someOtherHandler, new ShutdownHandler(server,"secret password") });
41 server.setHandler(handlers);
42 server.start();
43 </pre>
44 *
45 <pre>
46 public static void attemptShutdown(int port, String shutdownCookie) {
47 try {
48 URL url = new URL("http://localhost:" + port + "/shutdown?cookie=" + shutdownCookie);
49 HttpURLConnection connection = (HttpURLConnection)url.openConnection();
50 connection.setRequestMethod("POST");
51 connection.getResponseCode();
52 logger.info("Shutting down " + url + ": " + connection.getResponseMessage());
53 } catch (SocketException e) {
54 logger.debug("Not running");
55 // Okay - the server is not running
56 } catch (IOException e) {
57 throw new RuntimeException(e);
58 }
59 }
60 </pre>
61 */
62 public class ShutdownHandler extends AbstractHandler
63 {
64 private static final Logger LOG = Log.getLogger(ShutdownHandler.class);
65
66 private final String _shutdownToken;
67
68 private final Server _server;
69
70 private boolean _exitJvm = false;
71
72
73
74 /**
75 * Creates a listener that lets the server be shut down remotely (but only from localhost).
76 *
77 * @param server
78 * the Jetty instance that should be shut down
79 * @param shutdownToken
80 * a secret password to avoid unauthorized shutdown attempts
81 */
82 public ShutdownHandler(Server server, String shutdownToken)
83 {
84 this._server = server;
85 this._shutdownToken = shutdownToken;
86 }
87
88 public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
89 {
90 if (!target.equals("/shutdown"))
91 {
92 return;
93 }
94
95 if (!request.getMethod().equals("POST"))
96 {
97 response.sendError(HttpServletResponse.SC_BAD_REQUEST);
98 return;
99 }
100 if (!hasCorrectSecurityToken(request))
101 {
102 LOG.warn("Unauthorized shutdown attempt from " + getRemoteAddr(request));
103 response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
104 return;
105 }
106 if (!requestFromLocalhost(request))
107 {
108 LOG.warn("Unauthorized shutdown attempt from " + getRemoteAddr(request));
109 response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
110 return;
111 }
112
113 LOG.info("Shutting down by request from " + getRemoteAddr(request));
114
115 new Thread()
116 {
117 public void run ()
118 {
119 try
120 {
121 shutdownServer();
122 }
123 catch (InterruptedException e)
124 {
125 LOG.ignore(e);
126 }
127 catch (Exception e)
128 {
129 throw new RuntimeException("Shutting down server",e);
130 }
131 }
132 }.start();
133 }
134
135 private boolean requestFromLocalhost(HttpServletRequest request)
136 {
137 return "127.0.0.1".equals(getRemoteAddr(request));
138 }
139
140 protected String getRemoteAddr(HttpServletRequest request)
141 {
142 return request.getRemoteAddr();
143 }
144
145 private boolean hasCorrectSecurityToken(HttpServletRequest request)
146 {
147 return _shutdownToken.equals(request.getParameter("token"));
148 }
149
150 private void shutdownServer() throws Exception
151 {
152 _server.stop();
153
154 if (_exitJvm)
155 {
156 System.exit(0);
157 }
158 }
159
160 public void setExitJvm(boolean exitJvm)
161 {
162 this._exitJvm = exitJvm;
163 }
164
165 }