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  
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 }