2 // ========================================================================
3 // Copyright (c) 1995-2014 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.ServerSocket;
27 import java.net.Socket;
28 import java.nio.charset.StandardCharsets;
29 import java.util.Properties;
31 import org.eclipse.jetty.util.thread.ShutdownThread;
34 * Shutdown/Stop Monitor thread.
36 * This thread listens on the port specified by the STOP.PORT system parameter (defaults to -1 for not listening) for request authenticated with the key given
37 * by the STOP.KEY system parameter (defaults to "eclipse") for admin requests.
39 * If the stop port is set to zero, then a random port is assigned and the port number is printed to stdout.
41 * Commands "stop" and "status" are currently supported.
43 public class ShutdownMonitor
45 // Implementation of safe lazy init, using Initialization on Demand Holder technique.
48 static ShutdownMonitor instance = new ShutdownMonitor();
51 public static ShutdownMonitor getInstance()
53 return Holder.instance;
57 * ShutdownMonitorThread
59 * Thread for listening to STOP.PORT for command to stop Jetty.
60 * If ShowndownMonitor.exitVm is true, then Sytem.exit will also be
61 * called after the stop.
64 public class ShutdownMonitorThread extends Thread
67 public ShutdownMonitorThread ()
70 setName("ShutdownMonitor");
76 if (serverSocket == null)
81 while (serverSocket != null)
86 socket = serverSocket.accept();
88 LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
89 String receivedKey = lin.readLine();
90 if (!key.equals(receivedKey))
92 System.err.println("Ignoring command with incorrect key");
96 OutputStream out = socket.getOutputStream();
98 String cmd = lin.readLine();
99 debug("command=%s",cmd);
100 if ("stop".equals(cmd))
103 debug("Issuing graceful shutdown..");
104 ShutdownThread.getInstance().run();
106 //Stop accepting any more
110 //Shutdown input from client
111 shutdownInput(socket);
114 debug("Informing client that we are stopped.");
115 out.write("Stopped\r\n".getBytes(StandardCharsets.UTF_8));
119 socket.shutdownOutput();
122 debug("Shutting down monitor");
127 debug("Killing JVM");
131 else if ("status".equals(cmd))
134 out.write("OK\r\n".getBytes(StandardCharsets.UTF_8));
141 System.err.println(e.toString());
155 // TODO why are we reentrant here?
157 System.err.printf("ShutdownMonitorThread already started");
158 return; // cannot start it again
163 if (serverSocket == null)
168 System.err.println("Starting ShutdownMonitorThread");
172 private void startListenSocket()
177 System.err.println("ShutdownMonitor not in use (port < 0): " + port);
183 serverSocket = new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));
186 // server assigned port in use
187 port = serverSocket.getLocalPort();
188 System.out.printf("STOP.PORT=%d%n",port);
194 key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36);
195 System.out.printf("STOP.KEY=%s%n",key);
201 System.err.println("Error binding monitor port " + port + ": " + e.toString());
206 // establish the port and key that are in use
207 debug("STOP.PORT=%d",port);
208 debug("STOP.KEY=%s",key);
209 debug("%s",serverSocket);
215 private boolean DEBUG;
218 private boolean exitVm;
219 private ServerSocket serverSocket;
220 private ShutdownMonitorThread thread;
225 * Create a ShutdownMonitor using configuration from the System properties.
227 * <code>STOP.PORT</code> = the port to listen on (empty, null, or values less than 0 disable the stop ability)<br>
228 * <code>STOP.KEY</code> = the magic key/passphrase to allow the stop (defaults to "eclipse")<br>
230 * Note: server socket will only listen on localhost, and a successful stop will issue a System.exit() call.
232 private ShutdownMonitor()
234 Properties props = System.getProperties();
236 this.DEBUG = props.containsKey("DEBUG");
238 // Use values passed thru via /jetty-start/
239 this.port = Integer.parseInt(props.getProperty("STOP.PORT","-1"));
240 this.key = props.getProperty("STOP.KEY",null);
244 private void close(ServerSocket server)
255 catch (IOException ignore)
261 private void close(Socket socket)
272 catch (IOException ignore)
279 private void shutdownInput(Socket socket)
286 socket.shutdownInput();
288 catch (IOException ignore)
295 private void debug(String format, Object... args)
299 System.err.printf("[ShutdownMonitor] " + format + "%n",args);
303 private void debug(Throwable t)
307 t.printStackTrace(System.err);
311 public String getKey()
321 public ServerSocket getServerSocket()
326 public boolean isExitVm()
332 public void setDebug(boolean flag)
337 public void setExitVm(boolean exitVm)
341 if (thread != null && thread.isAlive())
343 throw new IllegalStateException("ShutdownMonitorThread already started");
345 this.exitVm = exitVm;
349 public void setKey(String key)
353 if (thread != null && thread.isAlive())
355 throw new IllegalStateException("ShutdownMonitorThread already started");
361 public void setPort(int port)
365 if (thread != null && thread.isAlive())
367 throw new IllegalStateException("ShutdownMonitorThread already started");
373 protected void start() throws Exception
375 ShutdownMonitorThread t = null;
378 if (thread != null && thread.isAlive())
380 // TODO why are we reentrant here?
382 System.err.printf("ShutdownMonitorThread already started");
383 return; // cannot start it again
386 thread = new ShutdownMonitorThread();
395 protected boolean isAlive ()
397 boolean result = false;
400 result = (thread != null && thread.isAlive());
407 public String toString()
409 return String.format("%s[port=%d]",this.getClass().getName(),port);