View Javadoc

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,&quot;secret password&quot;) });
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       * Creates a listener that lets the server be shut down remotely (but only from localhost).
74       *
75       * @param server
76       *            the Jetty instance that should be shut down
77       * @param shutdownToken
78       *            a secret password to avoid unauthorized shutdown attempts
79       */
80      public ShutdownHandler(Server server, String shutdownToken)
81      {
82          this._server = server;
83          this._shutdownToken = shutdownToken;
84      }
85  
86      public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
87      {
88          if (!target.equals("/shutdown"))
89          {
90              return;
91          }
92  
93          if (!request.getMethod().equals("POST"))
94          {
95              response.sendError(HttpServletResponse.SC_BAD_REQUEST);
96              return;
97          }
98          if (!hasCorrectSecurityToken(request))
99          {
100             LOG.warn("Unauthorized shutdown attempt from " + getRemoteAddr(request));
101             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
102             return;
103         }
104         if (!requestFromLocalhost(request))
105         {
106             LOG.warn("Unauthorized shutdown attempt from " + getRemoteAddr(request));
107             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
108             return;
109         }
110 
111         LOG.info("Shutting down by request from " + getRemoteAddr(request));
112         
113         try
114         {
115             shutdownServer();
116         }
117         catch (Exception e)
118         {
119             throw new RuntimeException("Shutting down server",e);
120         }
121     }
122 
123     private boolean requestFromLocalhost(HttpServletRequest request)
124     {
125         return "127.0.0.1".equals(getRemoteAddr(request));
126     }
127 
128     protected String getRemoteAddr(HttpServletRequest request)
129     {
130         return request.getRemoteAddr();
131     }
132 
133     private boolean hasCorrectSecurityToken(HttpServletRequest request)
134     {
135         return _shutdownToken.equals(request.getParameter("token"));
136     }
137 
138     private void shutdownServer() throws Exception
139     {
140         _server.stop();
141         
142         if (_exitJvm)
143         {
144             System.exit(0);
145         }
146     }
147 
148     public void setExitJvm(boolean exitJvm)
149     {
150         this._exitJvm = exitJvm;
151     }
152 
153 }