From 3ea91d3dde6f81bec53d1915fcf24900f19ba78a Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Thu, 30 Jun 2016 11:37:04 +0200 Subject: [PATCH] add Administration.shutdownGracefully --- .../extras/creaper/core/online/Constants.java | 1 + .../operations/admin/Administration.java | 19 ++- .../admin/AdministrationOperations.java | 2 +- .../admin/DomainAdministration.java | 65 +++++++++- .../admin/DomainAdministrationOperations.java | 20 ++- .../StandaloneAdministrationOperations.java | 10 +- testsuite/common/pom.xml | 5 + .../extras/creaper/arquillian/Extension.java | 11 ++ .../arquillian/FakeServerKillProcessor.java | 11 ++ ...boss.arquillian.core.spi.LoadableExtension | 1 + ...minTest.java => ReloadIfRequiredTest.java} | 2 +- .../online/operations/admin/ShutdownTest.java | 114 ++++++++++++++++++ 12 files changed, 251 insertions(+), 10 deletions(-) create mode 100644 testsuite/common/src/main/java/org/wildfly/extras/creaper/arquillian/Extension.java create mode 100644 testsuite/common/src/main/java/org/wildfly/extras/creaper/arquillian/FakeServerKillProcessor.java create mode 100644 testsuite/common/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension rename testsuite/standalone/src/test/java/org/wildfly/extras/creaper/core/online/operations/admin/{AdminTest.java => ReloadIfRequiredTest.java} (98%) create mode 100644 testsuite/standalone/src/test/java/org/wildfly/extras/creaper/core/online/operations/admin/ShutdownTest.java diff --git a/core/src/main/java/org/wildfly/extras/creaper/core/online/Constants.java b/core/src/main/java/org/wildfly/extras/creaper/core/online/Constants.java index 6861156c..613c93f6 100644 --- a/core/src/main/java/org/wildfly/extras/creaper/core/online/Constants.java +++ b/core/src/main/java/org/wildfly/extras/creaper/core/online/Constants.java @@ -37,6 +37,7 @@ private Constants() {} // avoid instantiation public static final String START = "start"; public static final String STEPS = "steps"; public static final String STOP = "stop"; + public static final String TIMEOUT = "timeout"; public static final String WHOAMI = "whoami"; public static final List RESULT_CODES_FOR_UNKNOWN_OR_NOT_FOUND = Collections.unmodifiableList(Arrays.asList( diff --git a/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/Administration.java b/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/Administration.java index 6f2fc431..fb61e572 100644 --- a/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/Administration.java +++ b/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/Administration.java @@ -1,5 +1,6 @@ package org.wildfly.extras.creaper.core.online.operations.admin; +import org.wildfly.extras.creaper.core.ServerVersion; import org.wildfly.extras.creaper.core.online.OnlineManagementClient; import java.io.IOException; @@ -24,6 +25,7 @@ public class Administration { static final int DEFAULT_TIMEOUT = 60; // seconds + private final OnlineManagementClient client; private final AdministrationOperations ops; public Administration(OnlineManagementClient client) { @@ -31,6 +33,7 @@ public Administration(OnlineManagementClient client) { } public Administration(OnlineManagementClient client, int timeoutInSeconds) { + this.client = client; if (client.options().isDomain) { this.ops = new DomainAdministrationOperations(client, timeoutInSeconds); } else { @@ -97,7 +100,21 @@ public final boolean restartIfRequired() throws IOException, InterruptedExceptio /** Shuts down the server. In domain, shuts down the entire host. */ public final void shutdown() throws IOException { - ops.shutdown(); + ops.shutdown(0); + } + + /** + * Shuts down the server gracefully. That is, the server will wait up to {@code timeoutInSeconds} for all active + * requests to finish. In domain, all servers on the host are shut down gracefully and then the host itself + * is shut down (there's no such thing as graceful shutdown of a host controller). + * + * @param timeoutInSeconds if {@code == 0}, then the server will shutdown immediately without waiting + * for the active requests to finish; if {@code <= 0}, then the server will wait indefinitely for the active + * requests to finish + */ + public final void shutdownGracefully(int timeoutInSeconds) throws IOException { + client.version().assertAtLeast(ServerVersion.VERSION_3_0_0, "Graceful shutdown is only supported since WildFly 9"); + ops.shutdown(timeoutInSeconds); } // --- diff --git a/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/AdministrationOperations.java b/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/AdministrationOperations.java index 4672b0b2..98e7d6ab 100644 --- a/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/AdministrationOperations.java +++ b/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/AdministrationOperations.java @@ -16,7 +16,7 @@ interface AdministrationOperations { boolean restartIfRequired() throws IOException, InterruptedException, TimeoutException; - void shutdown() throws IOException; + void shutdown(int timeoutInSeconds) throws IOException; void waitUntilRunning() throws InterruptedException, TimeoutException, IOException; } diff --git a/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/DomainAdministration.java b/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/DomainAdministration.java index f61ddcf8..3aea5cac 100644 --- a/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/DomainAdministration.java +++ b/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/DomainAdministration.java @@ -1,5 +1,6 @@ package org.wildfly.extras.creaper.core.online.operations.admin; +import org.wildfly.extras.creaper.core.ServerVersion; import org.wildfly.extras.creaper.core.online.Constants; import org.wildfly.extras.creaper.core.online.ModelNodeResult; import org.wildfly.extras.creaper.core.online.OnlineManagementClient; @@ -197,7 +198,7 @@ void restartServers(String host, List servers) throws IOException, Inter /** Shuts down given {@code host}. This is a variant of {@link Administration#shutdown()}. */ public void shutdown(String host) throws IOException, InterruptedException, TimeoutException { - domainOps.shutdown(host); + domainOps.shutdown(host, 0); } /** @see #shutdownAllServers(String) */ @@ -231,6 +232,68 @@ void shutdownServers(String host, List servers) throws IOException, Inte ops.batch(batch); } + /** + * Shuts down given {@code host} gracefully. This is a variant of {@link Administration#shutdownGracefully(int)}. + * + * @param timeoutInSeconds if {@code == 0}, then the server will shutdown immediately without waiting + * for the active requests to finish; if {@code <= 0}, then the server will wait indefinitely for the active + * requests to finish + */ + public void shutdownGracefully(String host, int timeoutInSeconds) throws IOException, InterruptedException, + TimeoutException { + client.version().assertAtLeast(ServerVersion.VERSION_3_0_0, "Graceful shutdown is only supported since WildFly 9"); + domainOps.shutdown(host, timeoutInSeconds); + } + + /** @see #shutdownAllServers(String) */ + public void shutdownAllServersGracefully(int timeoutInSeconds) throws InterruptedException, IOException, + TimeoutException { + shutdownAllServersGracefully(client.options().defaultHost, timeoutInSeconds); + } + + /** + * Shuts down all the servers on given {@code host} gracefully. As opposed to + * {@link Administration#shutdownGracefully(int)}, this doesn't shut down the entire host, just the servers. + * + * @param timeoutInSeconds if {@code == 0}, then the server will shutdown immediately without waiting + * for the active requests to finish; if {@code <= 0}, then the server will wait indefinitely for the active + * requests to finish + */ + public void shutdownAllServersGracefully(String host, int timeoutInSeconds) throws InterruptedException, + TimeoutException, IOException { + shutdownServersGracefully(host, allRunningServers(host), timeoutInSeconds); + } + + /** @see #shutdownServer(String, String) */ + public void shutdownServerGracefully(String server, int timeoutInSeconds) throws InterruptedException, + TimeoutException, IOException { + shutdownServerGracefully(client.options().defaultHost, server, timeoutInSeconds); + } + + /** + * Shuts down given {@code server} on given {@code host} gracefully. + * + * @param timeoutInSeconds if {@code == 0}, then the server will shutdown immediately without waiting + * for the active requests to finish; if {@code <= 0}, then the server will wait indefinitely for the active + * requests to finish + */ + public void shutdownServerGracefully(String host, String server, int timeoutInSeconds) + throws InterruptedException, TimeoutException, IOException { + shutdownServersGracefully(host, Collections.singletonList(server), timeoutInSeconds); + } + + void shutdownServersGracefully(String host, List servers, int timeoutInSeconds) throws IOException, + InterruptedException, TimeoutException { + client.version().assertAtLeast(ServerVersion.VERSION_3_0_0, "Graceful shutdown is only supported since WildFly 9"); + + Batch batch = new Batch(); + for (String server : servers) { + batch.invoke(Constants.STOP, Address.host(host).and(Constants.SERVER_CONFIG, server), + Values.of(Constants.TIMEOUT, timeoutInSeconds)); + } + ops.batch(batch); + } + // --- /** diff --git a/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/DomainAdministrationOperations.java b/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/DomainAdministrationOperations.java index abbeb876..49d90cd8 100644 --- a/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/DomainAdministrationOperations.java +++ b/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/DomainAdministrationOperations.java @@ -6,6 +6,7 @@ import org.wildfly.extras.creaper.core.online.operations.Address; import org.wildfly.extras.creaper.core.online.operations.Batch; import org.wildfly.extras.creaper.core.online.operations.Operations; +import org.wildfly.extras.creaper.core.online.operations.Values; import java.io.IOException; import java.util.ArrayList; @@ -63,12 +64,23 @@ public boolean restartIfRequired() throws IOException, InterruptedException, Tim } @Override - public void shutdown() throws IOException { - shutdown(client.options().defaultHost); + public void shutdown(int timeoutInSeconds) throws IOException { + shutdown(client.options().defaultHost, timeoutInSeconds); } - void shutdown(String host) throws IOException { - ops.invoke(Constants.SHUTDOWN, Address.host(host)); + void shutdown(String host, int timeoutInSeconds) throws IOException { + if (timeoutInSeconds == 0) { + // older versions don't understand the "timeout" parameter + ops.invoke(Constants.SHUTDOWN, Address.host(host)); + } else { + Batch batch = new Batch(); + for (String server : allRunningServers(host)) { + batch.invoke(Constants.STOP, Address.host(host).and(Constants.SERVER_CONFIG, server), + Values.of(Constants.TIMEOUT, timeoutInSeconds)); + } + batch.invoke(Constants.SHUTDOWN, Address.host(host)); + ops.batch(batch); + } } @Override diff --git a/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/StandaloneAdministrationOperations.java b/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/StandaloneAdministrationOperations.java index 5a9e64b1..80c26eff 100644 --- a/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/StandaloneAdministrationOperations.java +++ b/core/src/main/java/org/wildfly/extras/creaper/core/online/operations/admin/StandaloneAdministrationOperations.java @@ -5,6 +5,7 @@ import org.wildfly.extras.creaper.core.online.OnlineManagementClient; import org.wildfly.extras.creaper.core.online.operations.Address; import org.wildfly.extras.creaper.core.online.operations.Operations; +import org.wildfly.extras.creaper.core.online.operations.Values; import java.io.IOException; import java.util.concurrent.TimeUnit; @@ -55,8 +56,13 @@ public boolean restartIfRequired() throws IOException, InterruptedException, Tim } @Override - public void shutdown() throws IOException { - ops.invoke(Constants.SHUTDOWN, Address.root()); + public void shutdown(int timeoutInSeconds) throws IOException { + if (timeoutInSeconds == 0) { + // older versions don't understand the "timeout" parameter + ops.invoke(Constants.SHUTDOWN, Address.root()); + } else { + ops.invoke(Constants.SHUTDOWN, Address.root(), Values.of(Constants.TIMEOUT, timeoutInSeconds)); + } } @Override diff --git a/testsuite/common/pom.xml b/testsuite/common/pom.xml index 849f69b2..d563e25e 100644 --- a/testsuite/common/pom.xml +++ b/testsuite/common/pom.xml @@ -24,6 +24,11 @@ bcprov-jdk15on compile + + org.jboss.arquillian.junit + arquillian-junit-container + compile + org.jboss.shrinkwrap shrinkwrap-api diff --git a/testsuite/common/src/main/java/org/wildfly/extras/creaper/arquillian/Extension.java b/testsuite/common/src/main/java/org/wildfly/extras/creaper/arquillian/Extension.java new file mode 100644 index 00000000..2be90ad8 --- /dev/null +++ b/testsuite/common/src/main/java/org/wildfly/extras/creaper/arquillian/Extension.java @@ -0,0 +1,11 @@ +package org.wildfly.extras.creaper.arquillian; + +import org.jboss.arquillian.container.spi.ServerKillProcessor; +import org.jboss.arquillian.core.spi.LoadableExtension; + +public final class Extension implements LoadableExtension { + @Override + public void register(ExtensionBuilder builder) { + builder.service(ServerKillProcessor.class, FakeServerKillProcessor.class); + } +} diff --git a/testsuite/common/src/main/java/org/wildfly/extras/creaper/arquillian/FakeServerKillProcessor.java b/testsuite/common/src/main/java/org/wildfly/extras/creaper/arquillian/FakeServerKillProcessor.java new file mode 100644 index 00000000..da8a504f --- /dev/null +++ b/testsuite/common/src/main/java/org/wildfly/extras/creaper/arquillian/FakeServerKillProcessor.java @@ -0,0 +1,11 @@ +package org.wildfly.extras.creaper.arquillian; + +import org.jboss.arquillian.container.spi.Container; +import org.jboss.arquillian.container.spi.ServerKillProcessor; + +final class FakeServerKillProcessor implements ServerKillProcessor { + @Override + public void kill(Container container) throws Exception { + // does nothing, this is only to let Arquillian know that the server is gone + } +} diff --git a/testsuite/common/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension b/testsuite/common/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension new file mode 100644 index 00000000..da4a21bd --- /dev/null +++ b/testsuite/common/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension @@ -0,0 +1 @@ +org.wildfly.extras.creaper.arquillian.Extension \ No newline at end of file diff --git a/testsuite/standalone/src/test/java/org/wildfly/extras/creaper/core/online/operations/admin/AdminTest.java b/testsuite/standalone/src/test/java/org/wildfly/extras/creaper/core/online/operations/admin/ReloadIfRequiredTest.java similarity index 98% rename from testsuite/standalone/src/test/java/org/wildfly/extras/creaper/core/online/operations/admin/AdminTest.java rename to testsuite/standalone/src/test/java/org/wildfly/extras/creaper/core/online/operations/admin/ReloadIfRequiredTest.java index 95feedc8..0d195503 100644 --- a/testsuite/standalone/src/test/java/org/wildfly/extras/creaper/core/online/operations/admin/AdminTest.java +++ b/testsuite/standalone/src/test/java/org/wildfly/extras/creaper/core/online/operations/admin/ReloadIfRequiredTest.java @@ -21,7 +21,7 @@ import static org.junit.Assert.assertTrue; @RunWith(Arquillian.class) -public class AdminTest { +public class ReloadIfRequiredTest { private OnlineManagementClient client; private Operations ops; private Administration admin; diff --git a/testsuite/standalone/src/test/java/org/wildfly/extras/creaper/core/online/operations/admin/ShutdownTest.java b/testsuite/standalone/src/test/java/org/wildfly/extras/creaper/core/online/operations/admin/ShutdownTest.java new file mode 100644 index 00000000..2137869a --- /dev/null +++ b/testsuite/standalone/src/test/java/org/wildfly/extras/creaper/core/online/operations/admin/ShutdownTest.java @@ -0,0 +1,114 @@ +package org.wildfly.extras.creaper.core.online.operations.admin; + +import org.jboss.arquillian.container.test.api.ContainerController; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.junit.InSequence; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.junit.After; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.wildfly.extras.creaper.core.ManagementClient; +import org.wildfly.extras.creaper.core.ServerVersion; +import org.wildfly.extras.creaper.core.online.OnlineManagementClient; +import org.wildfly.extras.creaper.core.online.OnlineOptions; +import org.wildfly.extras.creaper.test.ManualTests; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@Category(ManualTests.class) +@RunWith(Arquillian.class) +public class ShutdownTest { + private OnlineManagementClient client = ManagementClient.onlineLazy( + OnlineOptions.standalone().localDefault().build()); + private Administration admin = new Administration(client); + + @ArquillianResource + private ContainerController controller; + + @Test + @InSequence(1) + public void startServer() { + controller.start(ManualTests.ARQUILLIAN_CONTAINER); + } + + @Test + @InSequence(2) + public void shutdown() throws Exception { + assertTrue(controller.isStarted(ManualTests.ARQUILLIAN_CONTAINER)); + + admin.shutdown(); + serverShutdown(); + + assertFalse(controller.isStarted(ManualTests.ARQUILLIAN_CONTAINER)); + } + + @Test + @InSequence(3) + public void startServerAgain() throws Exception { + controller.start(ManualTests.ARQUILLIAN_CONTAINER); + client.reconnect(10); + } + + @Test + @InSequence(4) + public void shutdownGracefully() throws Exception { + if (client.version().lessThan(ServerVersion.VERSION_3_0_0)) { + // graceful shutdown not supported + return; + } + + assertTrue(controller.isStarted(ManualTests.ARQUILLIAN_CONTAINER)); + + admin.shutdownGracefully(5); + serverShutdown(); + + assertFalse(controller.isStarted(ManualTests.ARQUILLIAN_CONTAINER)); + + } + + @Test + @InSequence(5) + public void startServerYetAgain() throws Exception { + startServerAgain(); + } + + @Test + @InSequence(6) + public void shutdownGracefullyWithZeroTimeout() throws Exception { + if (client.version().lessThan(ServerVersion.VERSION_3_0_0)) { + // graceful shutdown not supported + return; + } + + assertTrue(controller.isStarted(ManualTests.ARQUILLIAN_CONTAINER)); + + admin.shutdownGracefully(0); + serverShutdown(); + + assertFalse(controller.isStarted(ManualTests.ARQUILLIAN_CONTAINER)); + } + + @Test + @InSequence(7) + public void stopServer() throws Exception { + if (controller.isStarted(ManualTests.ARQUILLIAN_CONTAINER)) { + controller.stop(ManualTests.ARQUILLIAN_CONTAINER); + } + } + + @After + public void tearDown() throws Exception { + client.close(); + } + + private void serverShutdown() throws Exception { + // server shutdown takes a couple of millis, we have to wait for it + // TODO how could Creaper wait for the server to shut down? polling the mgmt port? + Thread.sleep(100); + + // this only lets Arquillian know that the server is gone (see FakeServerKillProcessor) + controller.kill(ManualTests.ARQUILLIAN_CONTAINER); + } +}