Merge remote-tracking branch 'origin/jetty-7' into jetty-8

This commit is contained in:
Jan Bartel 2013-03-18 15:27:10 +11:00
commit b69b52d984
5 changed files with 317 additions and 141 deletions

View File

@ -40,7 +40,7 @@ import org.eclipse.jetty.util.thread.ShutdownThread;
* <p> * <p>
* Commands "stop" and "status" are currently supported. * Commands "stop" and "status" are currently supported.
*/ */
public class ShutdownMonitor extends Thread public class ShutdownMonitor
{ {
// Implementation of safe lazy init, using Initialization on Demand Holder technique. // Implementation of safe lazy init, using Initialization on Demand Holder technique.
static class Holder static class Holder
@ -53,11 +53,164 @@ public class ShutdownMonitor extends Thread
return Holder.instance; return Holder.instance;
} }
/**
* ShutdownMonitorThread
*
* Thread for listening to STOP.PORT for command to stop Jetty.
* If ShowndownMonitor.exitVm is true, then Sytem.exit will also be
* called after the stop.
*
*/
public class ShutdownMonitorThread extends Thread
{
public ShutdownMonitorThread ()
{
setDaemon(true);
setName("ShutdownMonitor");
}
@Override
public void run()
{
if (serverSocket == null)
{
return;
}
while (serverSocket != null)
{
Socket socket = null;
try
{
socket = serverSocket.accept();
LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
String receivedKey = lin.readLine();
if (!key.equals(receivedKey))
{
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);
socket = null;
close(serverSocket);
serverSocket = null;
if (exitVm)
{
// 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;
}
}
}
public void start()
{
if (isAlive())
{
System.err.printf("ShutdownMonitorThread already started");
return; // cannot start it again
}
startListenSocket();
if (serverSocket == null)
{
return;
}
System.err.println("Starting ShutdownMonitorThread");
super.start();
}
private void startListenSocket()
{
if (port < 0)
{
if (DEBUG)
System.err.println("ShutdownMonitor not in use (port < 0): " + port);
return;
}
try
{
serverSocket = new ServerSocket(port,1,InetAddress.getByName("127.0.0.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 boolean DEBUG;
private int port; private int port;
private String key; private String key;
private boolean exitVm; private boolean exitVm;
private ServerSocket serverSocket; private ServerSocket serverSocket;
private ShutdownMonitorThread thread;
/** /**
* Create a ShutdownMonitor using configuration from the System properties. * Create a ShutdownMonitor using configuration from the System properties.
@ -149,77 +302,6 @@ public class ShutdownMonitor extends Thread
return exitVm; return exitVm;
} }
@Override
public void run()
{
if (serverSocket == null)
{
return;
}
while (serverSocket != null)
{
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);
socket = null;
close(serverSocket);
serverSocket = null;
if (exitVm)
{
// 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;
}
}
}
public void setDebug(boolean flag) public void setDebug(boolean flag)
{ {
@ -228,89 +310,70 @@ public class ShutdownMonitor extends Thread
public void setExitVm(boolean exitVm) public void setExitVm(boolean exitVm)
{ {
if (isAlive()) synchronized (this)
{ {
throw new IllegalStateException("ShutdownMonitor already started"); if (thread != null && thread.isAlive())
{
throw new IllegalStateException("ShutdownMonitorThread already started");
} }
this.exitVm = exitVm; this.exitVm = exitVm;
} }
}
public void setKey(String key) public void setKey(String key)
{ {
if (isAlive()) synchronized (this)
{ {
throw new IllegalStateException("ShutdownMonitor already started"); if (thread != null && thread.isAlive())
{
throw new IllegalStateException("ShutdownMonitorThread already started");
} }
this.key = key; this.key = key;
} }
}
public void setPort(int port) public void setPort(int port)
{ {
if (isAlive()) synchronized (this)
{ {
throw new IllegalStateException("ShutdownMonitor already started"); if (thread != null && thread.isAlive())
{
throw new IllegalStateException("ShutdownMonitorThread already started");
} }
this.port = port; this.port = port;
} }
}
public void start() protected void start() throws Exception
{ {
if (isAlive()) ShutdownMonitorThread t = null;
synchronized (this)
{ {
System.err.printf("ShutdownMonitor already started"); if (thread != null && thread.isAlive())
{
System.err.printf("ShutdownMonitorThread already started");
return; // cannot start it again return; // cannot start it again
} }
startListenSocket();
if (serverSocket == null) thread = new ShutdownMonitorThread();
{ t = thread;
return;
} }
super.start(); if (t != null)
t.start();
} }
private void startListenSocket()
protected boolean isAlive ()
{ {
if (this.port < 0) boolean result = false;
synchronized (this)
{ {
if (DEBUG) result = (thread != null && thread.isAlive());
System.err.println("ShutdownMonitor not in use (port < 0): " + port); }
return; return result;
} }
try
{
setDaemon(true);
setName("ShutdownMonitor");
this.serverSocket = new ServerSocket(this.port,1,InetAddress.getByName("127.0.0.1"));
if (this.port == 0)
{
// server assigned port in use
this.port = serverSocket.getLocalPort();
System.out.printf("STOP.PORT=%d%n",this.port);
}
if (this.key == null)
{
// create random key
this.key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36);
System.out.printf("STOP.KEY=%s%n",this.key);
}
}
catch (Exception e)
{
debug(e);
System.err.println("Error binding monitor port " + this.port + ": " + e.toString());
}
finally
{
// establish the port and key that are in use
debug("STOP.PORT=%d",this.port);
debug("STOP.KEY=%s",this.key);
debug("%s",serverSocket);
}
}
@Override @Override
public String toString() public String toString()

View File

@ -0,0 +1,112 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
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 org.junit.Test;
/**
* ShutdownMonitorTest
*
*
*
*/
public class ShutdownMonitorTest
{
@Test
public void testShutdown ()
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();
//try starting a 2nd time (should be ignored)
ShutdownMonitor.getInstance().start();
stop(port,key,true);
assertTrue(!ShutdownMonitor.getInstance().isAlive());
//should be able to change port and key because it is stopped
ShutdownMonitor.getInstance().setPort(0);
ShutdownMonitor.getInstance().setKey("foo");
ShutdownMonitor.getInstance().start();
key = ShutdownMonitor.getInstance().getKey();
port = ShutdownMonitor.getInstance().getPort();
assertTrue(ShutdownMonitor.getInstance().isAlive());
stop(port,key,true);
assertTrue(!ShutdownMonitor.getInstance().isAlive());
}
public void stop (int port, String key, boolean check)
throws Exception
{
Socket s = null;
try
{
//send stop command
s = new Socket(InetAddress.getByName("127.0.0.1"),port);
OutputStream out = s.getOutputStream();
out.write((key + "\r\nstop\r\n").getBytes());
out.flush();
if (check)
{
//wait a little
Thread.currentThread().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");
}
}
finally
{
if (s != null) s.close();
}
}
}

View File

@ -13,7 +13,8 @@
<name>Jetty :: SPDY :: Parent</name> <name>Jetty :: SPDY :: Parent</name>
<properties> <properties>
<npn.version>1.1.0.v20120525</npn.version> <npn.version>1.1.5.v20130313</npn.version>
<npn.api.version>1.1.0.v20120525</npn.api.version>
</properties> </properties>
<modules> <modules>

View File

@ -57,7 +57,7 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty.npn</groupId> <groupId>org.eclipse.jetty.npn</groupId>
<artifactId>npn-api</artifactId> <artifactId>npn-api</artifactId>
<version>1.0.0.v20120402</version> <version>${npn.api.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -58,7 +58,7 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty.npn</groupId> <groupId>org.eclipse.jetty.npn</groupId>
<artifactId>npn-api</artifactId> <artifactId>npn-api</artifactId>
<version>${npn.version}</version> <version>${npn.api.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>