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;
21 import java.io.IOException;
22 import java.io.InputStreamReader;
23 import java.io.LineNumberReader;
24 import java.io.OutputStream;
25 import java.net.InetAddress;
26 import java.net.InetSocketAddress;
27 import java.net.ServerSocket;
28 import java.net.Socket;
29 import java.nio.charset.StandardCharsets;
30 import java.util.Arrays;
31 import java.util.Properties;
33 import java.util.concurrent.CopyOnWriteArraySet;
35 import org.eclipse.jetty.util.component.Destroyable;
36 import org.eclipse.jetty.util.component.LifeCycle;
37 import org.eclipse.jetty.util.thread.ShutdownThread;
40 * Shutdown/Stop Monitor thread.
42 * This thread listens on the host/port specified by the STOP.HOST/STOP.PORT system parameter (defaults to 127.0.0.1/-1 for not listening) for
43 * request authenticated with the key given by the STOP.KEY system parameter (defaults to "eclipse") for admin requests.
45 * If the stop port is set to zero, then a random port is assigned and the port number is printed to stdout.
47 * Commands "stop" and "status" are currently supported.
49 public class ShutdownMonitor
51 private final Set<LifeCycle> _lifeCycles = new CopyOnWriteArraySet<LifeCycle>();
53 // Implementation of safe lazy init, using Initialization on Demand Holder technique.
56 static ShutdownMonitor instance = new ShutdownMonitor();
59 public static ShutdownMonitor getInstance()
61 return Holder.instance;
64 /* ------------------------------------------------------------ */
65 public static synchronized void register(LifeCycle... lifeCycles)
67 getInstance()._lifeCycles.addAll(Arrays.asList(lifeCycles));
71 /* ------------------------------------------------------------ */
72 public static synchronized void deregister(LifeCycle lifeCycle)
74 getInstance()._lifeCycles.remove(lifeCycle);
77 /* ------------------------------------------------------------ */
78 public static synchronized boolean isRegistered(LifeCycle lifeCycle)
80 return getInstance()._lifeCycles.contains(lifeCycle);
83 /* ------------------------------------------------------------ */
85 * ShutdownMonitorRunnable
87 * Thread for listening to STOP.PORT for command to stop Jetty.
88 * If ShowndownMonitor.exitVm is true, then Sytem.exit will also be
89 * called after the stop.
92 private class ShutdownMonitorRunnable implements Runnable
94 public ShutdownMonitorRunnable()
102 if (serverSocket == null)
107 while (serverSocket != null)
109 Socket socket = null;
112 socket = serverSocket.accept();
114 LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
115 String receivedKey = lin.readLine();
116 if (!key.equals(receivedKey))
118 System.err.println("Ignoring command with incorrect key");
122 OutputStream out = socket.getOutputStream();
124 String cmd = lin.readLine();
125 debug("command=%s",cmd);
126 if ("stop".equalsIgnoreCase(cmd)) //historic, for backward compatibility
128 //Stop the lifecycles, only if they are registered with the ShutdownThread, only destroying if vm is exiting
129 debug("Issuing stop...");
131 for (LifeCycle l:_lifeCycles)
135 if (l.isStarted() && ShutdownThread.isRegistered(l))
140 if ((l instanceof Destroyable) && exitVm)
141 ((Destroyable)l).destroy();
149 //Stop accepting any more commands
153 debug("Informing client that we are stopped.");
154 informClient(out, "Stopped\r\n");
156 //Stop the output and close the monitor socket
162 debug("Killing JVM");
166 else if ("forcestop".equalsIgnoreCase(cmd))
168 debug("Issuing force stop...");
170 //Ensure that objects are stopped, destroyed only if vm is forcibly exiting
171 stopLifeCycles(exitVm);
173 //Stop accepting any more commands
177 debug("Informing client that we are stopped.");
178 informClient(out, "Stopped\r\n");
180 //Stop the output and close the monitor socket
183 //Honour any pre-setup config to stop the jvm when this command is given
187 debug("Killing JVM");
191 else if ("stopexit".equalsIgnoreCase(cmd))
193 debug("Issuing stop and exit...");
194 //Make sure that objects registered with the shutdown thread will be stopped
195 stopLifeCycles(true);
197 //Stop accepting any more input
201 debug("Informing client that we are stopped.");
202 informClient(out, "Stopped\r\n");
204 //Stop the output and close the monitor socket
207 debug("Killing JVM");
210 else if ("exit".equalsIgnoreCase(cmd))
212 debug("Killing JVM");
215 else if ("status".equalsIgnoreCase(cmd))
218 informClient(out, "OK\r\n");
224 System.err.println(e.toString());
234 public void stopInput (Socket socket)
236 //Stop accepting any more input
239 //Shutdown input from client
240 shutdownInput(socket);
243 public void stopOutput (Socket socket) throws IOException
245 socket.shutdownOutput();
248 debug("Shutting down monitor");
252 public void informClient (OutputStream out, String message) throws IOException
254 out.write(message.getBytes(StandardCharsets.UTF_8));
259 * Stop the registered lifecycles, optionally
260 * calling destroy on them.
264 public void stopLifeCycles (boolean destroy)
266 for (LifeCycle l:_lifeCycles)
275 if ((l instanceof Destroyable) && destroy)
276 ((Destroyable)l).destroy();
285 public void startListenSocket()
290 System.err.println("ShutdownMonitor not in use (port < 0): " + port);
296 serverSocket = new ServerSocket();
297 serverSocket.setReuseAddress(true);
298 serverSocket.bind(new InetSocketAddress(InetAddress.getByName(host), port), 1);
301 // server assigned port in use
302 port = serverSocket.getLocalPort();
303 System.out.printf("STOP.PORT=%d%n",port);
309 key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36);
310 System.out.printf("STOP.KEY=%s%n",key);
316 System.err.println("Error binding monitor port " + port + ": " + e.toString());
321 // establish the port and key that are in use
322 debug("STOP.PORT=%d",port);
323 debug("STOP.KEY=%s",key);
324 debug("%s",serverSocket);
330 private boolean DEBUG;
334 private boolean exitVm;
335 private ServerSocket serverSocket;
336 private Thread thread;
339 * Create a ShutdownMonitor using configuration from the System properties.
341 * <code>STOP.PORT</code> = the port to listen on (empty, null, or values less than 0 disable the stop ability)<br>
342 * <code>STOP.KEY</code> = the magic key/passphrase to allow the stop (defaults to "eclipse")<br>
344 * Note: server socket will only listen on localhost, and a successful stop will issue a System.exit() call.
346 private ShutdownMonitor()
348 this.DEBUG = System.getProperty("DEBUG") != null;
350 // Use values passed thru via /jetty-start/
351 this.host = System.getProperty("STOP.HOST","127.0.0.1");
352 this.port = Integer.parseInt(System.getProperty("STOP.PORT","-1"));
353 this.key = System.getProperty("STOP.KEY",null);
357 private void close(ServerSocket server)
368 catch (IOException ignore)
374 private void close(Socket socket)
385 catch (IOException ignore)
392 private void shutdownInput(Socket socket)
399 socket.shutdownInput();
401 catch (IOException ignore)
408 private void debug(String format, Object... args)
412 System.err.printf("[ShutdownMonitor] " + format + "%n",args);
416 private void debug(Throwable t)
420 t.printStackTrace(System.err);
424 public String getKey()
434 public ServerSocket getServerSocket()
439 public boolean isExitVm()
445 public void setDebug(boolean flag)
453 public void setExitVm(boolean exitVm)
457 if (thread != null && thread.isAlive())
459 throw new IllegalStateException("ShutdownMonitorThread already started");
461 this.exitVm = exitVm;
465 public void setKey(String key)
469 if (thread != null && thread.isAlive())
471 throw new IllegalStateException("ShutdownMonitorThread already started");
477 public void setPort(int port)
481 if (thread != null && thread.isAlive())
483 throw new IllegalStateException("ShutdownMonitorThread already started");
489 protected void start() throws Exception
495 if (thread != null && thread.isAlive())
498 System.err.printf("ShutdownMonitorThread already started");
499 return; // cannot start it again
502 thread = new Thread(new ShutdownMonitorRunnable());
503 thread.setDaemon(true);
504 thread.setName("ShutdownMonitor");
513 protected boolean isAlive ()
515 boolean result = false;
518 result = (thread != null && thread.isAlive());
525 public String toString()
527 return String.format("%s[port=%d]",this.getClass().getName(),port);