diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java index 39d38e71af1..70347e036c5 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java @@ -40,7 +40,7 @@ import org.eclipse.jetty.util.thread.ShutdownThread; *
* 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.
static class Holder
@@ -53,11 +53,164 @@ public class ShutdownMonitor extends Thread
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 int port;
private String key;
private boolean exitVm;
private ServerSocket serverSocket;
+ private ShutdownMonitorThread thread;
+
+
/**
* Create a ShutdownMonitor using configuration from the System properties.
@@ -149,77 +302,6 @@ public class ShutdownMonitor extends Thread
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)
{
@@ -228,90 +310,71 @@ public class ShutdownMonitor extends Thread
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)
{
- 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)
{
- if (isAlive())
+ synchronized (this)
{
- throw new IllegalStateException("ShutdownMonitor already started");
- }
- this.port = port;
- }
-
- public void start()
- {
- if (isAlive())
- {
- System.err.printf("ShutdownMonitor already started");
- return; // cannot start it again
- }
- startListenSocket();
- if (serverSocket == null)
- {
- return;
- }
-
- super.start();
- }
-
- private void startListenSocket()
- {
- if (this.port < 0)
- {
- if (DEBUG)
- System.err.println("ShutdownMonitor not in use (port < 0): " + port);
- return;
- }
-
- try
- {
- setDaemon(true);
- setName("ShutdownMonitor");
-
- this.serverSocket = new ServerSocket(this.port,1,InetAddress.getByName("127.0.0.1"));
- if (this.port == 0)
+ if (thread != null && thread.isAlive())
{
- // server assigned port in use
- this.port = serverSocket.getLocalPort();
- System.out.printf("STOP.PORT=%d%n",this.port);
+ throw new IllegalStateException("ShutdownMonitorThread already started");
}
-
- 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);
+ this.port = port;
}
}
+ protected void start() throws Exception
+ {
+ ShutdownMonitorThread t = null;
+ synchronized (this)
+ {
+ if (thread != null && thread.isAlive())
+ {
+ System.err.printf("ShutdownMonitorThread already started");
+ return; // cannot start it again
+ }
+
+ thread = new ShutdownMonitorThread();
+ 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()
{
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java
new file mode 100644
index 00000000000..596f9ccc2bb
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java
@@ -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();
+ }
+ }
+
+}
diff --git a/jetty-spdy/pom.xml b/jetty-spdy/pom.xml
index 5d399eaa686..9ebd57081d5 100644
--- a/jetty-spdy/pom.xml
+++ b/jetty-spdy/pom.xml
@@ -13,7 +13,8 @@