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.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
@ -260,11 +261,14 @@ public class Server extends HandlerWrapper implements Attributes
|
||||||
@Override
|
@Override
|
||||||
protected void doStart() throws Exception
|
protected void doStart() throws Exception
|
||||||
{
|
{
|
||||||
if (getStopAtShutdown())
|
if (getStopAtShutdown()) {
|
||||||
ShutdownThread.register(this);
|
ShutdownThread.register(this);
|
||||||
|
ShutdownMonitor.getInstance(); // initialize
|
||||||
|
}
|
||||||
|
|
||||||
LOG.info("jetty-"+__version);
|
LOG.info("jetty-"+__version);
|
||||||
HttpGenerator.setServerVersion(__version);
|
HttpGenerator.setServerVersion(__version);
|
||||||
|
|
||||||
MultiException mex=new MultiException();
|
MultiException mex=new MultiException();
|
||||||
|
|
||||||
if (_threadPool==null)
|
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>
|
* <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
|
* Main start class. This class is intended to be the main class listed in the MANIFEST.MF of the start.jar archive. It
|
||||||
* the command "java -jar start.jar".
|
* allows an application to be started with the command "java -jar start.jar".
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <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>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class Main
|
public class Main
|
||||||
|
@ -492,11 +493,6 @@ public class Main
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public void start(List<String> xmls) throws IOException, InterruptedException
|
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)
|
// Load potential Config (start.config)
|
||||||
List<String> configuredXmls = loadConfig(xmls);
|
List<String> configuredXmls = loadConfig(xmls);
|
||||||
|
|
||||||
|
@ -581,9 +577,8 @@ public class Main
|
||||||
copyInThread(process.getErrorStream(),System.err);
|
copyInThread(process.getErrorStream(),System.err);
|
||||||
copyInThread(process.getInputStream(),System.out);
|
copyInThread(process.getInputStream(),System.out);
|
||||||
copyInThread(System.in,process.getOutputStream());
|
copyInThread(System.in,process.getOutputStream());
|
||||||
monitor.setProcess(process);
|
|
||||||
process.waitFor();
|
process.waitFor();
|
||||||
|
System.exit(0); // exit JVM when child process ends.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -688,11 +683,18 @@ public class Main
|
||||||
cmd.addArg(x);
|
cmd.addArg(x);
|
||||||
}
|
}
|
||||||
cmd.addRawArg("-Djetty.home=" + _jettyHome);
|
cmd.addRawArg("-Djetty.home=" + _jettyHome);
|
||||||
|
|
||||||
|
// Special Stop/Shutdown properties
|
||||||
|
ensureSystemPropertySet("STOP.PORT");
|
||||||
|
ensureSystemPropertySet("STOP.KEY");
|
||||||
|
|
||||||
|
// System Properties
|
||||||
for (String p : _sysProps)
|
for (String p : _sysProps)
|
||||||
{
|
{
|
||||||
String v = System.getProperty(p);
|
String v = System.getProperty(p);
|
||||||
cmd.addEqualsArg("-D" + p,v);
|
cmd.addEqualsArg("-D" + p,v);
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.addArg("-cp");
|
cmd.addArg("-cp");
|
||||||
cmd.addRawArg(classpath.toString());
|
cmd.addRawArg(classpath.toString());
|
||||||
cmd.addRawArg(_config.getMainClassname());
|
cmd.addRawArg(_config.getMainClassname());
|
||||||
|
@ -715,6 +717,34 @@ public class Main
|
||||||
return cmd;
|
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()
|
private String findJavaBin()
|
||||||
{
|
{
|
||||||
File javaHome = new File(System.getProperty("java.home"));
|
File javaHome = new File(System.getProperty("java.home"));
|
||||||
|
@ -928,8 +958,8 @@ public class Main
|
||||||
/**
|
/**
|
||||||
* Load Configuration.
|
* Load Configuration.
|
||||||
*
|
*
|
||||||
* No specific configuration is real until a {@link Config#getCombinedClasspath(java.util.Collection)} is used to execute the {@link Class} specified by
|
* No specific configuration is real until a {@link Config#getCombinedClasspath(java.util.Collection)} is used to
|
||||||
* {@link Config#getMainClassname()} is executed.
|
* execute the {@link Class} specified by {@link Config#getMainClassname()} is executed.
|
||||||
*
|
*
|
||||||
* @param xmls
|
* @param xmls
|
||||||
* the command line specified xml configuration options.
|
* the command line specified xml configuration options.
|
||||||
|
@ -1010,7 +1040,6 @@ public class Main
|
||||||
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;
|
int _port = port;
|
||||||
|
|
|
@ -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