View Javadoc

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