diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java index 5b95c269622..df6a4788b3b 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java @@ -211,21 +211,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo */ protected boolean dumpOnStart; - /** - *

- * Determines whether or not the server blocks when started. The default - * behavior (daemon = false) will cause the server to pause other processes - * while it continues to handle web requests. This is useful when starting the - * server with the intent to work with it interactively. - *

- * Often, it is desirable to let the server start and continue running subsequent - * processes in an automated build environment. This can be facilitated by setting - * daemon to true. - *

- * - * @parameter expression="${jetty.daemon}" default-value="false" - */ - protected boolean daemon; + /** @@ -319,9 +305,20 @@ public abstract class AbstractJettyMojo extends AbstractMojo protected Thread consoleScanner; - - - + /** + *

+ * Determines whether or not the server blocks when started. The default + * behavior (false) will cause the server to pause other processes + * while it continues to handle web requests. This is useful when starting the + * server with the intent to work with it interactively. This is the + * behaviour of the jetty:run, jetty:run-war, jetty:run-war-exploded goals. + *

+ * If true, the server will not block the execution of subsequent code. This + * is the behaviour of the jetty:start and default behaviour of the jetty:deploy goals. + *

+ */ + protected boolean nonblocking = false; + public abstract void restartWebApp(boolean reconfigureScanner) throws Exception; @@ -472,14 +469,8 @@ public abstract class AbstractJettyMojo extends AbstractMojo try { getLog().debug("Starting Jetty Server ..."); - - if(stopPort>0 && stopKey!=null) - { - ShutdownMonitor monitor = ShutdownMonitor.getInstance(); - monitor.setPort(stopPort); - monitor.setKey(stopKey); - monitor.setExitVm(!daemon); - } + + configureMonitor(); printSystemProperties(); @@ -558,7 +549,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo startConsoleScanner(); // keep the thread going if not in daemon mode - if (!daemon ) + if (!nonblocking ) { server.join(); } @@ -569,7 +560,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo } finally { - if (!daemon ) + if (!nonblocking ) { getLog().info("Jetty server exiting."); } @@ -577,6 +568,16 @@ public abstract class AbstractJettyMojo extends AbstractMojo } + public void configureMonitor() + { + if(stopPort>0 && stopKey!=null) + { + ShutdownMonitor monitor = ShutdownMonitor.getInstance(); + monitor.setPort(stopPort); + monitor.setKey(stopKey); + monitor.setExitVm(!nonblocking); + } + } /** diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyDeployWar.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyDeployWar.java index ff94f32d2d7..e4f6181c9bc 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyDeployWar.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyDeployWar.java @@ -18,6 +18,11 @@ package org.eclipse.jetty.maven.plugin; +import java.io.File; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + /** *

* This goal is used to run Jetty with a pre-assembled war. @@ -27,7 +32,7 @@ package org.eclipse.jetty.maven.plugin; * However, it doesn't assume that the current artifact is a * webapp and doesn't try to assemble it into a war before its execution. * So using it makes sense only when used in conjunction with the - * webApp configuration parameter pointing to a pre-built WAR. + * war configuration parameter pointing to a pre-built WAR. *

*

* This goal is useful e.g. for launching a web app in Jetty as a target for unit-tested @@ -42,4 +47,34 @@ package org.eclipse.jetty.maven.plugin; */ public class JettyDeployWar extends JettyRunWarMojo { + + + /** + * If true, the plugin should continue and not block. Otherwise the + * plugin will block further execution and you will need to use + * cntrl-c to stop it. + * + * + * @parameter default-value="true" + */ + protected boolean daemon = true; + + + @Override + public void execute() throws MojoExecutionException, MojoFailureException + { + nonblocking = daemon; + super.execute(); + } + + + + @Override + public void finishConfigurationBeforeStart() throws Exception + { + //only stop the server at shutdown if we are blocking + server.setStopAtShutdown(!nonblocking); + super.finishConfigurationBeforeStart(); + } + } diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java index 130dd52504a..16185ab7908 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java @@ -290,6 +290,16 @@ public class JettyRunMojo extends AbstractJettyMojo + @Override + public void finishConfigurationBeforeStart() throws Exception + { + server.setStopAtShutdown(true); //as we will normally be stopped with a cntrl-c, ensure server stopped + super.finishConfigurationBeforeStart(); + } + + + + /** * @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#configureWebApplication() */ diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarExplodedMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarExplodedMojo.java index c5f2eb88a9e..2a055e3656f 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarExplodedMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarExplodedMojo.java @@ -32,17 +32,13 @@ import org.eclipse.jetty.util.Scanner; * This goal is used to assemble your webapp into an exploded war and automatically deploy it to Jetty. *

*

- * Once invoked, the plugin can be configured to run continuously, scanning for changes in the pom.xml and + * Once invoked, the plugin runs continuously, and can be configured to scan for changes in the pom.xml and * to WEB-INF/web.xml, WEB-INF/classes or WEB-INF/lib and hot redeploy when a change is detected. *

*

* You may also specify the location of a jetty.xml file whose contents will be applied before any plugin configuration. * This can be used, for example, to deploy a static webapp that is not part of your maven build. *

- *

- * There is a reference guide to the configuration parameters for this plugin, and more detailed information - * with examples in the Configuration Guide. - *

* *@goal run-exploded *@requiresDependencyResolution compile+runtime @@ -56,7 +52,7 @@ public class JettyRunWarExplodedMojo extends AbstractJettyMojo /** * The location of the war file. * - * @parameter alias="webApp" expression="${project.build.directory}/${project.build.finalName}" + * @parameter expression="${project.build.directory}/${project.build.finalName}" * @required */ private File war; @@ -74,7 +70,14 @@ public class JettyRunWarExplodedMojo extends AbstractJettyMojo } - + + @Override + public void finishConfigurationBeforeStart() throws Exception + { + server.setStopAtShutdown(true); //as we will normally be stopped with a cntrl-c, ensure server stopped + super.finishConfigurationBeforeStart(); + } + /** * diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarMojo.java index 7fe9e71a03d..09881ab33d8 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunWarMojo.java @@ -31,18 +31,13 @@ import org.eclipse.jetty.util.Scanner; * This goal is used to assemble your webapp into a war and automatically deploy it to Jetty. *

*

- * Once invoked, the plugin can be configured to run continuously, scanning for changes in the project and to the - * war file and automatically performing a - * hot redeploy when necessary. + * Once invoked, the plugin runs continuously and can be configured to scan for changes in the project and to the + * war file and automatically perform a hot redeploy when necessary. *

*

* You may also specify the location of a jetty.xml file whose contents will be applied before any plugin configuration. * This can be used, for example, to deploy a static webapp that is not part of your maven build. *

- *

- * There is a reference guide to the configuration parameters for this plugin, and more detailed information - * with examples in the Configuration Guide. - *

* * @goal run-war * @requiresDependencyResolution compile+runtime @@ -70,6 +65,13 @@ public class JettyRunWarMojo extends AbstractJettyMojo } + @Override + public void finishConfigurationBeforeStart() throws Exception + { + server.setStopAtShutdown(true); //as we will normally be stopped with a cntrl-c, ensure server stopped + super.finishConfigurationBeforeStart(); + } + public void configureWebApplication () throws Exception diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyServer.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyServer.java index 3e60e1008b3..cd7b3c3cae3 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyServer.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyServer.java @@ -43,9 +43,7 @@ import org.eclipse.jetty.xml.XmlConfiguration; * */ public class JettyServer extends org.eclipse.jetty.server.Server -{ - public static final JettyServer __instance = new JettyServer(); - +{ private RequestLog requestLog; private ContextHandlerCollection contexts; @@ -57,7 +55,6 @@ public class JettyServer extends org.eclipse.jetty.server.Server public JettyServer() { super(); - setStopAtShutdown(true); //make sure Jetty does not use URLConnection caches with the plugin Resource.setDefaultUseCaches(false); } diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStartMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStartMojo.java index 42e9c74f102..8189d4616c4 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStartMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStartMojo.java @@ -18,6 +18,9 @@ package org.eclipse.jetty.maven.plugin; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + /** *

@@ -37,4 +40,20 @@ package org.eclipse.jetty.maven.plugin; */ public class JettyStartMojo extends JettyRunMojo { + + @Override + public void execute() throws MojoExecutionException, MojoFailureException + { + nonblocking = true; //ensure that starting jetty won't hold up the thread + super.execute(); + } + + + @Override + public void finishConfigurationBeforeStart() throws Exception + { + super.finishConfigurationBeforeStart(); + server.setStopAtShutdown(false); //as we will normally be stopped with a cntrl-c, ensure server stopped + } + } diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStopMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStopMojo.java index 80c530cf2fc..a5890b48f96 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStopMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStopMojo.java @@ -63,6 +63,10 @@ public class JettyStopMojo extends AbstractMojo */ protected int stopWait; + + + + public void execute() throws MojoExecutionException, MojoFailureException { @@ -71,13 +75,17 @@ public class JettyStopMojo extends AbstractMojo if (stopKey == null) throw new MojoExecutionException("Please specify a valid stopKey"); + //Ensure jetty Server instance stops. Whether or not the remote process + //also stops depends whether or not it was started with ShutdownMonitor.exitVm=true + String command = "forcestop"; + try { Socket s=new Socket(InetAddress.getByName("127.0.0.1"),stopPort); s.setSoLinger(false, 0); OutputStream out=s.getOutputStream(); - out.write((stopKey+"\r\nstop\r\n").getBytes()); + out.write((stopKey+"\r\n"+command+"\r\n").getBytes()); out.flush(); if (stopWait > 0) diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java index 574259a882c..63e49c97342 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java @@ -178,7 +178,6 @@ public class Starter this.server.addWebApplication(webApp); - System.err.println("STOP PORT="+stopPort+", STOP KEY="+stopKey); if(stopPort>0 && stopKey!=null) { ShutdownMonitor monitor = ShutdownMonitor.getInstance(); 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 ce4bdd9097c..38cea332fba 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 38b866574da..861c0198888 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 _lifeCycles = new CopyOnWriteArraySet(); + // 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,29 +124,38 @@ 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"); - // Shutdown Monitor - socket.shutdownOutput(); - close(socket); - socket = null; - debug("Shutting down monitor"); + //Stop the output and close the monitor socket + stopOutput(socket); if (exitVm) { @@ -126,11 +164,59 @@ public class ShutdownMonitor System.exit(0); } } - else if ("status".equals(cmd)) + 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 + 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); + + // 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 0b02ee1b8ab..0f523967bbe 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()); } - - public void stop(int port, String key, boolean check) throws Exception + + + @Test + public void testForceStopCommand() throws Exception { - System.out.printf("Attempting stop to localhost:%d (%b)%n",port,check); + //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(String command, int port, String key, boolean check) throws Exception + { + 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) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java index 168b444ca00..61bd6d15d91 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java @@ -119,6 +119,12 @@ public class ShutdownThread extends Thread _thread.unhook(); } + /* ------------------------------------------------------------ */ + public static synchronized boolean isRegistered(LifeCycle lifeCycle) + { + return _thread._lifeCycles.contains(lifeCycle); + } + /* ------------------------------------------------------------ */ @Override public void run() @@ -132,7 +138,7 @@ public class ShutdownThread extends Thread lifeCycle.stop(); LOG.debug("Stopped {}",lifeCycle); } - + if (lifeCycle instanceof Destroyable) { ((Destroyable)lifeCycle).destroy();