391623 - Making --stop with STOP.WAIT perform graceful shutdown
+ Moving jetty-start Monitor to jetty-server ShutdownMonitor and using ShutdownThread to perform a graceful shutdown instead.
This commit is contained in:
parent
7488452e8a
commit
7abd512b32
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.server;
|
|||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -260,11 +261,14 @@ public class Server extends HandlerWrapper implements Attributes
|
|||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
if (getStopAtShutdown())
|
||||
if (getStopAtShutdown()) {
|
||||
ShutdownThread.register(this);
|
||||
ShutdownMonitor.getInstance(); // initialize
|
||||
}
|
||||
|
||||
LOG.info("jetty-"+__version);
|
||||
HttpGenerator.setServerVersion(__version);
|
||||
|
||||
MultiException mex=new MultiException();
|
||||
|
||||
if (_threadPool==null)
|
||||
|
|
|
@ -0,0 +1,235 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.thread.ShutdownThread;
|
||||
|
||||
/**
|
||||
* Shutdown/Stop Monitor thread.
|
||||
* <p>
|
||||
* 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 by the STOP.KEY system parameter (defaults to "eclipse") for admin requests.
|
||||
* <p>
|
||||
* If the stop port is set to zero, then a random port is assigned and the port number is printed to stdout.
|
||||
* <p>
|
||||
* Commands "stop" and "status" are currently supported.
|
||||
*/
|
||||
public class ShutdownMonitor extends Thread
|
||||
{
|
||||
private static final ShutdownMonitor INSTANCE = new ShutdownMonitor();
|
||||
|
||||
public static ShutdownMonitor getInstance()
|
||||
{
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private final boolean DEBUG;
|
||||
private final int port;
|
||||
private final String key;
|
||||
private final ServerSocket serverSocket;
|
||||
|
||||
private ShutdownMonitor()
|
||||
{
|
||||
Properties props = System.getProperties();
|
||||
|
||||
// Use the same debug option as /jetty-start/
|
||||
this.DEBUG = props.containsKey("DEBUG");
|
||||
|
||||
// Use values passed thru /jetty-start/
|
||||
int stopPort = Integer.parseInt(props.getProperty("STOP.PORT","-1"));
|
||||
String stopKey = props.getProperty("STOP.KEY",null);
|
||||
|
||||
ServerSocket sock = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (stopPort < 0)
|
||||
{
|
||||
System.out.println("ShutdownMonitor not in use");
|
||||
sock = null;
|
||||
return;
|
||||
}
|
||||
|
||||
setDaemon(true);
|
||||
setName("ShutdownMonitor");
|
||||
|
||||
sock = new ServerSocket(stopPort,1,InetAddress.getByName("127.0.0.1"));
|
||||
if (stopPort == 0)
|
||||
{
|
||||
// server assigned port in use
|
||||
stopPort = sock.getLocalPort();
|
||||
System.out.printf("STOP.PORT=%d%n",stopPort);
|
||||
}
|
||||
|
||||
if (stopKey == null)
|
||||
{
|
||||
// create random key
|
||||
stopKey = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36);
|
||||
System.out.printf("STOP.KEY=%s%n",stopKey);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
debug(e);
|
||||
System.err.println("Error binding monitor port " + stopPort + ": " + e.toString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
// establish the port and key that are in use
|
||||
this.port = stopPort;
|
||||
this.key = stopKey;
|
||||
|
||||
this.serverSocket = sock;
|
||||
debug("STOP.PORT=%d", port);
|
||||
debug("STOP.KEY=%s", key);
|
||||
debug("%s", serverSocket);
|
||||
}
|
||||
|
||||
this.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[port=%d]",this.getClass().getName(),port);
|
||||
}
|
||||
|
||||
private void debug(Throwable t)
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
t.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
private void debug(String format, Object... args)
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
System.err.printf("[ShutdownMonitor] " + format + "%n",args);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Socket socket = null;
|
||||
try
|
||||
{
|
||||
socket = serverSocket.accept();
|
||||
|
||||
LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
|
||||
String key = lin.readLine();
|
||||
if (!this.key.equals(key))
|
||||
{
|
||||
System.err.println("Ignoring command with incorrect key");
|
||||
continue;
|
||||
}
|
||||
|
||||
OutputStream out = socket.getOutputStream();
|
||||
|
||||
String cmd = lin.readLine();
|
||||
debug("command=%s",cmd);
|
||||
if ("stop".equals(cmd))
|
||||
{
|
||||
// Graceful Shutdown
|
||||
debug("Issuing graceful shutdown..");
|
||||
ShutdownThread.getInstance().run();
|
||||
|
||||
// Reply to client
|
||||
debug("Informing client that we are stopped.");
|
||||
out.write("Stopped\r\n".getBytes(StringUtil.__UTF8));
|
||||
out.flush();
|
||||
|
||||
// Shutdown Monitor
|
||||
debug("Shutting down monitor");
|
||||
close(socket);
|
||||
close(serverSocket);
|
||||
|
||||
// Kill JVM
|
||||
debug("Killing JVM");
|
||||
System.exit(0);
|
||||
}
|
||||
else if ("status".equals(cmd))
|
||||
{
|
||||
// Reply to client
|
||||
out.write("OK\r\n".getBytes(StringUtil.__UTF8));
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
debug(e);
|
||||
System.err.println(e.toString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
close(socket);
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void close(Socket socket)
|
||||
{
|
||||
if (socket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException ignore)
|
||||
{
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
private void close(ServerSocket server)
|
||||
{
|
||||
if (server == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
server.close();
|
||||
}
|
||||
catch (IOException ignore)
|
||||
{
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
|
@ -53,12 +53,13 @@ import java.util.Set;
|
|||
/*-------------------------------------------*/
|
||||
/**
|
||||
* <p>
|
||||
* Main start class. This class is intended to be the main class listed in the MANIFEST.MF of the start.jar archive. It allows an application to be started with
|
||||
* the command "java -jar start.jar".
|
||||
* Main start class. This class is intended to be the main class listed in the MANIFEST.MF of the start.jar archive. It
|
||||
* allows an application to be started with the command "java -jar start.jar".
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* The behaviour of Main is controlled by the parsing of the {@link Config} "org/eclipse/start/start.config" file obtained as a resource or file.
|
||||
* The behaviour of Main is controlled by the parsing of the {@link Config} "org/eclipse/start/start.config" file
|
||||
* obtained as a resource or file.
|
||||
* </p>
|
||||
*/
|
||||
public class Main
|
||||
|
@ -91,7 +92,7 @@ public class Main
|
|||
Main main = new Main();
|
||||
List<String> arguments = main.expandCommandLine(args);
|
||||
List<String> xmls = main.processCommandLine(arguments);
|
||||
if (xmls!=null)
|
||||
if (xmls != null)
|
||||
main.start(xmls);
|
||||
}
|
||||
catch (Throwable e)
|
||||
|
@ -143,7 +144,7 @@ public class Main
|
|||
|
||||
List<String> parseStartIniFiles()
|
||||
{
|
||||
List<String> ini_args=new ArrayList<String>();
|
||||
List<String> ini_args = new ArrayList<String>();
|
||||
File start_ini = new File(_jettyHome,"start.ini");
|
||||
if (start_ini.exists())
|
||||
ini_args.addAll(loadStartIni(start_ini));
|
||||
|
@ -179,14 +180,14 @@ public class Main
|
|||
_showUsage = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if ("--stop".equals(arg))
|
||||
{
|
||||
int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1"));
|
||||
String key = Config.getProperty("STOP.KEY",null);
|
||||
int timeout = Integer.parseInt(Config.getProperty("STOP.WAIT", "0"));
|
||||
int timeout = Integer.parseInt(Config.getProperty("STOP.WAIT","0"));
|
||||
stop(port,key,timeout);
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
if ("--version".equals(arg) || "-v".equals(arg) || "--info".equals(arg))
|
||||
|
@ -226,7 +227,7 @@ public class Main
|
|||
if (!startDir.exists() || !startDir.canWrite())
|
||||
startDir = new File(".");
|
||||
|
||||
File startLog = new File(startDir, START_LOG_ROLLOVER_DATEFORMAT.format(new Date()));
|
||||
File startLog = new File(startDir,START_LOG_ROLLOVER_DATEFORMAT.format(new Date()));
|
||||
|
||||
if (!startLog.exists() && !startLog.createNewFile())
|
||||
{
|
||||
|
@ -246,7 +247,7 @@ public class Main
|
|||
PrintStream logger = new PrintStream(new FileOutputStream(startLog,false));
|
||||
System.setOut(logger);
|
||||
System.setErr(logger);
|
||||
System.out.println("Establishing "+ START_LOG_FILENAME + " on " + new Date());
|
||||
System.out.println("Establishing " + START_LOG_FILENAME + " on " + new Date());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -492,11 +493,6 @@ public class Main
|
|||
/* ------------------------------------------------------------ */
|
||||
public void start(List<String> xmls) throws IOException, InterruptedException
|
||||
{
|
||||
// Setup Start / Stop Monitoring
|
||||
int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1"));
|
||||
String key = Config.getProperty("STOP.KEY",null);
|
||||
Monitor monitor = new Monitor(port,key);
|
||||
|
||||
// Load potential Config (start.config)
|
||||
List<String> configuredXmls = loadConfig(xmls);
|
||||
|
||||
|
@ -581,9 +577,8 @@ public class Main
|
|||
copyInThread(process.getErrorStream(),System.err);
|
||||
copyInThread(process.getInputStream(),System.out);
|
||||
copyInThread(System.in,process.getOutputStream());
|
||||
monitor.setProcess(process);
|
||||
process.waitFor();
|
||||
|
||||
System.exit(0); // exit JVM when child process ends.
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -688,11 +683,18 @@ public class Main
|
|||
cmd.addArg(x);
|
||||
}
|
||||
cmd.addRawArg("-Djetty.home=" + _jettyHome);
|
||||
|
||||
// Special Stop/Shutdown properties
|
||||
ensureSystemPropertySet("STOP.PORT");
|
||||
ensureSystemPropertySet("STOP.KEY");
|
||||
|
||||
// System Properties
|
||||
for (String p : _sysProps)
|
||||
{
|
||||
String v = System.getProperty(p);
|
||||
cmd.addEqualsArg("-D" + p,v);
|
||||
}
|
||||
|
||||
cmd.addArg("-cp");
|
||||
cmd.addRawArg(classpath.toString());
|
||||
cmd.addRawArg(_config.getMainClassname());
|
||||
|
@ -715,6 +717,34 @@ public class Main
|
|||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the System Properties are set (if defined as a System property, or start.config property, or
|
||||
* start.ini property)
|
||||
*
|
||||
* @param key
|
||||
* the key to be sure of
|
||||
*/
|
||||
private void ensureSystemPropertySet(String key)
|
||||
{
|
||||
if (_sysProps.contains(key))
|
||||
{
|
||||
return; // done
|
||||
}
|
||||
|
||||
Properties props = Config.getProperties();
|
||||
if (props.containsKey(key))
|
||||
{
|
||||
String val = props.getProperty(key,null);
|
||||
if (val == null)
|
||||
{
|
||||
return; // no value to set
|
||||
}
|
||||
// setup system property
|
||||
_sysProps.add(key);
|
||||
System.setProperty(key,val);
|
||||
}
|
||||
}
|
||||
|
||||
private String findJavaBin()
|
||||
{
|
||||
File javaHome = new File(System.getProperty("java.home"));
|
||||
|
@ -927,10 +957,10 @@ public class Main
|
|||
|
||||
/**
|
||||
* Load Configuration.
|
||||
*
|
||||
* No specific configuration is real until a {@link Config#getCombinedClasspath(java.util.Collection)} is used to execute the {@link Class} specified by
|
||||
* {@link Config#getMainClassname()} is executed.
|
||||
*
|
||||
*
|
||||
* No specific configuration is real until a {@link Config#getCombinedClasspath(java.util.Collection)} is used to
|
||||
* execute the {@link Class} specified by {@link Config#getMainClassname()} is executed.
|
||||
*
|
||||
* @param xmls
|
||||
* the command line specified xml configuration options.
|
||||
* @return the list of xml configurations arriving via command line and start.config choices.
|
||||
|
@ -1007,11 +1037,10 @@ public class Main
|
|||
*/
|
||||
public void stop(int port, String key)
|
||||
{
|
||||
stop (port,key, 0);
|
||||
stop(port,key,0);
|
||||
}
|
||||
|
||||
|
||||
public void stop (int port, String key, int timeout)
|
||||
public void stop(int port, String key, int timeout)
|
||||
{
|
||||
int _port = port;
|
||||
String _key = key;
|
||||
|
@ -1031,7 +1060,7 @@ public class Main
|
|||
|
||||
Socket s = new Socket(InetAddress.getByName("127.0.0.1"),_port);
|
||||
if (timeout > 0)
|
||||
s.setSoTimeout(timeout*1000);
|
||||
s.setSoTimeout(timeout * 1000);
|
||||
try
|
||||
{
|
||||
OutputStream out = s.getOutputStream();
|
||||
|
@ -1040,7 +1069,7 @@ public class Main
|
|||
|
||||
if (timeout > 0)
|
||||
{
|
||||
System.err.printf("Waiting %,d seconds for jetty to stop%n", timeout);
|
||||
System.err.printf("Waiting %,d seconds for jetty to stop%n",timeout);
|
||||
LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));
|
||||
String response;
|
||||
while ((response = lin.readLine()) != null)
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.start;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
|
||||
/*-------------------------------------------*/
|
||||
/** Monitor thread.
|
||||
* 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 by the STOP.KEY
|
||||
* system parameter (defaults to "eclipse") for admin requests.
|
||||
* <p>
|
||||
* If the stop port is set to zero, then a random port is assigned and the port number
|
||||
* is printed to stdout.
|
||||
* <p>
|
||||
* Commands "stop" and * "status" are currently supported.
|
||||
*
|
||||
*/
|
||||
public class Monitor extends Thread
|
||||
{
|
||||
private Process _process;
|
||||
private final int _port;
|
||||
private final String _key;
|
||||
|
||||
ServerSocket _socket;
|
||||
|
||||
public Monitor(int port,String key)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(port<0)
|
||||
return;
|
||||
setDaemon(true);
|
||||
setName("StopMonitor");
|
||||
_socket=new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));
|
||||
if (port==0)
|
||||
{
|
||||
port=_socket.getLocalPort();
|
||||
System.out.println(port);
|
||||
}
|
||||
|
||||
if (key==null)
|
||||
{
|
||||
key=Long.toString((long)(Long.MAX_VALUE*Math.random()+this.hashCode()+System.currentTimeMillis()),36);
|
||||
System.out.println("STOP.KEY="+key);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Config.debug(e);
|
||||
System.err.println("Error binding monitor port "+port+": "+e.toString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
_port=port;
|
||||
_key=key;
|
||||
}
|
||||
|
||||
if (_socket!=null)
|
||||
this.start();
|
||||
else
|
||||
System.err.println("WARN: Not listening on monitor port: "+_port);
|
||||
}
|
||||
|
||||
public Process getProcess()
|
||||
{
|
||||
return _process;
|
||||
}
|
||||
|
||||
public void setProcess(Process process)
|
||||
{
|
||||
_process = process;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Socket socket=null;
|
||||
try{
|
||||
socket=_socket.accept();
|
||||
|
||||
LineNumberReader lin=
|
||||
new LineNumberReader(new InputStreamReader(socket.getInputStream()));
|
||||
String key=lin.readLine();
|
||||
if (!_key.equals(key))
|
||||
{
|
||||
System.err.println("Ignoring command with incorrect key");
|
||||
continue;
|
||||
}
|
||||
|
||||
String cmd=lin.readLine();
|
||||
Config.debug("command=" + cmd);
|
||||
if ("stop".equals(cmd))
|
||||
{
|
||||
Config.debug("Child Process: " + _process);
|
||||
if (_process!=null)
|
||||
{
|
||||
//if we have a child process, wait for it to finish before we stop
|
||||
try
|
||||
{
|
||||
_process.destroy();
|
||||
_process.waitFor();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
System.err.println("Interrupted waiting for child to terminate");
|
||||
}
|
||||
}
|
||||
socket.getOutputStream().write("Stopped\r\n".getBytes());
|
||||
try {socket.close();}catch(Exception e){e.printStackTrace();}
|
||||
try {_socket.close();}catch(Exception e){e.printStackTrace();}
|
||||
System.exit(0);
|
||||
}
|
||||
else if ("status".equals(cmd))
|
||||
{
|
||||
socket.getOutputStream().write("OK\r\n".getBytes());
|
||||
socket.getOutputStream().flush();
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Config.debug(e);
|
||||
System.err.println(e.toString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (socket!=null)
|
||||
{
|
||||
try{socket.close();}catch(Exception e){}
|
||||
}
|
||||
socket=null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue