Merge branch 'jetty-9.3.x' of github.com:eclipse/jetty.project into jetty-9.3.x
This commit is contained in:
commit
63f9224009
|
@ -28,7 +28,6 @@ import java.net.Socket;
|
|||
import java.net.SocketTimeoutException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
|
@ -49,16 +48,16 @@ public abstract class SslBytesTest
|
|||
|
||||
public static class TLSRecord
|
||||
{
|
||||
private final SslBytesServerTest.TLSRecord.Type type;
|
||||
private final Type type;
|
||||
private final byte[] bytes;
|
||||
|
||||
public TLSRecord(SslBytesServerTest.TLSRecord.Type type, byte[] bytes)
|
||||
public TLSRecord(Type type, byte[] bytes)
|
||||
{
|
||||
this.type = type;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
public SslBytesServerTest.TLSRecord.Type getType()
|
||||
public Type getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
@ -80,15 +79,15 @@ public abstract class SslBytesTest
|
|||
|
||||
private int code;
|
||||
|
||||
private Type(int code)
|
||||
Type(int code)
|
||||
{
|
||||
this.code = code;
|
||||
SslBytesServerTest.TLSRecord.Type.Mapper.codes.put(this.code, this);
|
||||
Mapper.codes.put(this.code, this);
|
||||
}
|
||||
|
||||
public static SslBytesServerTest.TLSRecord.Type from(int code)
|
||||
public static Type from(int code)
|
||||
{
|
||||
SslBytesServerTest.TLSRecord.Type result = SslBytesServerTest.TLSRecord.Type.Mapper.codes.get(code);
|
||||
Type result = Mapper.codes.get(code);
|
||||
if (result == null)
|
||||
throw new IllegalArgumentException("Invalid TLSRecord.Type " + code);
|
||||
return result;
|
||||
|
@ -96,7 +95,7 @@ public abstract class SslBytesTest
|
|||
|
||||
private static class Mapper
|
||||
{
|
||||
private static final Map<Integer, SslBytesServerTest.TLSRecord.Type> codes = new HashMap<>();
|
||||
private static final Map<Integer, Type> codes = new HashMap<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +217,7 @@ public abstract class SslBytesTest
|
|||
}
|
||||
}
|
||||
|
||||
private TLSRecord read(SslBytesServerTest.TLSRecord.Type type, InputStream input, byte[] bytes, int offset, int length) throws IOException
|
||||
private TLSRecord read(TLSRecord.Type type, InputStream input, byte[] bytes, int offset, int length) throws IOException
|
||||
{
|
||||
while (length > 0)
|
||||
{
|
||||
|
@ -291,13 +290,11 @@ public abstract class SslBytesTest
|
|||
}
|
||||
}
|
||||
|
||||
public SslBytesServerTest.SimpleProxy.AutomaticFlow startAutomaticFlow() throws InterruptedException
|
||||
public SslBytesTest.SimpleProxy.AutomaticFlow startAutomaticFlow() throws InterruptedException
|
||||
{
|
||||
final CountDownLatch startLatch = new CountDownLatch(2);
|
||||
final CountDownLatch stopLatch = new CountDownLatch(2);
|
||||
Future<Object> clientToServer = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
public Object call() throws Exception
|
||||
Future<Object> clientToServer = threadPool.submit(() ->
|
||||
{
|
||||
startLatch.countDown();
|
||||
logger.debug("Automatic flow C --> S started");
|
||||
|
@ -317,11 +314,8 @@ public abstract class SslBytesTest
|
|||
stopLatch.countDown();
|
||||
logger.debug("Automatic flow C --> S finished");
|
||||
}
|
||||
}
|
||||
});
|
||||
Future<Object> serverToClient = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
public Object call() throws Exception
|
||||
Future<Object> serverToClient = threadPool.submit(() ->
|
||||
{
|
||||
startLatch.countDown();
|
||||
logger.debug("Automatic flow C <-- S started");
|
||||
|
@ -341,7 +335,6 @@ public abstract class SslBytesTest
|
|||
stopLatch.countDown();
|
||||
logger.debug("Automatic flow C <-- S finished");
|
||||
}
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(startLatch.await(5, TimeUnit.SECONDS));
|
||||
return new SslBytesServerTest.SimpleProxy.AutomaticFlow(stopLatch, clientToServer, serverToClient);
|
||||
|
|
|
@ -27,10 +27,12 @@ import java.net.InetSocketAddress;
|
|||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.eclipse.jetty.util.component.Destroyable;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
|
@ -39,19 +41,20 @@ import org.eclipse.jetty.util.thread.ShutdownThread;
|
|||
/**
|
||||
* Shutdown/Stop Monitor thread.
|
||||
* <p>
|
||||
* 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
|
||||
* request authenticated with the key given by the STOP.KEY system parameter (defaults to "eclipse") for admin requests.
|
||||
* 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 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.
|
||||
* 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
|
||||
{
|
||||
private final Set<LifeCycle> _lifeCycles = new CopyOnWriteArraySet<LifeCycle>();
|
||||
|
||||
// Implementation of safe lazy init, using Initialization on Demand Holder technique.
|
||||
static class Holder
|
||||
private static class Holder
|
||||
{
|
||||
static ShutdownMonitor instance = new ShutdownMonitor();
|
||||
}
|
||||
|
@ -61,149 +64,326 @@ public class ShutdownMonitor
|
|||
return Holder.instance;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static synchronized void register(LifeCycle... lifeCycles)
|
||||
public static void register(LifeCycle... lifeCycles)
|
||||
{
|
||||
getInstance()._lifeCycles.addAll(Arrays.asList(lifeCycles));
|
||||
getInstance().addLifeCycles(lifeCycles);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static synchronized void deregister(LifeCycle lifeCycle)
|
||||
public static void deregister(LifeCycle lifeCycle)
|
||||
{
|
||||
getInstance()._lifeCycles.remove(lifeCycle);
|
||||
getInstance().removeLifeCycle(lifeCycle);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static synchronized boolean isRegistered(LifeCycle lifeCycle)
|
||||
public static boolean isRegistered(LifeCycle lifeCycle)
|
||||
{
|
||||
return getInstance()._lifeCycles.contains(lifeCycle);
|
||||
return getInstance().containsLifeCycle(lifeCycle);
|
||||
}
|
||||
|
||||
private final Set<LifeCycle> _lifeCycles = new LinkedHashSet<>();
|
||||
private boolean debug;
|
||||
private final String host;
|
||||
private int port;
|
||||
private String key;
|
||||
private boolean exitVm;
|
||||
private boolean alive;
|
||||
|
||||
/**
|
||||
* Creates a ShutdownMonitor using configuration from the System properties.
|
||||
* <p>
|
||||
* <code>STOP.PORT</code> = the port to listen on (empty, null, or values less than 0 disable the stop ability)<br>
|
||||
* <code>STOP.KEY</code> = the magic key/passphrase to allow the stop (defaults to "eclipse")<br>
|
||||
* <p>
|
||||
* Note: server socket will only listen on localhost, and a successful stop will issue a System.exit() call.
|
||||
*/
|
||||
private ShutdownMonitor()
|
||||
{
|
||||
this.debug = System.getProperty("DEBUG") != null;
|
||||
this.host = System.getProperty("STOP.HOST", "127.0.0.1");
|
||||
this.port = Integer.parseInt(System.getProperty("STOP.PORT", "-1"));
|
||||
this.key = System.getProperty("STOP.KEY", null);
|
||||
this.exitVm = true;
|
||||
}
|
||||
|
||||
private void addLifeCycles(LifeCycle... lifeCycles)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
_lifeCycles.addAll(Arrays.asList(lifeCycles));
|
||||
}
|
||||
}
|
||||
|
||||
private void removeLifeCycle(LifeCycle lifeCycle)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
_lifeCycles.remove(lifeCycle);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsLifeCycle(LifeCycle lifeCycle)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
return _lifeCycles.contains(lifeCycle);
|
||||
}
|
||||
}
|
||||
|
||||
private void debug(String format, Object... args)
|
||||
{
|
||||
if (debug)
|
||||
System.err.printf("[ShutdownMonitor] " + format + "%n", args);
|
||||
}
|
||||
|
||||
private void debug(Throwable t)
|
||||
{
|
||||
if (debug)
|
||||
t.printStackTrace(System.err);
|
||||
}
|
||||
|
||||
public String getKey()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
public int getPort()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isExitVm()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
return exitVm;
|
||||
}
|
||||
}
|
||||
|
||||
public void setDebug(boolean flag)
|
||||
{
|
||||
this.debug = flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param exitVm true to exit the VM on shutdown
|
||||
*/
|
||||
public void setExitVm(boolean exitVm)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (alive)
|
||||
throw new IllegalStateException("ShutdownMonitor already started");
|
||||
this.exitVm = exitVm;
|
||||
}
|
||||
}
|
||||
|
||||
public void setKey(String key)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (alive)
|
||||
throw new IllegalStateException("ShutdownMonitor already started");
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
|
||||
public void setPort(int port)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (alive)
|
||||
throw new IllegalStateException("ShutdownMonitor already started");
|
||||
this.port = port;
|
||||
}
|
||||
}
|
||||
|
||||
protected void start() throws Exception
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (alive)
|
||||
{
|
||||
debug("Already started");
|
||||
return; // cannot start it again
|
||||
}
|
||||
ServerSocket serverSocket = listen();
|
||||
if (serverSocket != null)
|
||||
{
|
||||
alive = true;
|
||||
Thread thread = new Thread(new ShutdownMonitorRunnable(serverSocket));
|
||||
thread.setDaemon(true);
|
||||
thread.setName("ShutdownMonitor");
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void stop()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
alive = false;
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
// For test purposes only.
|
||||
void await() throws InterruptedException
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
while (alive)
|
||||
{
|
||||
wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isAlive()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
return alive;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private ServerSocket listen()
|
||||
{
|
||||
int port = getPort();
|
||||
if (port < 0)
|
||||
{
|
||||
debug("Not enabled (port < 0): %d", port);
|
||||
return null;
|
||||
}
|
||||
|
||||
String key = getKey();
|
||||
try
|
||||
{
|
||||
ServerSocket serverSocket = new ServerSocket();
|
||||
serverSocket.setReuseAddress(true);
|
||||
serverSocket.bind(new InetSocketAddress(InetAddress.getByName(host), port));
|
||||
if (port == 0)
|
||||
{
|
||||
port = serverSocket.getLocalPort();
|
||||
System.out.printf("STOP.PORT=%d%n", port);
|
||||
setPort(port);
|
||||
}
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()), 36);
|
||||
System.out.printf("STOP.KEY=%s%n", key);
|
||||
setKey(key);
|
||||
}
|
||||
|
||||
return serverSocket;
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
debug(x);
|
||||
System.err.println("Error binding ShutdownMonitor to port " + port + ": " + x.toString());
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// establish the port and key that are in use
|
||||
debug("STOP.PORT=%d", port);
|
||||
debug("STOP.KEY=%s", key);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[port=%d,alive=%b]", this.getClass().getName(), getPort(), isAlive());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* ShutdownMonitorRunnable
|
||||
*
|
||||
* Thread for listening to STOP.PORT for command to stop Jetty.
|
||||
* If ShowndownMonitor.exitVm is true, then Sytem.exit will also be
|
||||
* If ShutdownMonitor.exitVm is true, then System.exit will also be
|
||||
* called after the stop.
|
||||
*
|
||||
*/
|
||||
private class ShutdownMonitorRunnable implements Runnable
|
||||
{
|
||||
public ShutdownMonitorRunnable()
|
||||
private final ServerSocket serverSocket;
|
||||
|
||||
private ShutdownMonitorRunnable(ServerSocket serverSocket)
|
||||
{
|
||||
startListenSocket();
|
||||
this.serverSocket = serverSocket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (serverSocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (serverSocket != null)
|
||||
{
|
||||
Socket socket = null;
|
||||
debug("Started");
|
||||
try
|
||||
{
|
||||
socket = serverSocket.accept();
|
||||
|
||||
LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
|
||||
String receivedKey = lin.readLine();
|
||||
String key = getKey();
|
||||
while (true)
|
||||
{
|
||||
try (Socket socket = serverSocket.accept())
|
||||
{
|
||||
LineNumberReader reader = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
|
||||
String receivedKey = reader.readLine();
|
||||
if (!key.equals(receivedKey))
|
||||
{
|
||||
System.err.println("Ignoring command with incorrect key");
|
||||
debug("Ignoring command with incorrect key: %s", receivedKey);
|
||||
continue;
|
||||
}
|
||||
|
||||
OutputStream out = socket.getOutputStream();
|
||||
|
||||
String cmd = lin.readLine();
|
||||
String cmd = reader.readLine();
|
||||
debug("command=%s", cmd);
|
||||
OutputStream out = socket.getOutputStream();
|
||||
boolean exitVm = isExitVm();
|
||||
|
||||
if ("stop".equalsIgnoreCase(cmd)) //historic, for backward compatibility
|
||||
{
|
||||
//Stop the lifecycles, only if they are registered with the ShutdownThread, only destroying if vm is exiting
|
||||
debug("Issuing stop...");
|
||||
|
||||
for (LifeCycle l:_lifeCycles)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (l.isStarted() && ShutdownThread.isRegistered(l))
|
||||
{
|
||||
l.stop();
|
||||
}
|
||||
|
||||
if ((l instanceof Destroyable) && exitVm)
|
||||
((Destroyable)l).destroy();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
debug(e);
|
||||
}
|
||||
}
|
||||
|
||||
//Stop accepting any more commands
|
||||
stopInput(socket);
|
||||
debug("Performing stop command");
|
||||
stopLifeCycles(ShutdownThread::isRegistered, exitVm);
|
||||
|
||||
// Reply to client
|
||||
debug("Informing client that we are stopped.");
|
||||
debug("Informing client that we are stopped");
|
||||
informClient(out, "Stopped\r\n");
|
||||
|
||||
//Stop the output and close the monitor socket
|
||||
stopOutput(socket);
|
||||
if (!exitVm)
|
||||
break;
|
||||
|
||||
if (exitVm)
|
||||
{
|
||||
// Kill JVM
|
||||
debug("Killing JVM");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
else if ("forcestop".equalsIgnoreCase(cmd))
|
||||
{
|
||||
debug("Issuing force stop...");
|
||||
|
||||
//Ensure that objects are stopped, destroyed only if vm is forcibly exiting
|
||||
stopLifeCycles(exitVm);
|
||||
|
||||
//Stop accepting any more commands
|
||||
stopInput(socket);
|
||||
debug("Performing forced stop command");
|
||||
stopLifeCycles(l -> true, exitVm);
|
||||
|
||||
// Reply to client
|
||||
debug("Informing client that we are stopped.");
|
||||
debug("Informing client that we are stopped");
|
||||
informClient(out, "Stopped\r\n");
|
||||
|
||||
//Stop the output and close the monitor socket
|
||||
stopOutput(socket);
|
||||
if (!exitVm)
|
||||
break;
|
||||
|
||||
//Honour any pre-setup config to stop the jvm when this command is given
|
||||
if (exitVm)
|
||||
{
|
||||
// Kill JVM
|
||||
debug("Killing JVM");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
else if ("stopexit".equalsIgnoreCase(cmd))
|
||||
{
|
||||
debug("Issuing stop and exit...");
|
||||
//Make sure that objects registered with the shutdown thread will be stopped
|
||||
stopLifeCycles(true);
|
||||
|
||||
//Stop accepting any more input
|
||||
stopInput(socket);
|
||||
debug("Performing stop and exit commands");
|
||||
stopLifeCycles(ShutdownThread::isRegistered, true);
|
||||
|
||||
// Reply to client
|
||||
debug("Informing client that we are stopped.");
|
||||
debug("Informing client that we are stopped");
|
||||
informClient(out, "Stopped\r\n");
|
||||
|
||||
//Stop the output and close the monitor socket
|
||||
stopOutput(socket);
|
||||
|
||||
debug("Killing JVM");
|
||||
System.exit(0);
|
||||
}
|
||||
|
@ -218,312 +398,52 @@ public class ShutdownMonitor
|
|||
informClient(out, "OK\r\n");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Throwable x)
|
||||
{
|
||||
debug(e);
|
||||
System.err.println(e.toString());
|
||||
debug(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
debug(x);
|
||||
}
|
||||
finally
|
||||
{
|
||||
close(socket);
|
||||
socket = null;
|
||||
}
|
||||
stop();
|
||||
debug("Stopped");
|
||||
}
|
||||
}
|
||||
|
||||
public void stopInput (Socket socket)
|
||||
{
|
||||
//Stop accepting any more input
|
||||
close(serverSocket);
|
||||
serverSocket = null;
|
||||
//Shutdown input from client
|
||||
shutdownInput(socket);
|
||||
}
|
||||
|
||||
public void stopOutput (Socket socket) throws IOException
|
||||
{
|
||||
socket.shutdownOutput();
|
||||
close(socket);
|
||||
socket = null;
|
||||
debug("Shutting down monitor");
|
||||
serverSocket = null;
|
||||
}
|
||||
|
||||
public void informClient (OutputStream out, String message) throws IOException
|
||||
private void informClient(OutputStream out, String message) throws IOException
|
||||
{
|
||||
out.write(message.getBytes(StandardCharsets.UTF_8));
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the registered lifecycles, optionally
|
||||
* calling destroy on them.
|
||||
*
|
||||
* @param destroy true if {@link Destroyable}'s should also be destroyed.
|
||||
*/
|
||||
public void stopLifeCycles (boolean destroy)
|
||||
private void stopLifeCycles(Predicate<LifeCycle> predicate, boolean destroy)
|
||||
{
|
||||
for (LifeCycle l:_lifeCycles)
|
||||
List<LifeCycle> lifeCycles = new ArrayList<>();
|
||||
synchronized (this)
|
||||
{
|
||||
lifeCycles.addAll(_lifeCycles);
|
||||
}
|
||||
|
||||
for (LifeCycle l : lifeCycles)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (l.isStarted())
|
||||
{
|
||||
if (l.isStarted() && predicate.test(l))
|
||||
l.stop();
|
||||
}
|
||||
|
||||
if ((l instanceof Destroyable) && destroy)
|
||||
((Destroyable)l).destroy();
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Throwable x)
|
||||
{
|
||||
debug(e);
|
||||
debug(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startListenSocket()
|
||||
{
|
||||
if (port < 0)
|
||||
{
|
||||
if (DEBUG)
|
||||
System.err.println("ShutdownMonitor not in use (port < 0): " + port);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
serverSocket = new ServerSocket();
|
||||
serverSocket.setReuseAddress(true);
|
||||
serverSocket.bind(new InetSocketAddress(InetAddress.getByName(host), port), 1);
|
||||
if (port == 0)
|
||||
{
|
||||
// server assigned port in use
|
||||
port = serverSocket.getLocalPort();
|
||||
System.out.printf("STOP.PORT=%d%n",port);
|
||||
}
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
// create random key
|
||||
key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36);
|
||||
System.out.printf("STOP.KEY=%s%n",key);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
debug(e);
|
||||
System.err.println("Error binding monitor port " + port + ": " + e.toString());
|
||||
serverSocket = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// establish the port and key that are in use
|
||||
debug("STOP.PORT=%d",port);
|
||||
debug("STOP.KEY=%s",key);
|
||||
debug("%s",serverSocket);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean DEBUG;
|
||||
private String host;
|
||||
private int port;
|
||||
private String key;
|
||||
private boolean exitVm;
|
||||
private ServerSocket serverSocket;
|
||||
private Thread thread;
|
||||
|
||||
/**
|
||||
* Create a ShutdownMonitor using configuration from the System properties.
|
||||
* <p>
|
||||
* <code>STOP.PORT</code> = the port to listen on (empty, null, or values less than 0 disable the stop ability)<br>
|
||||
* <code>STOP.KEY</code> = the magic key/passphrase to allow the stop (defaults to "eclipse")<br>
|
||||
* <p>
|
||||
* Note: server socket will only listen on localhost, and a successful stop will issue a System.exit() call.
|
||||
*/
|
||||
private ShutdownMonitor()
|
||||
{
|
||||
this.DEBUG = System.getProperty("DEBUG") != null;
|
||||
|
||||
// Use values passed thru via /jetty-start/
|
||||
this.host = System.getProperty("STOP.HOST","127.0.0.1");
|
||||
this.port = Integer.parseInt(System.getProperty("STOP.PORT","-1"));
|
||||
this.key = System.getProperty("STOP.KEY",null);
|
||||
this.exitVm = true;
|
||||
}
|
||||
|
||||
private void close(ServerSocket server)
|
||||
{
|
||||
if (server == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
server.close();
|
||||
}
|
||||
catch (IOException ignore)
|
||||
{
|
||||
debug(ignore);
|
||||
}
|
||||
}
|
||||
|
||||
private void close(Socket socket)
|
||||
{
|
||||
if (socket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException ignore)
|
||||
{
|
||||
debug(ignore);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void shutdownInput(Socket socket)
|
||||
{
|
||||
if (socket == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
socket.shutdownInput();
|
||||
}
|
||||
catch (IOException ignore)
|
||||
{
|
||||
debug(ignore);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void debug(String format, Object... args)
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
System.err.printf("[ShutdownMonitor] " + format + "%n",args);
|
||||
}
|
||||
}
|
||||
|
||||
private void debug(Throwable t)
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
t.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
public String getKey()
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
public int getPort()
|
||||
{
|
||||
return port;
|
||||
}
|
||||
|
||||
public ServerSocket getServerSocket()
|
||||
{
|
||||
return serverSocket;
|
||||
}
|
||||
|
||||
public boolean isExitVm()
|
||||
{
|
||||
return exitVm;
|
||||
}
|
||||
|
||||
|
||||
public void setDebug(boolean flag)
|
||||
{
|
||||
this.DEBUG = flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param exitVm true to exit the VM on shutdown
|
||||
*/
|
||||
public void setExitVm(boolean exitVm)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (thread != null && thread.isAlive())
|
||||
{
|
||||
throw new IllegalStateException("ShutdownMonitorThread already started");
|
||||
}
|
||||
this.exitVm = exitVm;
|
||||
}
|
||||
}
|
||||
|
||||
public void setKey(String key)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (thread != null && thread.isAlive())
|
||||
{
|
||||
throw new IllegalStateException("ShutdownMonitorThread already started");
|
||||
}
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
|
||||
public void setPort(int port)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (thread != null && thread.isAlive())
|
||||
{
|
||||
throw new IllegalStateException("ShutdownMonitorThread already started");
|
||||
}
|
||||
this.port = port;
|
||||
}
|
||||
}
|
||||
|
||||
protected void start() throws Exception
|
||||
{
|
||||
Thread t = null;
|
||||
|
||||
synchronized (this)
|
||||
{
|
||||
if (thread != null && thread.isAlive())
|
||||
{
|
||||
if (DEBUG)
|
||||
System.err.printf("ShutdownMonitorThread already started");
|
||||
return; // cannot start it again
|
||||
}
|
||||
|
||||
thread = new Thread(new ShutdownMonitorRunnable());
|
||||
thread.setDaemon(true);
|
||||
thread.setName("ShutdownMonitor");
|
||||
t = thread;
|
||||
}
|
||||
|
||||
if (t != null)
|
||||
t.start();
|
||||
}
|
||||
|
||||
|
||||
protected boolean isAlive ()
|
||||
{
|
||||
boolean result = false;
|
||||
synchronized (this)
|
||||
{
|
||||
result = (thread != null && thread.isAlive());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[port=%d]",this.getClass().getName(),port);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.server.handler.gzip;
|
||||
|
||||
import static org.eclipse.jetty.http.GzipHttpContent.ETAG_GZIP_QUOTE;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
@ -47,7 +45,6 @@ import org.eclipse.jetty.util.URIUtil;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* A Handler that can dynamically GZIP compress responses. Unlike
|
||||
* previous and 3rd party GzipFilters, this mechanism works with asynchronously
|
||||
|
@ -71,7 +68,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
|||
private boolean _syncFlush = false;
|
||||
|
||||
// non-static, as other GzipHandler instances may have different configurations
|
||||
private final ThreadLocal<Deflater> _deflater = new ThreadLocal<Deflater>();
|
||||
private final ThreadLocal<Deflater> _deflater = new ThreadLocal<>();
|
||||
|
||||
private final IncludeExclude<String> _agentPatterns=new IncludeExclude<>(RegexSet.class);
|
||||
private final IncludeExclude<String> _methods = new IncludeExclude<>();
|
||||
|
@ -365,6 +362,11 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
|||
return _minGzipSize;
|
||||
}
|
||||
|
||||
protected HttpField getVaryField()
|
||||
{
|
||||
return _vary;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
|
@ -441,20 +443,21 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
|||
String etag = baseRequest.getHttpFields().get(HttpHeader.IF_NONE_MATCH);
|
||||
if (etag!=null)
|
||||
{
|
||||
int i=etag.indexOf(ETAG_GZIP_QUOTE);
|
||||
int i=etag.indexOf(GzipHttpContent.ETAG_GZIP_QUOTE);
|
||||
if (i>0)
|
||||
{
|
||||
while (i>=0)
|
||||
{
|
||||
etag=etag.substring(0,i)+etag.substring(i+GzipHttpContent.ETAG_GZIP.length());
|
||||
i=etag.indexOf(ETAG_GZIP_QUOTE,i);
|
||||
i=etag.indexOf(GzipHttpContent.ETAG_GZIP_QUOTE,i);
|
||||
}
|
||||
baseRequest.getHttpFields().put(new HttpField(HttpHeader.IF_NONE_MATCH,etag));
|
||||
}
|
||||
}
|
||||
|
||||
// install interceptor and handle
|
||||
out.setInterceptor(new GzipHttpOutputInterceptor(this,_vary,baseRequest.getHttpChannel(),out.getInterceptor(),_syncFlush));
|
||||
out.setInterceptor(new GzipHttpOutputInterceptor(this,getVaryField(),baseRequest.getHttpChannel(),out.getInterceptor(),isSyncFlush()));
|
||||
|
||||
if (_handler!=null)
|
||||
_handler.handle(target,baseRequest, request, response);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.eclipse.jetty.http.MimeTypes;
|
|||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.HttpOutput;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IteratingNestedCallback;
|
||||
|
@ -150,7 +151,8 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
|||
protected void commit(ByteBuffer content, boolean complete, Callback callback)
|
||||
{
|
||||
// Are we excluding because of status?
|
||||
int sc = _channel.getResponse().getStatus();
|
||||
Response response = _channel.getResponse();
|
||||
int sc = response.getStatus();
|
||||
if (sc>0 && (sc<200 || sc==204 || sc==205 || sc>=300))
|
||||
{
|
||||
LOG.debug("{} exclude by status {}",this,sc);
|
||||
|
@ -160,7 +162,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
|||
}
|
||||
|
||||
// Are we excluding because of mime-type?
|
||||
String ct = _channel.getResponse().getContentType();
|
||||
String ct = response.getContentType();
|
||||
if (ct!=null)
|
||||
{
|
||||
ct=MimeTypes.getContentTypeWithoutCharset(ct);
|
||||
|
@ -174,7 +176,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
|||
}
|
||||
|
||||
// Has the Content-Encoding header already been set?
|
||||
String ce=_channel.getResponse().getHeader("Content-Encoding");
|
||||
String ce=response.getHeader("Content-Encoding");
|
||||
if (ce != null)
|
||||
{
|
||||
LOG.debug("{} exclude by content-encoding {}",this,ce);
|
||||
|
@ -187,10 +189,11 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
|||
if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.COMMITTING))
|
||||
{
|
||||
// We are varying the response due to accept encoding header.
|
||||
HttpFields fields = _channel.getResponse().getHttpFields();
|
||||
HttpFields fields = response.getHttpFields();
|
||||
if (_vary != null)
|
||||
fields.add(_vary);
|
||||
|
||||
long content_length = _channel.getResponse().getContentLength();
|
||||
long content_length = response.getContentLength();
|
||||
if (content_length<0 && complete)
|
||||
content_length=content.remaining();
|
||||
|
||||
|
@ -210,7 +213,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
|||
BufferUtil.fill(_buffer,GZIP_HEADER,0,GZIP_HEADER.length);
|
||||
|
||||
// Adjust headers
|
||||
_channel.getResponse().setContentLength(-1);
|
||||
response.setContentLength(-1);
|
||||
String etag=fields.get(HttpHeader.ETAG);
|
||||
if (etag!=null)
|
||||
{
|
||||
|
|
|
@ -18,89 +18,59 @@
|
|||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.util.thread.ShutdownThread;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* ShutdownMonitorTest
|
||||
*/
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ShutdownMonitorTest
|
||||
{
|
||||
public class TestableServer extends Server
|
||||
{
|
||||
boolean destroyed = false;
|
||||
boolean stopped = false;
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
stopped = true;
|
||||
super.doStop();
|
||||
}
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
destroyed = true;
|
||||
super.destroy();
|
||||
}
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
stopped = false;
|
||||
destroyed = false;
|
||||
super.doStart();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testShutdownMonitor() throws Exception
|
||||
{
|
||||
// test port and key assignment
|
||||
ShutdownMonitor.getInstance().setPort(0);
|
||||
ShutdownMonitor.getInstance().setExitVm(false);
|
||||
ShutdownMonitor.getInstance().start();
|
||||
String key = ShutdownMonitor.getInstance().getKey();
|
||||
int port = ShutdownMonitor.getInstance().getPort();
|
||||
ShutdownMonitor monitor = ShutdownMonitor.getInstance();
|
||||
monitor.setDebug(true);
|
||||
monitor.setPort(0);
|
||||
monitor.setExitVm(false);
|
||||
monitor.start();
|
||||
String key = monitor.getKey();
|
||||
int port = monitor.getPort();
|
||||
|
||||
// try starting a 2nd time (should be ignored)
|
||||
ShutdownMonitor.getInstance().start();
|
||||
monitor.start();
|
||||
|
||||
stop("stop", port, key, true);
|
||||
assertTrue(!ShutdownMonitor.getInstance().isAlive());
|
||||
monitor.await();
|
||||
assertTrue(!monitor.isAlive());
|
||||
|
||||
// should be able to change port and key because it is stopped
|
||||
ShutdownMonitor.getInstance().setPort(0);
|
||||
ShutdownMonitor.getInstance().setKey("foo");
|
||||
ShutdownMonitor.getInstance().start();
|
||||
monitor.setPort(0);
|
||||
String newKey = "foo";
|
||||
monitor.setKey(newKey);
|
||||
monitor.start();
|
||||
|
||||
key = ShutdownMonitor.getInstance().getKey();
|
||||
port = ShutdownMonitor.getInstance().getPort();
|
||||
assertTrue(ShutdownMonitor.getInstance().isAlive());
|
||||
key = monitor.getKey();
|
||||
assertEquals(newKey, key);
|
||||
port = monitor.getPort();
|
||||
assertTrue(monitor.isAlive());
|
||||
|
||||
stop("stop", port, key, true);
|
||||
assertTrue(!ShutdownMonitor.getInstance().isAlive());
|
||||
monitor.await();
|
||||
assertTrue(!monitor.isAlive());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testForceStopCommand() throws Exception
|
||||
{
|
||||
//create a testable Server with stop(), destroy() overridden to instrument
|
||||
//start server
|
||||
//call "forcestop" and check that server stopped but not destroyed
|
||||
// test port and key assignment
|
||||
System.setProperty("DEBUG", "true");
|
||||
ShutdownMonitor.getInstance().setPort(0);
|
||||
ShutdownMonitor monitor = ShutdownMonitor.getInstance();
|
||||
monitor.setPort(0);
|
||||
TestableServer server = new TestableServer();
|
||||
server.start();
|
||||
|
||||
|
@ -108,12 +78,13 @@ public class ShutdownMonitorTest
|
|||
assertTrue(!ShutdownThread.isRegistered(server));
|
||||
assertTrue(ShutdownMonitor.isRegistered(server));
|
||||
|
||||
String key = ShutdownMonitor.getInstance().getKey();
|
||||
int port = ShutdownMonitor.getInstance().getPort();
|
||||
String key = monitor.getKey();
|
||||
int port = monitor.getPort();
|
||||
|
||||
stop("forcestop", port, key, true);
|
||||
monitor.await();
|
||||
|
||||
assertTrue(!ShutdownMonitor.getInstance().isAlive());
|
||||
assertTrue(!monitor.isAlive());
|
||||
assertTrue(server.stopped);
|
||||
assertTrue(!server.destroyed);
|
||||
assertTrue(!ShutdownThread.isRegistered(server));
|
||||
|
@ -123,21 +94,10 @@ public class ShutdownMonitorTest
|
|||
@Test
|
||||
public void testOldStopCommandWithStopOnShutdownTrue() throws Exception
|
||||
{
|
||||
ShutdownMonitor monitor = ShutdownMonitor.getInstance();
|
||||
monitor.setExitVm(false);
|
||||
|
||||
//create a testable Server with stop(), destroy() overridden to instrument
|
||||
//call server.setStopAtShudown(true);
|
||||
//start server
|
||||
//call "stop" and check that server stopped but not destroyed
|
||||
|
||||
//stop server
|
||||
|
||||
//call server.setStopAtShutdown(false);
|
||||
//start server
|
||||
//call "stop" and check that the server is not stopped and not destroyed
|
||||
System.setProperty("DEBUG", "true");
|
||||
ShutdownMonitor.getInstance().setExitVm(false);
|
||||
|
||||
ShutdownMonitor.getInstance().setPort(0);
|
||||
monitor.setPort(0);
|
||||
TestableServer server = new TestableServer();
|
||||
server.setStopAtShutdown(true);
|
||||
server.start();
|
||||
|
@ -146,11 +106,13 @@ public class ShutdownMonitorTest
|
|||
assertTrue(ShutdownThread.isRegistered(server));
|
||||
assertTrue(ShutdownMonitor.isRegistered(server));
|
||||
|
||||
String key = ShutdownMonitor.getInstance().getKey();
|
||||
int port = ShutdownMonitor.getInstance().getPort();
|
||||
String key = monitor.getKey();
|
||||
int port = monitor.getPort();
|
||||
|
||||
stop("stop", port, key, true);
|
||||
assertTrue(!ShutdownMonitor.getInstance().isAlive());
|
||||
monitor.await();
|
||||
|
||||
assertTrue(!monitor.isAlive());
|
||||
assertTrue(server.stopped);
|
||||
assertTrue(!server.destroyed);
|
||||
assertTrue(!ShutdownThread.isRegistered(server));
|
||||
|
@ -160,10 +122,9 @@ public class ShutdownMonitorTest
|
|||
@Test
|
||||
public void testOldStopCommandWithStopOnShutdownFalse() throws Exception
|
||||
{
|
||||
//change so stopatshutdown is false, so stop does nothing in this case (as exitVm is false otherwise we couldn't run test)
|
||||
ShutdownMonitor.getInstance().setExitVm(false);
|
||||
System.setProperty("DEBUG", "true");
|
||||
ShutdownMonitor.getInstance().setPort(0);
|
||||
ShutdownMonitor monitor = ShutdownMonitor.getInstance();
|
||||
monitor.setExitVm(false);
|
||||
monitor.setPort(0);
|
||||
TestableServer server = new TestableServer();
|
||||
server.setStopAtShutdown(false);
|
||||
server.start();
|
||||
|
@ -171,20 +132,19 @@ public class ShutdownMonitorTest
|
|||
assertTrue(!ShutdownThread.isRegistered(server));
|
||||
assertTrue(ShutdownMonitor.isRegistered(server));
|
||||
|
||||
String key = ShutdownMonitor.getInstance().getKey();
|
||||
int port = ShutdownMonitor.getInstance().getPort();
|
||||
String key = monitor.getKey();
|
||||
int port = monitor.getPort();
|
||||
|
||||
stop("stop", port, key, true);
|
||||
assertTrue(!ShutdownMonitor.getInstance().isAlive());
|
||||
monitor.await();
|
||||
|
||||
assertTrue(!monitor.isAlive());
|
||||
assertTrue(!server.stopped);
|
||||
assertTrue(!server.destroyed);
|
||||
assertTrue(!ShutdownThread.isRegistered(server));
|
||||
assertTrue(ShutdownMonitor.isRegistered(server));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void stop(String command, int port, String key, boolean check) throws Exception
|
||||
{
|
||||
System.out.printf("Attempting to send " + command + " to localhost:%d (%b)%n", port, check);
|
||||
|
@ -198,16 +158,11 @@ public class ShutdownMonitorTest
|
|||
|
||||
if (check)
|
||||
{
|
||||
// wait a little
|
||||
TimeUnit.MILLISECONDS.sleep(600);
|
||||
|
||||
// check for stop confirmation
|
||||
LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));
|
||||
String response;
|
||||
if ((response = lin.readLine()) != null)
|
||||
{
|
||||
assertEquals("Stopped", response);
|
||||
}
|
||||
else
|
||||
throw new IllegalStateException("No stop confirmation");
|
||||
}
|
||||
|
@ -215,4 +170,31 @@ public class ShutdownMonitorTest
|
|||
}
|
||||
}
|
||||
|
||||
public class TestableServer extends Server
|
||||
{
|
||||
boolean destroyed = false;
|
||||
boolean stopped = false;
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
stopped = true;
|
||||
super.doStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
destroyed = true;
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
stopped = false;
|
||||
destroyed = false;
|
||||
super.doStart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue