diff --git a/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/extension/core-management/main/module.xml b/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/extension/core-management/main/module.xml index 8c993cf72ce..e98404f4a0b 100644 --- a/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/extension/core-management/main/module.xml +++ b/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/extension/core-management/main/module.xml @@ -26,6 +26,7 @@ + diff --git a/core-feature-pack/galleon-feature-pack/pom.xml b/core-feature-pack/galleon-feature-pack/pom.xml index d638df070d4..e565ad95d19 100644 --- a/core-feature-pack/galleon-feature-pack/pom.xml +++ b/core-feature-pack/galleon-feature-pack/pom.xml @@ -66,8 +66,8 @@ wildfly-core-model-test-framework - org.wildfly.core - wildfly-model-test + * + * provided diff --git a/core-management/core-management-subsystem/src/main/java/org/wildfly/extension/core/management/ProcessStateListenerService.java b/core-management/core-management-subsystem/src/main/java/org/wildfly/extension/core/management/ProcessStateListenerService.java index 68e507368f9..86d3de63881 100644 --- a/core-management/core-management-subsystem/src/main/java/org/wildfly/extension/core/management/ProcessStateListenerService.java +++ b/core-management/core-management-subsystem/src/main/java/org/wildfly/extension/core/management/ProcessStateListenerService.java @@ -27,13 +27,14 @@ import org.jboss.as.controller.ProcessType; import org.jboss.as.controller.RunningMode; import org.jboss.as.controller.management.Capabilities; -import org.jboss.as.server.suspend.OperationListener; -import org.jboss.as.server.suspend.SuspendController; +import org.jboss.as.server.suspend.ServerSuspendController; +import org.jboss.as.server.suspend.ServerSuspendListener; import org.jboss.msc.Service; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.StartContext; import org.jboss.msc.service.StartException; import org.jboss.msc.service.StopContext; +import org.wildfly.common.function.Functions; import org.wildfly.extension.core.management.client.Process; import org.wildfly.extension.core.management.client.Process.RunningState; import org.wildfly.extension.core.management.client.RuntimeConfigurationStateChangeEvent; @@ -57,10 +58,10 @@ public class ProcessStateListenerService implements Service { static final ServiceName SERVICE_NAME = ServiceName.JBOSS.append("core", "management", "process-state-listener"); private final Supplier processStateNotifierSupplier; - private final Supplier suspendControllerSupplier; + private final Supplier suspendControllerSupplier; private final Supplier executorSupplier; private final PropertyChangeListener propertyChangeListener; - private final OperationListener operationListener; + private final ServerSuspendListener suspendListener; private final ProcessStateListener listener; private final ProcessStateListenerInitParameters parameters; private final String name; @@ -72,7 +73,7 @@ public class ProcessStateListenerService implements Service { private ProcessStateListenerService(ProcessType processType, RunningMode runningMode, String name, ProcessStateListener listener, Map properties, int timeout, final Supplier processStateNotifierSupplier, - final Supplier suspendControllerSupplier, + final Supplier suspendControllerSupplier, final Supplier executorSupplier ) { CoreManagementLogger.ROOT_LOGGER.debugf("Initalizing ProcessStateListenerService with a running mode of %s", runningMode); @@ -96,20 +97,20 @@ private ProcessStateListenerService(ProcessType processType, RunningMode running this.suspendControllerSupplier = suspendControllerSupplier; this.executorSupplier = executorSupplier; if (!processType.isHostController()) { - this.operationListener = new OperationListener() { + this.suspendListener = new ServerSuspendListener() { @Override public void suspendStarted() { suspendTransition(runningState, Process.RunningState.SUSPENDING); } @Override - public void complete() { + public void suspendCompleted() { suspendTransition(runningState, Process.RunningState.SUSPENDED); } @Override - public void cancelled() { - if(runningState == null || runningState == Process.RunningState.STARTING) {//gracefull startup + public void suspendCancelled() { + if (runningState == null || runningState == Process.RunningState.STARTING) {//gracefull startup suspendTransition(Process.RunningState.STARTING, Process.RunningState.SUSPENDED); } switch (runningMode) { @@ -123,11 +124,11 @@ public void cancelled() { } @Override - public void timeout() { + public void suspendTimeout() { } }; } else { - operationListener = null; + suspendListener = null; } } @@ -241,8 +242,8 @@ static void install(CapabilityServiceTarget serviceTarget, ProcessType processTy final CapabilityServiceBuilder builder = serviceTarget.addCapability(PROCESS_STATE_LISTENER_CAPABILITY.fromBaseCapability(listenerName)); final Supplier psnSupplier = builder.requires(ProcessStateNotifier.SERVICE_DESCRIPTOR); final Supplier esSupplier = builder.requires(Capabilities.MANAGEMENT_EXECUTOR); - final Supplier scSupplier = !processType.isHostController() ? builder.requiresCapability("org.wildfly.server.suspend-controller", SuspendController.class) : null; - builder.setInstance(new ProcessStateListenerService(processType, runningMode, listenerName, listener, properties, timeout, psnSupplier, scSupplier, esSupplier)); + final Supplier suspendController = !processType.isHostController() ? builder.requires(ServerSuspendController.SERVICE_DESCRIPTOR) : Functions.constantSupplier(null); + builder.setInstance(new ProcessStateListenerService(processType, runningMode, listenerName, listener, properties, timeout, psnSupplier, suspendController, esSupplier)); builder.install(); } @@ -258,10 +259,9 @@ public void start(StartContext context) { WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(currentTccl); } processStateNotifierSupplier.get().addPropertyChangeListener(propertyChangeListener); - final Supplier suspendControllerSupplier = ProcessStateListenerService.this.suspendControllerSupplier; - SuspendController controller = suspendControllerSupplier != null ? suspendControllerSupplier.get() : null; + ServerSuspendController controller = ProcessStateListenerService.this.suspendControllerSupplier.get(); if (controller != null) { - controller.addListener(operationListener); + controller.addSuspendListener(suspendListener); CoreManagementLogger.ROOT_LOGGER.debugf("Starting ProcessStateListenerService with a SuspendControllerState %s", controller.getState()); switch (controller.getState()) { case PRE_SUSPEND: @@ -321,9 +321,9 @@ public void stop(StopContext context) { Runnable asyncStop = () -> { synchronized (stopLock) { processStateNotifierSupplier.get().removePropertyChangeListener(propertyChangeListener); - SuspendController controller = suspendControllerSupplier != null ? suspendControllerSupplier.get() : null; + ServerSuspendController controller = ProcessStateListenerService.this.suspendControllerSupplier.get(); if (controller != null) { - controller.removeListener(operationListener); + controller.removeSuspendListener(suspendListener); } runningState = null; ClassLoader currentTccl = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged(); diff --git a/core-model-test/framework/pom.xml b/core-model-test/framework/pom.xml index 27334639cb6..27ba10bcab7 100644 --- a/core-model-test/framework/pom.xml +++ b/core-model-test/framework/pom.xml @@ -32,13 +32,17 @@ junit junit - provided + compile + + + org.mockito + mockito-core + compile + + + org.wildfly.legacy.test + wildfly-legacy-spi + compile - - org.wildfly.legacy.test - wildfly-legacy-spi - provided - - diff --git a/core-model-test/framework/src/main/java/org/jboss/as/core/model/test/TestModelControllerService.java b/core-model-test/framework/src/main/java/org/jboss/as/core/model/test/TestModelControllerService.java index ffab08e347b..a39af0eb3d3 100644 --- a/core-model-test/framework/src/main/java/org/jboss/as/core/model/test/TestModelControllerService.java +++ b/core-model-test/framework/src/main/java/org/jboss/as/core/model/test/TestModelControllerService.java @@ -6,6 +6,8 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAMESPACES; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SCHEMA_LOCATIONS; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import java.io.File; import java.io.IOException; @@ -78,6 +80,7 @@ import org.jboss.as.server.ServerPathManagerService; import org.jboss.as.server.controller.resources.ServerRootResourceDefinition; import org.jboss.as.server.controller.resources.VersionModelInitializer; +import org.jboss.as.server.suspend.ServerSuspendController; import org.jboss.as.version.ProductConfig; import org.jboss.as.version.Version; import org.jboss.dmr.ModelNode; @@ -102,6 +105,7 @@ class TestModelControllerService extends ModelTestModelControllerService { private final ExtensionRegistry extensionRegistry; private final CapabilityRegistry capabilityRegistry; private volatile Initializer initializer; + private final ServerSuspendController suspendController = mock(ServerSuspendController.class); TestModelControllerService(ProcessType processType, RunningModeControl runningModeControl, StringConfigurationPersister persister, ModelTestOperationValidatorFilter validateOpsFilter, TestModelType type, ModelInitializer modelInitializer, TestDelegatingResourceDefinition rootResourceDefinition, ControlledProcessState processState, ExtensionRegistry extensionRegistry, @@ -124,6 +128,7 @@ class TestModelControllerService extends ModelTestModelControllerService { } else if (type == TestModelType.DOMAIN) { initializer = new DomainInitializer(); } + doReturn(ServerSuspendController.State.RUNNING).when(this.suspendController).getState(); } static TestModelControllerService create(ProcessType processType, RunningModeControl runningModeControl, StringConfigurationPersister persister, ModelTestOperationValidatorFilter validateOpsFilter, @@ -407,7 +412,7 @@ public void setRootResourceDefinitionDelegate() { securityIdentitySupplier, AuditLogger.NO_OP_LOGGER, getMutableRootResourceRegistrationProvider(), - getBootErrorCollector(), capabilityRegistry)); + getBootErrorCollector(), capabilityRegistry, suspendController)); } @Override diff --git a/host-controller/src/main/java/org/jboss/as/host/controller/model/host/HostResourceDefinition.java b/host-controller/src/main/java/org/jboss/as/host/controller/model/host/HostResourceDefinition.java index b10cfc30d3d..54bfcfe86a1 100644 --- a/host-controller/src/main/java/org/jboss/as/host/controller/model/host/HostResourceDefinition.java +++ b/host-controller/src/main/java/org/jboss/as/host/controller/model/host/HostResourceDefinition.java @@ -323,7 +323,7 @@ public void execute(OperationContext context, ModelNode operation) throws Operat hostRegistration.registerReadOnlyAttribute(HostResourceDefinition.RUNTIME_CONFIGURATION_STATE, new ProcessStateAttributeHandler(processState)); hostRegistration.registerReadOnlyAttribute(HostResourceDefinition.HOST_STATE, new ProcessStateAttributeHandler(processState)); hostRegistration.registerReadOnlyAttribute(ServerRootResourceDefinition.RUNNING_MODE, new RunningModeReadHandler(runningModeControl)); - hostRegistration.registerReadOnlyAttribute(ServerRootResourceDefinition.SUSPEND_STATE, SuspendStateReadHandler.INSTANCE); + hostRegistration.registerReadOnlyAttribute(ServerRootResourceDefinition.SUSPEND_STATE, new SuspendStateReadHandler(null)); } diff --git a/request-controller/pom.xml b/request-controller/pom.xml index 2421cc37ad6..4806d1c13e5 100644 --- a/request-controller/pom.xml +++ b/request-controller/pom.xml @@ -79,6 +79,11 @@ junit test + + org.mockito + mockito-core + test + org.wildfly.core wildfly-subsystem-test diff --git a/request-controller/src/main/java/org/wildfly/extension/requestcontroller/RequestController.java b/request-controller/src/main/java/org/wildfly/extension/requestcontroller/RequestController.java index 08827b4583e..36c2f2c884a 100644 --- a/request-controller/src/main/java/org/wildfly/extension/requestcontroller/RequestController.java +++ b/request-controller/src/main/java/org/wildfly/extension/requestcontroller/RequestController.java @@ -21,7 +21,7 @@ import org.jboss.as.server.suspend.CountingRequestCountCallback; import org.jboss.as.server.suspend.ServerActivity; import org.jboss.as.server.suspend.ServerActivityCallback; -import org.jboss.as.server.suspend.SuspendController; +import org.jboss.as.server.suspend.ServerSuspendController; import org.jboss.msc.service.Service; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.StartContext; @@ -59,9 +59,9 @@ public class RequestController implements Service, ServerActi private volatile ServerActivityCallback listener = null; private final boolean trackIndividualControlPoints; - private final Supplier suspendController; + private final Supplier suspendController; - public RequestController(boolean trackIndividualControlPoints, Supplier suspendControllerSupplier) { + public RequestController(boolean trackIndividualControlPoints, Supplier suspendControllerSupplier) { this.trackIndividualControlPoints = trackIndividualControlPoints; this.suspendController = suspendControllerSupplier; } @@ -302,13 +302,13 @@ public boolean isPaused() { @Override public void start(StartContext startContext) throws StartException { - suspendController.get().registerActivity(this); + suspendController.get().addServerActivity(this); timer = new Timer(); } @Override public void stop(StopContext stopContext) { - suspendController.get().unRegisterActivity(this); + suspendController.get().removeServerActivity(this); timer.cancel(); timer = null; while (!taskQueue.isEmpty()) { diff --git a/request-controller/src/main/java/org/wildfly/extension/requestcontroller/RequestControllerSubsystemAdd.java b/request-controller/src/main/java/org/wildfly/extension/requestcontroller/RequestControllerSubsystemAdd.java index 8ec91cd32ae..264f4b7483b 100644 --- a/request-controller/src/main/java/org/wildfly/extension/requestcontroller/RequestControllerSubsystemAdd.java +++ b/request-controller/src/main/java/org/wildfly/extension/requestcontroller/RequestControllerSubsystemAdd.java @@ -15,7 +15,7 @@ import org.jboss.as.server.AbstractDeploymentChainStep; import org.jboss.as.server.DeploymentProcessorTarget; import org.jboss.as.server.deployment.Phase; -import org.jboss.as.server.suspend.SuspendController; +import org.jboss.as.server.suspend.ServerSuspendController; import org.jboss.dmr.ModelNode; import java.util.function.Supplier; @@ -49,7 +49,7 @@ protected void execute(DeploymentProcessorTarget processorTarget) { CapabilityServiceBuilder svcBuilder = context.getCapabilityServiceTarget().addCapability(REQUEST_CONTROLLER_CAPABILITY); - Supplier supplier = svcBuilder.requiresCapability("org.wildfly.server.suspend-controller", SuspendController.class); + Supplier supplier = svcBuilder.requires(ServerSuspendController.SERVICE_DESCRIPTOR); RequestController requestController = new RequestController(trackIndividual, supplier); requestController.setMaxRequestCount(maxRequests); svcBuilder.setInstance(requestController) diff --git a/request-controller/src/test/java/org/wildfly/extension/requestcontroller/RequestControllerSubsystemTestCase.java b/request-controller/src/test/java/org/wildfly/extension/requestcontroller/RequestControllerSubsystemTestCase.java index 23776eeb946..fa1c35ed82f 100644 --- a/request-controller/src/test/java/org/wildfly/extension/requestcontroller/RequestControllerSubsystemTestCase.java +++ b/request-controller/src/test/java/org/wildfly/extension/requestcontroller/RequestControllerSubsystemTestCase.java @@ -5,20 +5,20 @@ package org.wildfly.extension.requestcontroller; -import static org.jboss.as.server.Services.JBOSS_SUSPEND_CONTROLLER; +import static org.mockito.Mockito.mock; import java.io.IOException; +import java.util.function.Consumer; -import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.RunningMode; -import org.jboss.as.controller.notification.NotificationFilter; -import org.jboss.as.controller.notification.NotificationHandler; -import org.jboss.as.controller.notification.NotificationHandlerRegistry; -import org.jboss.as.server.suspend.SuspendController; +import org.jboss.as.controller.ServiceNameFactory; +import org.jboss.as.server.suspend.ServerSuspendController; import org.jboss.as.subsystem.test.AbstractSubsystemBaseTest; import org.jboss.as.subsystem.test.AdditionalInitialization; import org.jboss.as.subsystem.test.KernelServices; import org.jboss.as.subsystem.test.KernelServicesBuilder; +import org.jboss.msc.Service; +import org.jboss.msc.service.ServiceBuilder; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceTarget; import org.junit.Assert; @@ -60,21 +60,9 @@ protected AdditionalInitialization createAdditionalInitialization() { @Override protected void addExtraServices(ServiceTarget target) { - SuspendController suspendController = new SuspendController(); - final NotificationHandlerRegistry nhr = new NotificationHandlerRegistry() { - @Override - public void registerNotificationHandler(PathAddress source, NotificationHandler handler, NotificationFilter filter) { - - } - - @Override - public void unregisterNotificationHandler(PathAddress source, NotificationHandler handler, NotificationFilter filter) { - - } - }; - suspendController.getNotificationHandlerRegistry().setValue(() -> nhr); - target.addService(JBOSS_SUSPEND_CONTROLLER, suspendController) - .install(); + ServiceBuilder builder = target.addService(); + Consumer injector = builder.provides(ServiceNameFactory.resolveServiceName(ServerSuspendController.SERVICE_DESCRIPTOR)); + builder.setInstance(Service.newInstance(injector, mock(ServerSuspendController.class))).install(); } @Override diff --git a/server/src/main/java/org/jboss/as/server/ApplicationServerService.java b/server/src/main/java/org/jboss/as/server/ApplicationServerService.java index 70749133031..924d2a054b7 100644 --- a/server/src/main/java/org/jboss/as/server/ApplicationServerService.java +++ b/server/src/main/java/org/jboss/as/server/ApplicationServerService.java @@ -29,7 +29,7 @@ import org.jboss.as.server.mgmt.domain.RemoteFileRepositoryService; import org.jboss.as.server.moduleservice.ExternalModuleService; import org.jboss.as.server.moduleservice.ServiceModuleLoader; -import org.jboss.as.server.suspend.SuspendController; +import org.jboss.as.server.suspend.ServerSuspendController; import org.jboss.as.version.ProductConfig; import org.jboss.msc.service.Service; import org.jboss.msc.service.ServiceActivator; @@ -55,7 +55,7 @@ final class ApplicationServerService implements Service extraServices, final Bootstrap.Configuration configuration, - final ControlledProcessState processState, final SuspendController suspendController, + final ControlledProcessState processState, final ServerSuspendController suspendController, final ElapsedTime elapsedTime) { this.extraServices = extraServices; this.configuration = configuration; diff --git a/server/src/main/java/org/jboss/as/server/BootstrapImpl.java b/server/src/main/java/org/jboss/as/server/BootstrapImpl.java index 333c6f27c5e..9fcb58bfccb 100644 --- a/server/src/main/java/org/jboss/as/server/BootstrapImpl.java +++ b/server/src/main/java/org/jboss/as/server/BootstrapImpl.java @@ -5,9 +5,9 @@ package org.jboss.as.server; -import static org.jboss.as.server.Services.JBOSS_SUSPEND_CONTROLLER; - import java.lang.management.ManagementFactory; +import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -19,8 +19,9 @@ import org.jboss.as.controller.ControlledProcessStateService; import org.jboss.as.server.jmx.RunningStateJmx; import org.jboss.as.server.logging.ServerLogger; -import org.jboss.as.server.suspend.OperationListener; import org.jboss.as.server.suspend.SuspendController; +import org.jboss.as.server.suspend.ServerSuspendController; +import org.jboss.as.server.suspend.ServerSuspendListener; import org.jboss.modules.Module; import org.jboss.modules.ModuleIdentifier; import org.jboss.modules.ModuleLoadException; @@ -99,7 +100,8 @@ private AsyncFuture internalBootstrap(final Configuration conf final ControlledProcessState processState = new ControlledProcessState(true); shutdownHook.setControlledProcessState(processState); ProcessStateNotifier processStateNotifier = ControlledProcessStateService.addService(tracker, processState); - final SuspendController suspendController = new SuspendController(); + ServerSuspendController suspendController = new SuspendController(); + this.shutdownHook.setSuspendController(suspendController); //Instantiating the suspendcontroller here to be able to get a reference to it in RunningStateJmx //Note that the SuspendController service will be started in the ServerService during the boot of the server. RunningStateJmx.registerMBean( @@ -211,6 +213,7 @@ private static class ShutdownHook extends Thread { private boolean down; private ControlledProcessState processState; private ServiceContainer container; + private volatile ServerSuspendController suspendController; private ServiceContainer register() { @@ -229,6 +232,10 @@ private synchronized void setControlledProcessState(final ControlledProcessState this.processState = ps; } + void setSuspendController(ServerSuspendController suspendController) { + this.suspendController = suspendController; + } + @Override public void run() { shutdown(false); @@ -277,45 +284,47 @@ public void handleTermination(Info info) { } } - private static void suspend(ServiceContainer sc) { + private void suspend(ServiceContainer sc) { try { - final SuspendController suspendController = getSuspendController(sc); - if (suspendController != null) { - long timeout = getSuspendTimeout(); + final ServerSuspendController suspendController = this.suspendController; + if ((suspendController != null) && !sc.isShutdownComplete()) { + Duration timeout = getSuspendTimeout(); final CountDownLatch suspendLatch = new CountDownLatch(1); - OperationListener listener = new OperationListener() { + ServerSuspendListener listener = new ServerSuspendListener() { @Override public void suspendStarted() { } @Override - public void complete() { - suspendController.removeListener(this); + public void suspendCompleted() { + suspendController.removeSuspendListener(this); suspendLatch.countDown(); } @Override - public void cancelled() { - suspendController.removeListener(this); + public void suspendCancelled() { + suspendController.removeSuspendListener(this); suspendLatch.countDown(); } @Override - public void timeout() { - suspendController.removeListener(this); + public void suspendTimeout() { + suspendController.removeSuspendListener(this); suspendLatch.countDown(); } }; - suspendController.addListener(listener); + suspendController.addSuspendListener(listener); suspendController.suspend(timeout); - if (timeout > 0) { - // The latch should trip within 'timeout' but if necessary we'll wait - // 500 ms longer for it in the off chance a gc or something delays things - suspendLatch.await(timeout + 500, TimeUnit.MILLISECONDS); - } else if (timeout < 0) { + if (timeout != null) { + if (!timeout.isZero()) { + // The latch should trip within 'timeout' but if necessary we'll wait + // 500 ms longer for it in the off chance a gc or something delays things + suspendLatch.await(timeout.plus(500, ChronoUnit.MILLIS).toMillis(), TimeUnit.MILLISECONDS); + } // else 0 means we don't wait for tasks to finish + } else { suspendLatch.await(); - } // else 0 means we don't wait for tasks to finish + } } } catch (InterruptedException ie) { Thread.currentThread().interrupt(); @@ -325,33 +334,17 @@ public void timeout() { } } - private static SuspendController getSuspendController(ServiceContainer sc) { - SuspendController result = null; - if (sc != null && !sc.isShutdownComplete()) { - final ServiceController serviceController = sc.getService(JBOSS_SUSPEND_CONTROLLER); - if (serviceController != null && serviceController.getState() == ServiceController.State.UP) { - result = (SuspendController) serviceController.getValue(); - } - } - return result; - } - - private static long getSuspendTimeout() { - long result = 0; + private static Duration getSuspendTimeout() { String timeoutString = System.getProperty(SIGTERM_SUSPEND_TIMEOUT_PROP); if (timeoutString != null && timeoutString.length() > 0) { try { - int max = Integer.decode(timeoutString); - result = max > 0 ? max * 1000 : max; + int seconds = Integer.decode(timeoutString); + return (seconds >= 0) ? Duration.ofSeconds(seconds) : null; } catch(NumberFormatException ex) { - try { - ServerLogger.ROOT_LOGGER.failedToParseCommandLineInteger(SIGTERM_SUSPEND_TIMEOUT_PROP, timeoutString); - } catch (Throwable ignored) { - // ignore - } + ServerLogger.ROOT_LOGGER.failedToParseCommandLineInteger(SIGTERM_SUSPEND_TIMEOUT_PROP, timeoutString); } } - return result; + return Duration.ZERO; } } } diff --git a/server/src/main/java/org/jboss/as/server/GracefulShutdownService.java b/server/src/main/java/org/jboss/as/server/GracefulShutdownService.java index 8cef855c17a..0485f16b167 100644 --- a/server/src/main/java/org/jboss/as/server/GracefulShutdownService.java +++ b/server/src/main/java/org/jboss/as/server/GracefulShutdownService.java @@ -6,14 +6,13 @@ package org.jboss.as.server; import org.jboss.as.server.logging.ServerLogger; -import org.jboss.as.server.suspend.OperationListener; -import org.jboss.as.server.suspend.SuspendController; +import org.jboss.as.server.suspend.ServerSuspendController; +import org.jboss.as.server.suspend.ServerSuspendListener; import org.jboss.msc.service.Service; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.StartContext; import org.jboss.msc.service.StartException; import org.jboss.msc.service.StopContext; -import org.jboss.msc.value.InjectedValue; /** @@ -28,13 +27,17 @@ public class GracefulShutdownService implements Service public static final ServiceName SERVICE_NAME = ServiceName.JBOSS.append("server", "graceful-shutdown-service"); - private final InjectedValue suspendControllerInjectedValue = new InjectedValue<>(); + private final ServerSuspendController suspendController; private boolean suspend = false; private boolean shuttingDown = false; private final Object lock = new Object(); - private final OperationListener listener = new OperationListener() { + GracefulShutdownService(ServerSuspendController suspendController) { + this.suspendController = suspendController; + } + + private final ServerSuspendListener listener = new ServerSuspendListener() { @Override public void suspendStarted() { synchronized (lock) { @@ -43,7 +46,7 @@ public void suspendStarted() { } @Override - public void complete() { + public void suspendCompleted() { synchronized (lock) { suspend = false; lock.notifyAll(); @@ -51,7 +54,7 @@ public void complete() { } @Override - public void cancelled() { + public void suspendCancelled() { synchronized (lock) { suspend = false; @@ -60,7 +63,7 @@ public void cancelled() { } @Override - public void timeout() { + public void suspendTimeout() { synchronized (lock) { suspend = false; lock.notifyAll(); @@ -70,12 +73,12 @@ public void timeout() { @Override public void start(StartContext context) throws StartException { - suspendControllerInjectedValue.getValue().addListener(listener); + this.suspendController.addSuspendListener(listener); } @Override public void stop(StopContext context) { - suspendControllerInjectedValue.getValue().removeListener(listener); + this.suspendController.removeSuspendListener(listener); } public void startGracefulShutdown() { @@ -96,10 +99,6 @@ public void awaitSuspend() { } } - public InjectedValue getSuspendControllerInjectedValue() { - return suspendControllerInjectedValue; - } - @Override public GracefulShutdownService getValue() throws IllegalStateException, IllegalArgumentException { return this; diff --git a/server/src/main/java/org/jboss/as/server/ServerService.java b/server/src/main/java/org/jboss/as/server/ServerService.java index 7e7915dd112..30fda5655d4 100644 --- a/server/src/main/java/org/jboss/as/server/ServerService.java +++ b/server/src/main/java/org/jboss/as/server/ServerService.java @@ -53,7 +53,6 @@ import org.jboss.as.controller.capability.registry.RuntimeCapabilityRegistry; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.controller.notification.Notification; -import org.jboss.as.controller.notification.NotificationHandlerRegistry; import org.jboss.as.controller.persistence.ConfigurationPersistenceException; import org.jboss.as.controller.persistence.ExtensibleConfigurationPersister; import org.jboss.as.controller.registry.ManagementResourceRegistration; @@ -113,6 +112,7 @@ import org.jboss.as.server.moduleservice.ExtensionIndexService; import org.jboss.as.server.moduleservice.ExternalModule; import org.jboss.as.server.moduleservice.ServiceModuleLoader; +import org.jboss.as.server.suspend.ServerSuspendController; import org.jboss.as.server.suspend.SuspendController; import org.jboss.as.version.Stability; import org.jboss.dmr.ModelNode; @@ -128,6 +128,7 @@ import org.jboss.threads.EnhancedQueueExecutor; import org.jboss.threads.JBossThreadFactory; import org.wildfly.security.manager.WildFlySecurityManager; +import org.wildfly.service.ServiceInstaller; /** * Service for the {@link org.jboss.as.controller.ModelController} for an AS server instance. @@ -158,15 +159,13 @@ public final class ServerService extends AbstractControllerService { private final RunningModeControl runningModeControl; private volatile ExtensibleConfigurationPersister extensibleConfigurationPersister; private final ServerDelegatingResourceDefinition rootResourceDefinition; - private final SuspendController suspendController; + private final ServerSuspendController suspendController; public static final String SERVER_NAME = "server"; - static final String SUSPEND_CONTROLLER_CAPABILITY_NAME = "org.wildfly.server.suspend-controller"; static final String EXTERNAL_MODULE_CAPABILITY_NAME = "org.wildfly.management.external-module"; - static final RuntimeCapability SUSPEND_CONTROLLER_CAPABILITY = - RuntimeCapability.Builder.of(SUSPEND_CONTROLLER_CAPABILITY_NAME, SuspendController.class) - .build(); + // TODO Remove type narrowing as soon as references to the implementation class are dropped from WildFly + static final RuntimeCapability SUSPEND_CONTROLLER_CAPABILITY = RuntimeCapability.Builder.of(ServerSuspendController.SERVICE_DESCRIPTOR.asType(SuspendController.class)).build(); static final RuntimeCapability EXTERNAL_MODULE_CAPABILITY = RuntimeCapability.Builder.of(EXTERNAL_MODULE_CAPABILITY_NAME, ExternalModule.class) @@ -185,7 +184,7 @@ private ServerService(final Supplier executorService, final OperationStepHandler prepareStep, final BootstrapListener bootstrapListener, final ServerDelegatingResourceDefinition rootResourceDefinition, final RunningModeControl runningModeControl, final ManagedAuditLogger auditLogger, final DelegatingConfigurableAuthorizer authorizer, final ManagementSecurityIdentitySupplier securityIdentitySupplier, - final CapabilityRegistry capabilityRegistry, final SuspendController suspendController, final RuntimeExpressionResolver expressionResolver) { + final CapabilityRegistry capabilityRegistry, final ServerSuspendController suspendController, final RuntimeExpressionResolver expressionResolver) { super(executorService, instabilityListener, getProcessType(configuration.getServerEnvironment()), getStability(configuration.getServerEnvironment()), runningModeControl, null, processState, rootResourceDefinition, prepareStep, expressionResolver, auditLogger, authorizer, securityIdentitySupplier, capabilityRegistry, configuration.getServerEnvironment().getConfigurationExtension()); @@ -216,7 +215,7 @@ public static void addService(final ServiceTarget serviceTarget, final Bootstrap final ControlledProcessState processState, final BootstrapListener bootstrapListener, final RunningModeControl runningModeControl, final ManagedAuditLogger auditLogger, final DelegatingConfigurableAuthorizer authorizer, final ManagementSecurityIdentitySupplier securityIdentitySupplier, - final SuspendController suspendController) { + final ServerSuspendController suspendController) { // Install Executor services final String namePattern = "ServerService Thread Pool -- %t"; @@ -287,10 +286,12 @@ public void updateOperationID(final int operationID) { super.getAuditLogger(), getMutableRootResourceRegistrationProvider(), super.getBootErrorCollector(), - configuration.getCapabilityRegistry())); + configuration.getCapabilityRegistry(), + this.suspendController)); super.start(context); } + @Override protected void boot(final BootContext context) throws ConfigurationPersistenceException { boolean ok; Throwable cause = null; @@ -302,24 +303,23 @@ protected void boot(final BootContext context) throws ConfigurationPersistenceEx newExtDirs[extDirs.length] = new File(serverEnvironment.getServerBaseDir(), "lib/ext"); serviceTarget.addService(org.jboss.as.server.deployment.Services.JBOSS_DEPLOYMENT_EXTENSION_INDEX, new ExtensionIndexService(newExtDirs)).setInitialMode(ServiceController.Mode.ON_DEMAND).install(); - final boolean suspend = runningModeControl.getSuspend()!= null ? runningModeControl.getSuspend() : serverEnvironment.isStartSuspended(); + final boolean suspend = runningModeControl.getSuspend() != null ? runningModeControl.getSuspend() : serverEnvironment.isStartSuspended(); final boolean gracefulStartup = serverEnvironment.isStartGracefully(); - suspendController.setStartSuspended(suspend); + this.suspendController.reset(); runningModeControl.setSuspend(false); - if (!gracefulStartup) { - if (suspend) { + if (suspend) { + if (!gracefulStartup) { ServerLogger.ROOT_LOGGER.disregardingNonGraceful(); - } else { - suspendController.nonGracefulStart(); } + ServerLogger.AS_ROOT_LOGGER.startingServerSuspended(); + } else if (!gracefulStartup) { + ServerLogger.ROOT_LOGGER.startingNonGraceful(); + this.suspendController.resume(); } - context.getServiceTarget().addService(SUSPEND_CONTROLLER_CAPABILITY.getCapabilityServiceName(), suspendController) - .install(); + ServiceInstaller.builder(this.suspendController).provides(SUSPEND_CONTROLLER_CAPABILITY.getCapabilityServiceName()).build().install(context.getServiceTarget()); - GracefulShutdownService gracefulShutdownService = new GracefulShutdownService(); - context.getServiceTarget().addService(GracefulShutdownService.SERVICE_NAME, gracefulShutdownService) - .addDependency(SUSPEND_CONTROLLER_CAPABILITY.getCapabilityServiceName(), SuspendController.class, gracefulShutdownService.getSuspendControllerInjectedValue()) - .install(); + GracefulShutdownService gracefulShutdownService = new GracefulShutdownService(this.suspendController); + context.getServiceTarget().addService(GracefulShutdownService.SERVICE_NAME, gracefulShutdownService).install(); // Activate module loader DeployerChainAddHandler.addDeploymentProcessor(SERVER_NAME, Phase.STRUCTURE, Phase.STRUCTURE_SERVICE_MODULE_LOADER, new DeploymentUnitProcessor() { @@ -441,9 +441,11 @@ public void logExit() { } } + @Override protected void finishBoot(boolean suspend) throws ConfigurationPersistenceException { super.finishBoot(); if (!suspend) { + ServerLogger.ROOT_LOGGER.resumingServer(); suspendController.resume(); } } diff --git a/server/src/main/java/org/jboss/as/server/controller/resources/ServerRootResourceDefinition.java b/server/src/main/java/org/jboss/as/server/controller/resources/ServerRootResourceDefinition.java index 0115c7da797..259f37002da 100644 --- a/server/src/main/java/org/jboss/as/server/controller/resources/ServerRootResourceDefinition.java +++ b/server/src/main/java/org/jboss/as/server/controller/resources/ServerRootResourceDefinition.java @@ -117,7 +117,7 @@ import org.jboss.as.server.services.net.SpecifiedInterfaceAddHandler; import org.jboss.as.server.services.net.SpecifiedInterfaceRemoveHandler; import org.jboss.as.server.services.net.SpecifiedInterfaceResolveHandler; -import org.jboss.as.server.suspend.SuspendController; +import org.jboss.as.server.suspend.ServerSuspendController; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; @@ -209,7 +209,7 @@ public class ServerRootResourceDefinition extends SimpleResourceDefinition { .build(); public static final AttributeDefinition SUSPEND_STATE = SimpleAttributeDefinitionBuilder.create(ModelDescriptionConstants.SUSPEND_STATE, ModelType.STRING) - .setValidator(EnumValidator.create(SuspendController.State.class)) + .setValidator(EnumValidator.create(ServerSuspendController.State.class)) .setStorageRuntime() .setRuntimeServiceNotRequired() .build(); @@ -266,6 +266,7 @@ public class ServerRootResourceDefinition extends SimpleResourceDefinition { private final CapabilityRegistry capabilityRegistry; private final MutableRootResourceRegistrationProvider rootResourceRegistrationProvider; private final BootErrorCollector bootErrorCollector; + private final ServerSuspendController suspendController; public ServerRootResourceDefinition( final ContentRepository contentRepository, @@ -282,7 +283,8 @@ public ServerRootResourceDefinition( final ManagedAuditLogger auditLogger, final MutableRootResourceRegistrationProvider rootResourceRegistrationProvider, final BootErrorCollector bootErrorCollector, - final CapabilityRegistry capabilityRegistry) { + final CapabilityRegistry capabilityRegistry, + final ServerSuspendController suspendController) { super(new Parameters(ResourceRegistration.root(), ServerDescriptions.getResourceDescriptionResolver(SERVER, false)) .addCapabilities(PATH_CAPABILITY.fromBaseCapability(ServerEnvironment.HOME_DIR), PATH_CAPABILITY.fromBaseCapability(ServerEnvironment.SERVER_BASE_DIR), @@ -309,6 +311,7 @@ public ServerRootResourceDefinition( this.securityIdentitySupplier = securityIdentitySupplier; this.rootResourceRegistrationProvider = rootResourceRegistrationProvider; this.bootErrorCollector = bootErrorCollector; + this.suspendController = suspendController; } @Override @@ -405,12 +408,12 @@ public void registerOperations(ManagementResourceRegistration resourceRegistrati final ServerDomainProcessReloadHandler reloadHandler = new ServerDomainProcessReloadHandler(Services.JBOSS_AS, runningModeControl, processState, operationIDUpdater, serverEnvironment); resourceRegistration.registerOperationHandler(getDomainServerLifecycleDefinition(RELOAD, ModelType.STRING, null, BLOCKING, START_MODE), reloadHandler); - resourceRegistration.registerOperationHandler(getDomainServerLifecycleDefinition(SUSPEND, null, null, TIMEOUT, SUSPEND_TIMEOUT), ServerSuspendHandler.INSTANCE); + resourceRegistration.registerOperationHandler(getDomainServerLifecycleDefinition(SUSPEND, null, null, TIMEOUT, SUSPEND_TIMEOUT), new ServerSuspendHandler(this.suspendController)); - resourceRegistration.registerOperationHandler(getDomainServerLifecycleDefinition(RESUME, null, null), ServerResumeHandler.INSTANCE); + resourceRegistration.registerOperationHandler(getDomainServerLifecycleDefinition(RESUME, null, null), new ServerResumeHandler(this.suspendController)); // This one is completely private, only for use by the HC - resourceRegistration.registerOperationHandler(ServerDomainProcessShutdownHandler.DOMAIN_DEFINITION, new ServerDomainProcessShutdownHandler()); + resourceRegistration.registerOperationHandler(ServerDomainProcessShutdownHandler.DOMAIN_DEFINITION, new ServerDomainProcessShutdownHandler(this.suspendController)); } else { @@ -418,13 +421,13 @@ public void registerOperations(ManagementResourceRegistration resourceRegistrati ServerProcessReloadHandler.registerStandardReloadOperation(resourceRegistration, runningModeControl, processState, serverEnvironment, extensibleConfigurationPersister); ServerProcessReloadHandler.registerEnhancedReloadOperation(resourceRegistration, runningModeControl, processState, serverEnvironment, extensibleConfigurationPersister); - resourceRegistration.registerOperationHandler(ServerSuspendHandler.DEFINITION, ServerSuspendHandler.INSTANCE); - resourceRegistration.registerOperationHandler(ServerResumeHandler.DEFINITION, ServerResumeHandler.INSTANCE); + resourceRegistration.registerOperationHandler(ServerSuspendHandler.DEFINITION, new ServerSuspendHandler(this.suspendController)); + resourceRegistration.registerOperationHandler(ServerResumeHandler.DEFINITION, new ServerResumeHandler(this.suspendController)); } // The System.exit() based shutdown command is only valid for a server process directly launched from the command line if (serverEnvironment.getLaunchType() == ServerEnvironment.LaunchType.STANDALONE) { - ServerShutdownHandler serverShutdownHandler = new ServerShutdownHandler(processState, serverEnvironment); + ServerShutdownHandler serverShutdownHandler = new ServerShutdownHandler(processState, serverEnvironment, this.suspendController); resourceRegistration.registerOperationHandler(ServerShutdownHandler.DEFINITION, serverShutdownHandler); } @@ -447,7 +450,7 @@ public void registerAttributes(ManagementResourceRegistration resourceRegistrati resourceRegistration.registerReadOnlyAttribute(SERVER_STATE, new ProcessStateAttributeHandler(processState)); resourceRegistration.registerReadOnlyAttribute(PROCESS_TYPE, ProcessTypeHandler.INSTANCE); resourceRegistration.registerReadOnlyAttribute(RUNNING_MODE, new RunningModeReadHandler(runningModeControl)); - resourceRegistration.registerReadOnlyAttribute(SUSPEND_STATE, SuspendStateReadHandler.INSTANCE); + resourceRegistration.registerReadOnlyAttribute(SUSPEND_STATE, new SuspendStateReadHandler(this.suspendController)); resourceRegistration.registerReadOnlyAttribute(UUID, new InstanceUuidReadHandler(serverEnvironment)); diff --git a/server/src/main/java/org/jboss/as/server/jmx/RunningStateJmx.java b/server/src/main/java/org/jboss/as/server/jmx/RunningStateJmx.java index bbfe6c0dcb9..a6d8d4fd9eb 100644 --- a/server/src/main/java/org/jboss/as/server/jmx/RunningStateJmx.java +++ b/server/src/main/java/org/jboss/as/server/jmx/RunningStateJmx.java @@ -25,8 +25,8 @@ import org.jboss.as.controller.ProcessStateNotifier; import org.jboss.as.controller.RunningModeControl; import org.jboss.as.server.logging.ServerLogger; -import org.jboss.as.server.suspend.OperationListener; -import org.jboss.as.server.suspend.SuspendController; +import org.jboss.as.server.suspend.ServerSuspendController; +import org.jboss.as.server.suspend.ServerSuspendListener; import org.wildfly.extension.core.management.client.Process.RunningMode; import org.wildfly.extension.core.management.client.Process.RunningState; import org.wildfly.extension.core.management.client.Process.RuntimeConfigurationState; @@ -145,7 +145,7 @@ public synchronized void setRunningState(RunningState oldState, RunningState new sendNotification(notification); } - public static void registerMBean(ProcessStateNotifier processStateNotifier, SuspendController suspendController, RunningModeControl runningModeControl, boolean isServer) { + public static void registerMBean(ProcessStateNotifier processStateNotifier, ServerSuspendController suspendController, RunningModeControl runningModeControl, boolean isServer) { try { final ObjectName name = new ObjectName(OBJECT_NAME); final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); @@ -156,31 +156,31 @@ public static void registerMBean(ProcessStateNotifier processStateNotifier, Susp server.registerMBean(mbean, name); registerStateListener(mbean, processStateNotifier); if (suspendController != null) { - suspendController.addListener(new OperationListener() { + suspendController.addSuspendListener(new ServerSuspendListener() { @Override public void suspendStarted() { mbean.setRunningState(mbean.getRunningState(), RunningState.SUSPENDING); } @Override - public void complete() { + public void suspendCompleted() { mbean.setRunningState(mbean.getRunningState(), RunningState.SUSPENDED); } @Override - public void cancelled() { - if(mbean.getRunningState() == RunningState.STARTING) { + public void suspendCancelled() { + if (mbean.getRunningState() == RunningState.STARTING) { mbean.setRunningState(RunningState.STARTING, RunningState.SUSPENDED); } if (mbean.getRunningMode() == RunningMode.NORMAL) { mbean.setRunningState(mbean.getRunningState(), RunningState.NORMAL); } else { - mbean.setRunningState(mbean.getRunningState(),RunningState.ADMIN_ONLY); + mbean.setRunningState(mbean.getRunningState(), RunningState.ADMIN_ONLY); } } @Override - public void timeout() { + public void suspendTimeout() { } }); } else { diff --git a/server/src/main/java/org/jboss/as/server/operations/ServerDomainProcessShutdownHandler.java b/server/src/main/java/org/jboss/as/server/operations/ServerDomainProcessShutdownHandler.java index d1e9652f2c7..c5f3e12a07d 100644 --- a/server/src/main/java/org/jboss/as/server/operations/ServerDomainProcessShutdownHandler.java +++ b/server/src/main/java/org/jboss/as/server/operations/ServerDomainProcessShutdownHandler.java @@ -5,12 +5,11 @@ package org.jboss.as.server.operations; - -import static org.jboss.as.server.Services.JBOSS_SUSPEND_CONTROLLER; import static org.jboss.as.server.controller.resources.ServerRootResourceDefinition.SUSPEND_TIMEOUT; import static org.jboss.as.server.controller.resources.ServerRootResourceDefinition.TIMEOUT; import static org.jboss.as.server.controller.resources.ServerRootResourceDefinition.renameTimeoutToSuspendTimeout; +import java.time.Duration; import java.util.EnumSet; import org.jboss.as.controller.OperationContext; @@ -23,7 +22,7 @@ import org.jboss.as.controller.registry.OperationEntry; import org.jboss.as.server.GracefulShutdownService; import org.jboss.as.server.controller.descriptions.ServerDescriptions; -import org.jboss.as.server.suspend.SuspendController; +import org.jboss.as.server.suspend.ServerSuspendController; import org.jboss.dmr.ModelNode; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceRegistry; @@ -38,18 +37,25 @@ */ public class ServerDomainProcessShutdownHandler implements OperationStepHandler { - public static final SimpleOperationDefinition DOMAIN_DEFINITION = new SimpleOperationDefinitionBuilder(ModelDescriptionConstants.SHUTDOWN, ServerDescriptions.getResourceDescriptionResolver()) .setParameters(TIMEOUT, SUSPEND_TIMEOUT) .setPrivateEntry() .withFlags(OperationEntry.Flag.HOST_CONTROLLER_ONLY, OperationEntry.Flag.RUNTIME_ONLY) .build(); + private final ServerSuspendController suspendController; + + public ServerDomainProcessShutdownHandler(ServerSuspendController suspendController) { + this.suspendController = suspendController; + } + @Override public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException { context.acquireControllerLock(); renameTimeoutToSuspendTimeout(operation); - final int timeout = SUSPEND_TIMEOUT.resolveModelAttribute(context, operation).asInt(); //in milliseconds, as this is what is passed in from the HC + final int seconds = SUSPEND_TIMEOUT.resolveModelAttribute(context, operation).asInt(); //in milliseconds, as this is what is passed in from the HC + Duration timeout = (seconds >= 0) ? Duration.ofSeconds(seconds) : null; + // Acquire the controller lock to prevent new write ops and wait until current ones are done context.acquireControllerLock(); context.addStep(new OperationStepHandler() { @@ -77,11 +83,8 @@ public void handleResult(OperationContext.ResultAction resultAction, OperationCo // is already shutting down due to receiving a SIGINT final ServiceController gracefulController = (ServiceController) registry.getService(GracefulShutdownService.SERVICE_NAME); if (gracefulController != null) { - final ServiceController suspendControllerServiceController = (ServiceController) registry.getService(JBOSS_SUSPEND_CONTROLLER); - if (suspendControllerServiceController != null) { - gracefulController.getValue().startGracefulShutdown(); - suspendControllerServiceController.getValue().suspend(timeout > 0 ? timeout * 1000 : timeout); - } + gracefulController.getValue().startGracefulShutdown(); + ServerDomainProcessShutdownHandler.this.suspendController.suspend(timeout); } } } diff --git a/server/src/main/java/org/jboss/as/server/operations/ServerResumeHandler.java b/server/src/main/java/org/jboss/as/server/operations/ServerResumeHandler.java index 05ca0c6e0bf..89ad4675d11 100644 --- a/server/src/main/java/org/jboss/as/server/operations/ServerResumeHandler.java +++ b/server/src/main/java/org/jboss/as/server/operations/ServerResumeHandler.java @@ -8,7 +8,6 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESUME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNNING_SERVER; -import static org.jboss.as.server.Services.JBOSS_SUSPEND_CONTROLLER; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationFailedException; @@ -16,10 +15,9 @@ import org.jboss.as.controller.SimpleOperationDefinition; import org.jboss.as.controller.SimpleOperationDefinitionBuilder; import org.jboss.as.server.controller.descriptions.ServerDescriptions; -import org.jboss.as.server.suspend.SuspendController; +import org.jboss.as.server.logging.ServerLogger; +import org.jboss.as.server.suspend.ServerSuspendController; import org.jboss.dmr.ModelNode; -import org.jboss.msc.service.ServiceController; -import org.jboss.msc.service.ServiceRegistry; /** * Handler that suspends server operations @@ -28,12 +26,16 @@ */ public class ServerResumeHandler implements OperationStepHandler { - public static final ServerResumeHandler INSTANCE = new ServerResumeHandler(); + private final ServerSuspendController suspendController; public static final SimpleOperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(RESUME, ServerDescriptions.getResourceDescriptionResolver(RUNNING_SERVER)) .setRuntimeOnly() .build(); + public ServerResumeHandler(ServerSuspendController suspendController) { + this.suspendController = suspendController; + } + /** * {@inheritDoc} */ @@ -44,17 +46,12 @@ public void execute(OperationContext context, ModelNode operation) throws Operat context.addStep(new OperationStepHandler() { @Override public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { - // Even though we don't read from the service registry, we have a reference to a - // service-fronted 'processState' so tell the controller we are modifying a service - final ServiceRegistry registry = context.getServiceRegistry(true); context.completeStep(new OperationContext.ResultHandler() { @Override public void handleResult(OperationContext.ResultAction resultAction, OperationContext context, ModelNode operation) { - if(resultAction == OperationContext.ResultAction.KEEP) { - //even if the timeout is zero we still pause the server - //to stop new requests being accepted as it is shutting down - ServiceController shutdownController = (ServiceController) registry.getRequiredService(JBOSS_SUSPEND_CONTROLLER); - shutdownController.getValue().resume(); + if (resultAction == OperationContext.ResultAction.KEEP) { + ServerLogger.ROOT_LOGGER.resumingServer(); + ServerResumeHandler.this.suspendController.resume(); } } }); diff --git a/server/src/main/java/org/jboss/as/server/operations/ServerShutdownHandler.java b/server/src/main/java/org/jboss/as/server/operations/ServerShutdownHandler.java index c011c4e73ae..7b9fbbce226 100644 --- a/server/src/main/java/org/jboss/as/server/operations/ServerShutdownHandler.java +++ b/server/src/main/java/org/jboss/as/server/operations/ServerShutdownHandler.java @@ -10,7 +10,6 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNNING_SERVER; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SHUTDOWN; -import static org.jboss.as.server.Services.JBOSS_SUSPEND_CONTROLLER; import static org.jboss.as.server.controller.resources.ServerRootResourceDefinition.SUSPEND_TIMEOUT; import static org.jboss.as.server.controller.resources.ServerRootResourceDefinition.TIMEOUT; import static org.jboss.as.server.controller.resources.ServerRootResourceDefinition.renameTimeoutToSuspendTimeout; @@ -19,6 +18,7 @@ import java.io.FileInputStream; import java.io.FileReader; import java.nio.file.Path; +import java.time.Duration; import java.util.EnumSet; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; @@ -41,13 +41,11 @@ import org.jboss.as.server.SystemExiter; import org.jboss.as.server.controller.descriptions.ServerDescriptions; import org.jboss.as.server.logging.ServerLogger; -import org.jboss.as.server.suspend.OperationListener; -import org.jboss.as.server.suspend.SuspendController; -import org.jboss.as.server.suspend.SuspendController.State; +import org.jboss.as.server.suspend.ServerSuspendController; +import org.jboss.as.server.suspend.ServerSuspendController.State; +import org.jboss.as.server.suspend.ServerSuspendListener; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; -import org.jboss.msc.service.ServiceController; -import org.jboss.msc.service.ServiceRegistry; /** * Handler that shuts down the standalone server. @@ -77,10 +75,12 @@ public class ServerShutdownHandler implements OperationStepHandler { private final ControlledProcessState processState; private final ServerEnvironment environment; + private final ServerSuspendController suspendController; - public ServerShutdownHandler(ControlledProcessState processState, ServerEnvironment serverEnvironment) { + public ServerShutdownHandler(ControlledProcessState processState, ServerEnvironment serverEnvironment, ServerSuspendController suspendController) { this.processState = processState; this.environment = serverEnvironment; + this.suspendController = suspendController; } /** @@ -90,7 +90,8 @@ public ServerShutdownHandler(ControlledProcessState processState, ServerEnvironm public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { renameTimeoutToSuspendTimeout(operation); final boolean restart = RESTART.resolveModelAttribute(context, operation).asBoolean(); - final int timeout = SUSPEND_TIMEOUT.resolveModelAttribute(context, operation).asInt(); //in seconds, need to convert to ms + final int seconds = SUSPEND_TIMEOUT.resolveModelAttribute(context, operation).asInt(); + Duration timeout = (seconds >= 0) ? Duration.ofSeconds(seconds) : null; final boolean performInstallation = PERFORM_INSTALLATION.resolveModelAttribute(context, operation).asBoolean(); // Verify the candidate server is prepared @@ -148,39 +149,37 @@ public void handleResult(OperationContext.ResultAction resultAction, OperationCo //even if the timeout is zero we still pause the server //to stop new requests being accepted as it is shutting down final ShutdownAction shutdown = new ShutdownAction(getOperationName(operation), restart, performInstallation); - final ServiceRegistry registry = context.getServiceRegistry(false); - final ServiceController suspendControllerServiceController = (ServiceController) registry.getRequiredService(JBOSS_SUSPEND_CONTROLLER); - final SuspendController suspendController = suspendControllerServiceController.getValue(); + final ServerSuspendController suspendController = ServerShutdownHandler.this.suspendController; if (suspendController.getState() == State.SUSPENDED) { // WFCORE-4935 if server is already in suspend, don't register any listener, just shut it down shutdown.shutdown(); } else { - OperationListener listener = new OperationListener() { + ServerSuspendListener listener = new ServerSuspendListener() { @Override public void suspendStarted() { } @Override - public void complete() { - suspendController.removeListener(this); + public void suspendCompleted() { + suspendController.removeSuspendListener(this); shutdown.shutdown(); } @Override - public void cancelled() { - suspendController.removeListener(this); + public void suspendCancelled() { + suspendController.removeSuspendListener(this); shutdown.cancel(); } @Override - public void timeout() { - suspendController.removeListener(this); + public void suspendTimeout() { + suspendController.removeSuspendListener(this); shutdown.shutdown(); } }; - suspendController.addListener(listener); - suspendController.suspend(timeout > 0 ? timeout * 1000 : timeout); + suspendController.addSuspendListener(listener); + suspendController.suspend(timeout); } } } diff --git a/server/src/main/java/org/jboss/as/server/operations/ServerSuspendHandler.java b/server/src/main/java/org/jboss/as/server/operations/ServerSuspendHandler.java index 63405f14c20..b6aff4671de 100644 --- a/server/src/main/java/org/jboss/as/server/operations/ServerSuspendHandler.java +++ b/server/src/main/java/org/jboss/as/server/operations/ServerSuspendHandler.java @@ -7,11 +7,11 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNNING_SERVER; -import static org.jboss.as.server.Services.JBOSS_SUSPEND_CONTROLLER; import static org.jboss.as.server.controller.resources.ServerRootResourceDefinition.SUSPEND_TIMEOUT; import static org.jboss.as.server.controller.resources.ServerRootResourceDefinition.TIMEOUT; import static org.jboss.as.server.controller.resources.ServerRootResourceDefinition.renameTimeoutToSuspendTimeout; +import java.time.Duration; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; @@ -22,11 +22,10 @@ import org.jboss.as.controller.SimpleOperationDefinitionBuilder; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.server.controller.descriptions.ServerDescriptions; -import org.jboss.as.server.suspend.OperationListener; -import org.jboss.as.server.suspend.SuspendController; +import org.jboss.as.server.logging.ServerLogger; +import org.jboss.as.server.suspend.ServerSuspendController; +import org.jboss.as.server.suspend.ServerSuspendListener; import org.jboss.dmr.ModelNode; -import org.jboss.msc.service.ServiceController; -import org.jboss.msc.service.ServiceRegistry; /** * Handler that suspends server operations @@ -35,14 +34,18 @@ */ public class ServerSuspendHandler implements OperationStepHandler { - public static final ServerSuspendHandler INSTANCE = new ServerSuspendHandler(); - public static final SimpleOperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(ModelDescriptionConstants.SUSPEND, ServerDescriptions.getResourceDescriptionResolver(RUNNING_SERVER)) .setParameters(TIMEOUT, SUSPEND_TIMEOUT) .setRuntimeOnly() .build(); + private final ServerSuspendController suspendController; + + public ServerSuspendHandler(ServerSuspendController suspendController) { + this.suspendController = suspendController; + } + /** * {@inheritDoc} */ @@ -51,53 +54,53 @@ public void execute(final OperationContext context, ModelNode operation) throws // Acquire the controller lock to prevent new write ops and wait until current ones are done context.acquireControllerLock(); renameTimeoutToSuspendTimeout(operation); - final int timeout = SUSPEND_TIMEOUT.resolveModelAttribute(context, operation).asInt(); + int seconds = SUSPEND_TIMEOUT.resolveModelAttribute(context, operation).asInt(); + Duration timeout = (seconds >= 0) ? Duration.ofSeconds(seconds) : null; context.addStep(new OperationStepHandler() { @Override public void execute(final OperationContext context, ModelNode operation) throws OperationFailedException { - final ServiceRegistry registry = context.getServiceRegistry(false); - ServiceController suspendControllerServiceController = (ServiceController) registry.getRequiredService(JBOSS_SUSPEND_CONTROLLER); - final SuspendController suspendController = suspendControllerServiceController.getValue(); + final ServerSuspendController suspendController = ServerSuspendHandler.this.suspendController; final CountDownLatch latch = new CountDownLatch(1); final AtomicBoolean cancelled = new AtomicBoolean(); - OperationListener operationListener = new OperationListener() { + ServerSuspendListener listener = new ServerSuspendListener() { @Override public void suspendStarted() { } @Override - public void complete() { - suspendController.removeListener(this); + public void suspendCompleted() { + suspendController.removeSuspendListener(this); latch.countDown(); } @Override - public void cancelled() { - suspendController.removeListener(this); + public void suspendCancelled() { + suspendController.removeSuspendListener(this); cancelled.set(true); latch.countDown(); } @Override - public void timeout() { - suspendController.removeListener(this); + public void suspendTimeout() { + suspendController.removeSuspendListener(this); latch.countDown(); } }; - suspendController.addListener(operationListener); - suspendController.suspend(timeout > 0 ? timeout * 1000 : timeout); - if(timeout != 0 && suspendController.getState() != SuspendController.State.SUSPENDED) { + suspendController.addSuspendListener(listener); + suspendController.suspend(timeout); + boolean waitForSuspend = (timeout == null) || !timeout.isZero(); + if (waitForSuspend && suspendController.getState() != ServerSuspendController.State.SUSPENDED) { try { latch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } - if(cancelled.get()) { + if (cancelled.get()) { context.setRollbackOnly(); } context.completeStep(new RollbackHandler(suspendController)); @@ -108,15 +111,16 @@ public void timeout() { private static final class RollbackHandler implements OperationContext.ResultHandler { - private final SuspendController controller; + private final ServerSuspendController controller; - private RollbackHandler(SuspendController controller) { + private RollbackHandler(ServerSuspendController controller) { this.controller = controller; } @Override public void handleResult(OperationContext.ResultAction resultAction, OperationContext context, ModelNode operation) { - if(resultAction == OperationContext.ResultAction.ROLLBACK) { + if (resultAction == OperationContext.ResultAction.ROLLBACK) { + ServerLogger.ROOT_LOGGER.resumingServer(); controller.resume(); } } diff --git a/server/src/main/java/org/jboss/as/server/operations/SuspendStateReadHandler.java b/server/src/main/java/org/jboss/as/server/operations/SuspendStateReadHandler.java index a5cccbccdf0..757f74b4887 100644 --- a/server/src/main/java/org/jboss/as/server/operations/SuspendStateReadHandler.java +++ b/server/src/main/java/org/jboss/as/server/operations/SuspendStateReadHandler.java @@ -5,42 +5,28 @@ package org.jboss.as.server.operations; -import static org.jboss.as.server.Services.JBOSS_SUSPEND_CONTROLLER; - import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.OperationStepHandler; -import org.jboss.as.server.suspend.SuspendController; +import org.jboss.as.server.suspend.ServerSuspendController; import org.jboss.dmr.ModelNode; -import org.jboss.msc.service.ServiceController; /** - * Reports the current server {@link org.jboss.as.server.suspend.SuspendController.State} + * Reports the current server {@link org.jboss.as.server.suspend.ServerSuspendController.State} * * @author Stuart Douglas */ public class SuspendStateReadHandler implements OperationStepHandler { - public static final SuspendStateReadHandler INSTANCE = new SuspendStateReadHandler(); + private final ServerSuspendController suspendController; - private SuspendStateReadHandler(){} + public SuspendStateReadHandler(ServerSuspendController suspendController) { + this.suspendController = suspendController; + } @Override public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { - @SuppressWarnings("unchecked") - ServiceController sc = (ServiceController) context.getServiceRegistry(false).getService(JBOSS_SUSPEND_CONTROLLER); - SuspendController.State state; - if(sc != null) { - state = sc.getValue().getState(); - } else { - // Either we haven't installed the SC yet or we're stopping and it's been removed - // If we haven't installed, when we do its initial state is SUSPENDED - // If it's been removed, it's last state was SUSPENDED. - // So, report that. - state = SuspendController.State.SUSPENDED; - } + ServerSuspendController.State state = (this.suspendController != null) ? this.suspendController.getState() : ServerSuspendController.State.SUSPENDED; context.getResult().set(state.name()); } - - } diff --git a/server/src/main/java/org/jboss/as/server/suspend/CountingRequestCountCallback.java b/server/src/main/java/org/jboss/as/server/suspend/CountingRequestCountCallback.java index ec7adc21911..9a8083a989d 100644 --- a/server/src/main/java/org/jboss/as/server/suspend/CountingRequestCountCallback.java +++ b/server/src/main/java/org/jboss/as/server/suspend/CountingRequestCountCallback.java @@ -7,8 +7,7 @@ import java.util.concurrent.atomic.AtomicInteger; /** - * RequestCountListener that till n notification have been received before notifying - * its delegate. + * RequestCountListener that waits until a specified number of calls to {@link #done()} have been received before invoking {@link #done()} on its delegate. * * @author Stuart Douglas */ @@ -26,10 +25,9 @@ public CountingRequestCountCallback(int count, ServerActivityCallback delegate) @Override public void done() { if (count.decrementAndGet() == 0) { - if(delegate != null) { + if (delegate != null) { delegate.done(); } } } - } diff --git a/server/src/main/java/org/jboss/as/server/suspend/OperationListener.java b/server/src/main/java/org/jboss/as/server/suspend/OperationListener.java index 51368ab6b0d..1eec38d1d64 100644 --- a/server/src/main/java/org/jboss/as/server/suspend/OperationListener.java +++ b/server/src/main/java/org/jboss/as/server/suspend/OperationListener.java @@ -11,7 +11,9 @@ * suspend operation. * * @author Stuart Douglas + * @deprecated Use {@link ServerSuspendListener} instead. */ +@Deprecated(forRemoval = true) public interface OperationListener { /** diff --git a/server/src/main/java/org/jboss/as/server/suspend/ServerActivity.java b/server/src/main/java/org/jboss/as/server/suspend/ServerActivity.java index ca3dbbe5fe9..4679d98f3ca 100644 --- a/server/src/main/java/org/jboss/as/server/suspend/ServerActivity.java +++ b/server/src/main/java/org/jboss/as/server/suspend/ServerActivity.java @@ -30,7 +30,7 @@ public interface ServerActivity { /** * Returns a value that indicates to which set of {@code ServerActivity} instances - * {@link SuspendController#registerActivity(ServerActivity) registered} with the {@link SuspendController} + * {@link ServerActivityRegistry#addServerActivity(ServerActivity) registered} with the {@link ServerActivityRegistry} * this activity should belong. All {@code ServerActivity} instances with the same execution group value have their * {@link #preSuspend(ServerActivityCallback) preSuspend}, {@link #suspended(ServerActivityCallback) suspended} * and {@link #resume() resume} methods invoked separately from activities with different execution group values. diff --git a/server/src/main/java/org/jboss/as/server/suspend/ServerActivityRegistry.java b/server/src/main/java/org/jboss/as/server/suspend/ServerActivityRegistry.java new file mode 100644 index 00000000000..2f0e714cf85 --- /dev/null +++ b/server/src/main/java/org/jboss/as/server/suspend/ServerActivityRegistry.java @@ -0,0 +1,31 @@ +/* +`` * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.jboss.as.server.suspend; + +import org.wildfly.service.descriptor.NullaryServiceDescriptor; + +/** + * A registrar of server activity. + */ +public interface ServerActivityRegistry { + NullaryServiceDescriptor SERVICE_DESCRIPTOR = NullaryServiceDescriptor.of("org.wildfly.server.suspend-controller", ServerActivityRegistry.class); + + /** + * Adds the specified {@link ServerActivity} to this registry. + * @param activity server activity that should complete prior to suspending the server + * @throws NullPointerException if {@code activity} is null + * @throws IllegalArgumentException if {@link ServerActivity#getExecutionGroup() getExecutionGroup()} is invalid. + */ + void addServerActivity(ServerActivity activity); + + /** + * Removes the specified {@link ServerActivity} from this registry. + * @param activity server activity that should complete prior to suspending the server + * @throws NullPointerException if {@code activity} is null + * @throws IllegalArgumentException if {@link ServerActivity#getExecutionGroup() getExecutionGroup()} is invalid. + */ + void removeServerActivity(ServerActivity activity); +} diff --git a/server/src/main/java/org/jboss/as/server/suspend/ServerSuspendController.java b/server/src/main/java/org/jboss/as/server/suspend/ServerSuspendController.java new file mode 100644 index 00000000000..0b99cc4d466 --- /dev/null +++ b/server/src/main/java/org/jboss/as/server/suspend/ServerSuspendController.java @@ -0,0 +1,64 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.jboss.as.server.suspend; + +import java.time.Duration; + +import org.wildfly.service.descriptor.NullaryServiceDescriptor; + +public interface ServerSuspendController extends ServerActivityRegistry { + NullaryServiceDescriptor SERVICE_DESCRIPTOR = ServerActivityRegistry.SERVICE_DESCRIPTOR.asType(ServerSuspendController.class); + + enum State { + RUNNING, + PRE_SUSPEND, + SUSPENDING, + SUSPENDED + } + + /** + * Suspend the server after waiting for {@link ServerActivity#preSuspend(ServerActivityCallback)} and {@link ServerActivity#suspended(ServerActivityCallback)} completion for all registered server activity. + */ + default void suspend() { + this.suspend(null); + } + + /** + * Suspend the server, waiting for {@link ServerActivity#preSuspend(ServerActivityCallback)} and {@link ServerActivity#suspended(ServerActivityCallback)} completion for all registered server activity, + * or after the specified timeout (if not null) has elapsed. + * @param timeout the duration of time to wait for pre-suspend activity to complete before suspending the server, or null to wait indefinitely. + * @throws IllegalArgumentException if timeout is negative + */ + void suspend(Duration timeout); + + /** + * Resumes the server, invoking {@link ServerActivity#resume()} for all registered activity. + */ + void resume(); + + /** + * Resets the state of the suspend controller. + */ + void reset(); + + /** + * Returns the state of this controller. + * @return the suspend controller state. + */ + State getState(); + + /** + * Adds the specified server suspension event listener to this controller. + * @param listener a server suspension event listener + */ + void addSuspendListener(ServerSuspendListener listener); + + /** + * Removes the specified server suspension event listener from this controller. + * @param listener a server suspension event listener + */ + void removeSuspendListener(ServerSuspendListener listener); +} diff --git a/server/src/main/java/org/jboss/as/server/suspend/ServerSuspendListener.java b/server/src/main/java/org/jboss/as/server/suspend/ServerSuspendListener.java new file mode 100644 index 00000000000..ebd827560d3 --- /dev/null +++ b/server/src/main/java/org/jboss/as/server/suspend/ServerSuspendListener.java @@ -0,0 +1,32 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.jboss.as.server.suspend; + +/** + * Server suspend event listener. + */ +public interface ServerSuspendListener { + + /** + * Invoked when a suspend operation starts. + */ + void suspendStarted(); + + /** + * Invoked when a suspend operation completes successfully. + */ + void suspendCompleted(); + + /** + * Invoked upon canceling server suspension, i.e. resuming the server. + */ + void suspendCancelled(); + + /** + * Invoked when a suspend operation completes due to timeout, i.e. before registered server activity preparation completes. + */ + void suspendTimeout(); +} diff --git a/server/src/main/java/org/jboss/as/server/suspend/SuspendController.java b/server/src/main/java/org/jboss/as/server/suspend/SuspendController.java index 287d9062631..2a47badcbd5 100644 --- a/server/src/main/java/org/jboss/as/server/suspend/SuspendController.java +++ b/server/src/main/java/org/jboss/as/server/suspend/SuspendController.java @@ -5,8 +5,10 @@ package org.jboss.as.server.suspend; +import java.time.Duration; import java.util.ArrayList; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.NavigableMap; import java.util.Timer; @@ -15,11 +17,6 @@ import java.util.function.BiConsumer; import org.jboss.as.server.logging.ServerLogger; -import org.jboss.msc.service.Service; -import org.jboss.msc.service.StartContext; -import org.jboss.msc.service.StartException; -import org.jboss.msc.service.StopContext; -import org.jboss.msc.value.InjectedValue; import org.wildfly.common.Assert; /** @@ -33,49 +30,66 @@ * * @author Stuart Douglas */ -public class SuspendController implements Service { +public class SuspendController implements ServerSuspendController { + private static final ServerActivityCallback NO_OP = new ServerActivityCallback() { + @Override + public void done() { + // Do nothing + } + }; /** * Timer that handles the timeout. We create it on pause, rather than leaving it hanging round. */ private Timer timer; - private State state = State.SUSPENDED; + private volatile State state = State.SUSPENDED; private final NavigableMap> activitiesByGroup = new TreeMap<>(); - private final List operationListeners = new ArrayList<>(); + private final List listeners = new LinkedList<>(); private int groupsCount; - private boolean startSuspended; - private final ServerActivityCallback listener = this::activityPaused; - public SuspendController() { - this.startSuspended = false; + @Override + public void reset() { + this.state = State.SUSPENDED; } - public void setStartSuspended(boolean startSuspended) { - //TODO: it is not very clear what this boolean stands for now. - this.startSuspended = startSuspended; - state = State.SUSPENDED; + /** + * @deprecated Use {@link #suspend()} or {@link #suspend(Duration)} instead. + */ + @Deprecated(forRemoval = true) + public void suspend(long timeoutMillis) { + if (timeoutMillis >= 0) { + this.suspend(Duration.ofMillis(timeoutMillis)); + } else { + this.suspend(); + } } - public synchronized void suspend(long timeoutMillis) { - if(state == State.SUSPENDED) { + @Override + public synchronized void suspend(Duration timeout) { + if (state == State.SUSPENDED) { return; } - if (timeoutMillis > 0) { - ServerLogger.ROOT_LOGGER.suspendingServer(timeoutMillis); - } else if (timeoutMillis < 0) { - ServerLogger.ROOT_LOGGER.suspendingServerWithNoTimeout(); + if (timeout != null) { + if (timeout.isNegative()) { + throw new IllegalArgumentException(timeout.toString()); + } + if (timeout.isZero()) { + ServerLogger.ROOT_LOGGER.suspendingServer(); + } else { + ServerLogger.ROOT_LOGGER.suspendingServer(timeout.toMillis()); + } } else { - ServerLogger.ROOT_LOGGER.suspendingServer(); + ServerLogger.ROOT_LOGGER.suspendingServerWithNoTimeout(); } state = State.PRE_SUSPEND; - //we iterate a copy, in case a listener tries to register a new listener - for(OperationListener listener: new ArrayList<>(operationListeners)) { + // Iterate over a copy, in case a listener tries to register a new listener + for (ServerSuspendListener listener: List.copyOf(this.listeners)) { listener.suspendStarted(); } groupsCount = activitiesByGroup.size(); @@ -104,44 +118,43 @@ public synchronized void suspend(long timeoutMillis) { } }, preSuspendGroupCallBack); - if (timeoutMillis > 0) { - timer = new Timer(); - timer.schedule(new TimerTask() { - @Override - public void run() { - timeout(); - } - }, timeoutMillis); - } else if (timeoutMillis == 0) { - timeout(); + if (timeout != null) { + if (timeout.isZero()) { + this.timeout(); + } else { + timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + SuspendController.this.timeout(); + } + }, timeout.toMillis()); + } } } } + /** + * @deprecated Use {@link #resume()} instead. + */ + @Deprecated(forRemoval = true) public void nonGracefulStart() { - resume(false); + this.resume(); } - public void resume() { - resume(true); - } - - private synchronized void resume(boolean gracefulStart) { + @Override + public synchronized void resume() { if (state == State.RUNNING) { return; } - if (!gracefulStart) { - ServerLogger.ROOT_LOGGER.startingNonGraceful(); - } else { - ServerLogger.ROOT_LOGGER.resumingServer(); - } if (timer != null) { timer.cancel(); timer = null; } - for (OperationListener listener : new ArrayList<>(operationListeners)) { - listener.cancelled(); + // Iterate over a copy, in case a listener tries to register a new listener + for (ServerSuspendListener listener: List.copyOf(this.listeners)) { + listener.suspendCancelled(); } for (List executionGroup : activitiesByGroup.descendingMap().values()) { for (ServerActivity activity : executionGroup) { @@ -161,24 +174,38 @@ private synchronized void resume(boolean gracefulStart) { * @throws IllegalArgumentException if {@code activity} is {@code null} of if its * {@link ServerActivity#getExecutionGroup() getExecutionGroup()} method * returns a value outside of that method's documented legal range. + * @deprecated Use {@link #addServerActivity(ServerActivity)} instead. */ - public synchronized void registerActivity(final ServerActivity activity) { + @Deprecated(forRemoval = true) + public void registerActivity(final ServerActivity activity) { + this.addServerActivity(activity); + } + + /** + * @deprecated Use {@link #removeServerActivity(ServerActivity)} instead. + */ + @Deprecated(forRemoval = true) + public void unRegisterActivity(final ServerActivity activity) { + this.removeServerActivity(activity); + } + + @Override + public synchronized void addServerActivity(ServerActivity activity) { Assert.checkNotNullParam("activity", activity); Assert.checkMinimumParameter("activity.getExecutionGroup()", ServerActivity.LOWEST_EXECUTION_GROUP, activity.getExecutionGroup()); Assert.checkMaximumParameter("activity.getExecutionGroup()", ServerActivity.HIGHEST_EXECUTION_GROUP, activity.getExecutionGroup()); List executionGroup = this.activitiesByGroup.computeIfAbsent(activity.getExecutionGroup(), ArrayList::new); executionGroup.add(activity); - if(state != State.RUNNING) { + if (state != State.RUNNING) { //if the activity is added when we are not running we just immediately suspend it //this should only happen at boot, so there should be no outstanding requests anyway // note that this means there is no execution group grouping of these calls. - activity.suspended(() -> { - - }); + activity.suspended(NO_OP); } } - public synchronized void unRegisterActivity(final ServerActivity activity) { + @Override + public synchronized void removeServerActivity(ServerActivity activity) { List executionGroup = activitiesByGroup.get(activity.getExecutionGroup()); if (executionGroup != null) { executionGroup.remove(activity); @@ -189,16 +216,6 @@ public synchronized void unRegisterActivity(final ServerActivity activity) { } @Override - public synchronized void start(StartContext startContext) throws StartException { - if(startSuspended) { - ServerLogger.AS_ROOT_LOGGER.startingServerSuspended(); - } - } - - @Override - public synchronized void stop(StopContext stopContext) { - } - public State getState() { return state; } @@ -215,9 +232,9 @@ private void handlePause() { timer.cancel(); timer = null; } - - for(OperationListener listener: new ArrayList<>(operationListeners)) { - listener.complete(); + // Iterate over a copy, in case a listener tries to register a new listener + for (ServerSuspendListener listener: List.copyOf(this.listeners)) { + listener.suspendCompleted(); } } } @@ -227,23 +244,36 @@ private synchronized void timeout() { timer.cancel(); timer = null; } - for(OperationListener listener: new ArrayList<>(operationListeners)) { - listener.timeout(); + // Iterate over a copy, in case a listener tries to register a new listener + for (ServerSuspendListener listener: List.copyOf(this.listeners)) { + listener.suspendTimeout(); } } + @Override + public synchronized void addSuspendListener(ServerSuspendListener listener) { + this.listeners.add(listener); + } - public synchronized void addListener(final OperationListener listener) { - operationListeners.add(listener); + @Override + public synchronized void removeSuspendListener(ServerSuspendListener listener) { + this.listeners.remove(listener); } - public synchronized void removeListener(final OperationListener listener) { - operationListeners.remove(listener); + /** + * @deprecated Use {@link #addSuspendListener(ServerSuspendListener)} instead. + */ + @Deprecated(forRemoval = true) + public void addListener(final OperationListener listener) { + this.addSuspendListener(new LegacyServerSuspendListener(listener)); } - @Override - public SuspendController getValue() throws IllegalStateException, IllegalArgumentException { - return this; + /** + * @deprecated Use {@link #removeSuspendListener(ServerSuspendListener)} instead. + */ + @Deprecated(forRemoval = true) + public void removeListener(final OperationListener listener) { + this.removeSuspendListener(new LegacyServerSuspendListener(listener)); } private void processGroups(Iterator> iterator, @@ -262,10 +292,50 @@ private void processGroups(Iterator> iterator, } } - public enum State { - RUNNING, - PRE_SUSPEND, - SUSPENDING, - SUSPENDED + @Deprecated + private static class LegacyServerSuspendListener implements ServerSuspendListener { + private final OperationListener listener; + + LegacyServerSuspendListener(OperationListener listener) { + this.listener = listener; + } + + @Override + public void suspendStarted() { + this.listener.suspendStarted(); + } + + @Override + public void suspendCompleted() { + this.listener.complete(); + } + + @Override + public void suspendCancelled() { + this.listener.cancelled(); + } + + @Override + public void suspendTimeout() { + this.listener.timeout(); + } + + @Override + public int hashCode() { + return this.listener.hashCode(); + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (!(object instanceof LegacyServerSuspendListener)) return false; + LegacyServerSuspendListener listener = (LegacyServerSuspendListener) object; + return this.listener.equals(listener.listener); + } + + @Override + public String toString() { + return this.listener.toString(); + } } } diff --git a/server/src/test/java/org/jboss/as/server/suspend/SuspendControllerTestCase.java b/server/src/test/java/org/jboss/as/server/suspend/SuspendControllerTestCase.java index 51ad5c24cfc..f0ec3b0858b 100644 --- a/server/src/test/java/org/jboss/as/server/suspend/SuspendControllerTestCase.java +++ b/server/src/test/java/org/jboss/as/server/suspend/SuspendControllerTestCase.java @@ -16,7 +16,7 @@ import org.junit.Test; /** - * Unit tests of {@link SuspendController}. + * Unit tests of {@link ServerSuspendController}. */ public class SuspendControllerTestCase { @@ -33,16 +33,16 @@ public void testServerActivityCallbackOrder() { } private void serverActivityCallbackOrderTest(CounterActivity... activities) { - SuspendController testee = new SuspendController(); + ServerSuspendController testee = new SuspendController(); testee.resume(); NavigableSet activitySet = new TreeSet<>(); for (CounterActivity activity : activities) { - testee.registerActivity(activity); + testee.addServerActivity(activity); activitySet.add(activity); } - testee.suspend(-1); + testee.suspend(); testee.resume(); orderCheck(activitySet); @@ -50,12 +50,12 @@ private void serverActivityCallbackOrderTest(CounterActivity... activities) { // Randomly unregister some activities for (CounterActivity activity : activities) { if (Math.random() < 0.5) { - testee.unRegisterActivity(activity); + testee.removeServerActivity(activity); assertTrue(activitySet.remove(activity)); } } - testee.suspend(-1); + testee.suspend(); testee.resume(); orderCheck(activitySet); diff --git a/server/src/test/java/org/jboss/as/server/test/InterfaceManagementUnitTestCase.java b/server/src/test/java/org/jboss/as/server/test/InterfaceManagementUnitTestCase.java index 938448cfb00..2acfbfd3637 100644 --- a/server/src/test/java/org/jboss/as/server/test/InterfaceManagementUnitTestCase.java +++ b/server/src/test/java/org/jboss/as/server/test/InterfaceManagementUnitTestCase.java @@ -7,6 +7,7 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ANY_ADDRESS; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME; +import static org.mockito.Mockito.mock; import java.io.ByteArrayOutputStream; import java.io.File; @@ -61,6 +62,7 @@ import org.jboss.as.server.controller.resources.ServerRootResourceDefinition; import org.jboss.as.server.parsing.StandaloneXml; import org.jboss.as.server.services.net.NetworkInterfaceService; +import org.jboss.as.server.suspend.ServerSuspendController; import org.jboss.as.version.ProductConfig; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; @@ -310,7 +312,7 @@ protected void initModel(ManagementModel managementModel, Resource modelControll public void start(StartContext context) throws StartException { rootResourceDefinition.setDelegate(new ServerRootResourceDefinition(MockRepository.INSTANCE, persister, environment, processState, null, extensionRegistry, false, MOCK_PATH_MANAGER, null, - authorizer, securityIdentitySupplier, AuditLogger.NO_OP_LOGGER, getMutableRootResourceRegistrationProvider(), getBootErrorCollector(), capabilityRegistry)); + authorizer, securityIdentitySupplier, AuditLogger.NO_OP_LOGGER, getMutableRootResourceRegistrationProvider(), getBootErrorCollector(), capabilityRegistry, mock(ServerSuspendController.class))); super.start(context); }