Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Bartel2014-08-01 02:43:04 +0000
committerJan Bartel2014-08-01 02:44:30 +0000
commit75c92bf76ae8e9be26a341fac1c01201ec706973 (patch)
tree1f523cb0cef181b38a9116484777b09b7c42fe0f /jetty-server
parentf970ffc0acbdd536280367b052e5955c20b1057c (diff)
downloadorg.eclipse.jetty.project-75c92bf76ae8e9be26a341fac1c01201ec706973.tar.gz
org.eclipse.jetty.project-75c92bf76ae8e9be26a341fac1c01201ec706973.tar.xz
org.eclipse.jetty.project-75c92bf76ae8e9be26a341fac1c01201ec706973.zip
438500 Odd NoClassDef errors when shutting down the jetty-maven-plugin via the stop goal
Diffstat (limited to 'jetty-server')
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/Server.java19
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java180
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java133
3 files changed, 301 insertions, 31 deletions
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
index ce4bdd9097..38cea332fb 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
@@ -18,7 +18,6 @@
package org.eclipse.jetty.server;
-import java.awt.geom.PathIterator;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
@@ -54,7 +53,6 @@ import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.URIUtil;
-import org.eclipse.jetty.util.UrlEncoded;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.Name;
@@ -145,7 +143,8 @@ public class Server extends HandlerWrapper implements Attributes
{
return _stopAtShutdown;
}
-
+
+
/* ------------------------------------------------------------ */
/**
* Set a graceful stop time.
@@ -313,11 +312,16 @@ public class Server extends HandlerWrapper implements Attributes
@Override
protected void doStart() throws Exception
{
+ //If the Server should be stopped when the jvm exits, register
+ //with the shutdown handler thread.
if (getStopAtShutdown())
- {
ShutdownThread.register(this);
- }
+ //Register the Server with the handler thread for receiving
+ //remote stop commands
+ ShutdownMonitor.register(this);
+
+ //Start a thread waiting to receive "stop" commands.
ShutdownMonitor.getInstance().start(); // initialize
LOG.info("jetty-" + getVersion());
@@ -455,6 +459,11 @@ public class Server extends HandlerWrapper implements Attributes
if (getStopAtShutdown())
ShutdownThread.deregister(this);
+
+ //Unregister the Server with the handler thread for receiving
+ //remote stop commands as we are stopped already
+ ShutdownMonitor.deregister(this);
+
mex.ifExceptionThrow();
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 38b866574d..861c019888 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
@@ -26,8 +26,15 @@ import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+import org.eclipse.jetty.util.component.Destroyable;
+import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.thread.ShutdownThread;
/**
@@ -42,6 +49,8 @@ import org.eclipse.jetty.util.thread.ShutdownThread;
*/
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
{
@@ -52,6 +61,26 @@ public class ShutdownMonitor
{
return Holder.instance;
}
+
+ /* ------------------------------------------------------------ */
+ public static synchronized void register(LifeCycle... lifeCycles)
+ {
+ getInstance()._lifeCycles.addAll(Arrays.asList(lifeCycles));
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public static synchronized void deregister(LifeCycle lifeCycle)
+ {
+ getInstance()._lifeCycles.remove(lifeCycle);
+ }
+
+ /* ------------------------------------------------------------ */
+ public static synchronized boolean isRegistered(LifeCycle lifeCycle)
+ {
+ return getInstance()._lifeCycles.contains(lifeCycle);
+ }
+
/**
* ShutdownMonitorRunnable
@@ -95,30 +124,64 @@ public class ShutdownMonitor
String cmd = lin.readLine();
debug("command=%s",cmd);
- if ("stop".equals(cmd))
+ if ("stop".equalsIgnoreCase(cmd)) //historic, for backward compatibility
{
- // Graceful Shutdown
- debug("Issuing graceful shutdown..");
- ShutdownThread.getInstance().run();
-
- //Stop accepting any more
- close(serverSocket);
- serverSocket = null;
+ //Stop the lifecycles, only if they are registered with the ShutdownThread, only destroying if vm is exiting
+ debug("Issuing stop...");
- //Shutdown input from client
- shutdownInput(socket);
+ 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);
// Reply to client
debug("Informing client that we are stopped.");
- out.write("Stopped\r\n".getBytes(StandardCharsets.UTF_8));
- out.flush();
+ informClient(out, "Stopped\r\n");
+
+ //Stop the output and close the monitor socket
+ stopOutput(socket);
- // Shutdown Monitor
- socket.shutdownOutput();
- close(socket);
- socket = null;
- debug("Shutting down monitor");
+ 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);
+
+ // Reply to client
+ debug("Informing client that we are stopped.");
+ informClient(out, "Stopped\r\n");
+ //Stop the output and close the monitor socket
+ stopOutput(socket);
+
+ //Honour any pre-setup config to stop the jvm when this command is given
if (exitVm)
{
// Kill JVM
@@ -126,11 +189,34 @@ public class ShutdownMonitor
System.exit(0);
}
}
- else if ("status".equals(cmd))
+ 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);
+
+ // Reply to client
+ 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);
+ }
+ else if ("exit".equalsIgnoreCase(cmd))
+ {
+ debug("Killing JVM");
+ System.exit(0);
+ }
+ else if ("status".equalsIgnoreCase(cmd))
{
// Reply to client
- out.write("OK\r\n".getBytes(StandardCharsets.UTF_8));
- out.flush();
+ informClient(out, "OK\r\n");
}
}
catch (Exception e)
@@ -146,6 +232,57 @@ public class ShutdownMonitor
}
}
+ 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
+ {
+ out.write(message.getBytes(StandardCharsets.UTF_8));
+ out.flush();
+ }
+
+ /**
+ * Stop the registered lifecycles, optionally
+ * calling destroy on them.
+ *
+ * @param destroy
+ */
+ public void stopLifeCycles (boolean destroy)
+ {
+ for (LifeCycle l:_lifeCycles)
+ {
+ try
+ {
+ if (l.isStarted())
+ {
+ l.stop();
+ }
+
+ if ((l instanceof Destroyable) && destroy)
+ ((Destroyable)l).destroy();
+ }
+ catch (Exception e)
+ {
+ debug(e);
+ }
+ }
+ }
+
public void startListenSocket()
{
if (port < 0)
@@ -309,6 +446,9 @@ public class ShutdownMonitor
this.DEBUG = flag;
}
+ /**
+ * @param exitVm
+ */
public void setExitVm(boolean exitVm)
{
synchronized (this)
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
index 0b02ee1b8a..0f523967bb 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java
@@ -28,6 +28,7 @@ import java.net.InetAddress;
import java.net.Socket;
import java.util.concurrent.TimeUnit;
+import org.eclipse.jetty.util.thread.ShutdownThread;
import org.junit.Test;
/**
@@ -35,8 +36,34 @@ import org.junit.Test;
*/
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 testShutdown() throws Exception
+ public void testShutdownMonitor() throws Exception
{
// test port and key assignment
ShutdownMonitor.getInstance().setPort(0);
@@ -48,7 +75,7 @@ public class ShutdownMonitorTest
// try starting a 2nd time (should be ignored)
ShutdownMonitor.getInstance().start();
- stop(port,key,true);
+ stop("stop", port,key,true);
assertTrue(!ShutdownMonitor.getInstance().isAlive());
// should be able to change port and key because it is stopped
@@ -60,19 +87,113 @@ public class ShutdownMonitorTest
port = ShutdownMonitor.getInstance().getPort();
assertTrue(ShutdownMonitor.getInstance().isAlive());
- stop(port,key,true);
+ stop("stop", port,key,true);
+ assertTrue(!ShutdownMonitor.getInstance().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);
+ TestableServer server = new TestableServer();
+ server.start();
+
+ //shouldn't be registered for shutdown on jvm
+ assertTrue(!ShutdownThread.isRegistered(server));
+ assertTrue(ShutdownMonitor.isRegistered(server));
+
+ String key = ShutdownMonitor.getInstance().getKey();
+ int port = ShutdownMonitor.getInstance().getPort();
+
+ stop("forcestop", port,key,true);
+
+ assertTrue(!ShutdownMonitor.getInstance().isAlive());
+ assertTrue(server.stopped);
+ assertTrue(!server.destroyed);
+ assertTrue(!ShutdownThread.isRegistered(server));
+ assertTrue(!ShutdownMonitor.isRegistered(server));
+ }
+
+ @Test
+ public void testOldStopCommandWithStopOnShutdownTrue() throws Exception
+ {
+
+ //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);
+ TestableServer server = new TestableServer();
+ server.setStopAtShutdown(true);
+ server.start();
+
+ //should be registered for shutdown on exit
+ assertTrue(ShutdownThread.isRegistered(server));
+ assertTrue(ShutdownMonitor.isRegistered(server));
+
+ String key = ShutdownMonitor.getInstance().getKey();
+ int port = ShutdownMonitor.getInstance().getPort();
+
+ stop("stop", port, key, true);
+ assertTrue(!ShutdownMonitor.getInstance().isAlive());
+ assertTrue(server.stopped);
+ assertTrue(!server.destroyed);
+ assertTrue(!ShutdownThread.isRegistered(server));
+ assertTrue(!ShutdownMonitor.isRegistered(server));
+ }
+
+ @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);
+ TestableServer server = new TestableServer();
+ server.setStopAtShutdown(false);
+ server.start();
+
+ assertTrue(!ShutdownThread.isRegistered(server));
+ assertTrue(ShutdownMonitor.isRegistered(server));
+
+ String key = ShutdownMonitor.getInstance().getKey();
+ int port = ShutdownMonitor.getInstance().getPort();
+
+ stop ("stop", port, key, true);
assertTrue(!ShutdownMonitor.getInstance().isAlive());
+ assertTrue(!server.stopped);
+ assertTrue(!server.destroyed);
+ assertTrue(!ShutdownThread.isRegistered(server));
+ assertTrue(ShutdownMonitor.isRegistered(server));
}
+
+
+
- public void stop(int port, String key, boolean check) throws Exception
+ public void stop(String command, int port, String key, boolean check) throws Exception
{
- System.out.printf("Attempting stop to localhost:%d (%b)%n",port,check);
+ System.out.printf("Attempting to send "+command+" to localhost:%d (%b)%n",port,check);
try (Socket s = new Socket(InetAddress.getByName("127.0.0.1"),port))
{
// send stop command
try (OutputStream out = s.getOutputStream())
{
- out.write((key + "\r\nstop\r\n").getBytes());
+ out.write((key + "\r\n"+command+"\r\n").getBytes());
out.flush();
if (check)

Back to the top