/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.host.controller;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HOST;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RELOAD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESUME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNNING_SERVER;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.START_MODE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUSPEND;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.TIMEOUT;
import static org.jboss.as.host.controller.logging.HostControllerLogger.ROOT_LOGGER;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.jboss.as.controller.CurrentOperationIdHolder;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ProxyOperationAddressTranslator;
import org.jboss.as.controller.TransformingProxyController;
import org.jboss.as.controller.client.OperationAttachments;
import org.jboss.as.controller.client.OperationMessageHandler;
import org.jboss.as.controller.client.OperationResponse;
import org.jboss.as.controller.client.helpers.domain.ServerStatus;
import org.jboss.as.controller.remote.BlockingQueueOperationListener;
import org.jboss.as.controller.remote.TransactionalProtocolClient;
import org.jboss.as.controller.remote.TransactionalProtocolHandlers;
import org.jboss.as.controller.transform.TransformationTarget;
import org.jboss.as.controller.transform.Transformers;
import org.jboss.as.host.controller.logging.HostControllerLogger;
import org.jboss.as.process.ProcessControllerClient;
import org.jboss.as.protocol.mgmt.ManagementChannelHandler;
import org.jboss.as.server.DomainServerCommunicationServices;
import org.jboss.as.server.ServerStartTask;
import org.jboss.dmr.ModelNode;
import org.jboss.logging.Logger;
import org.jboss.marshalling.Marshaller;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
import org.jboss.marshalling.SimpleClassResolver;
import org.jboss.msc.service.ServiceActivator;
import org.jboss.threads.AsyncFuture;
/**
* Represents a managed server.
*
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
* @author <a href="mailto:kabir.khan@jboss.com">Kabir Khan</a>
* @author Brian Stansberry
* @author Emanuel Muckenhuber
*/
class ManagedServer {
private static final Logger.Level DEBUG_LEVEL = Logger.Level.TRACE;
private static final MarshallerFactory MARSHALLER_FACTORY;
private static final MarshallingConfiguration CONFIG;
static {
MARSHALLER_FACTORY = Marshalling.getMarshallerFactory("river", Marshalling.class.getClassLoader());
final ClassLoader cl = ManagedServer.class.getClassLoader();
final MarshallingConfiguration config = new MarshallingConfiguration();
config.setVersion(2);
config.setClassResolver(new SimpleClassResolver(cl));
CONFIG = config;
}
/**
* Prefix applied to a server's name to create it's process name.
*/
private static final String SERVER_PROCESS_NAME_PREFIX = "Server:";
static String getServerProcessName(String serverName) {
return SERVER_PROCESS_NAME_PREFIX + serverName;
}
static boolean isServerProcess(String serverProcessName) {
return serverProcessName.startsWith(SERVER_PROCESS_NAME_PREFIX);
}
static String getServerName(String serverProcessName) {
return serverProcessName.substring(SERVER_PROCESS_NAME_PREFIX.length());
}
private final String authKey;
private final String serverName;
private final String serverProcessName;
private final String hostControllerName;
private final URI managementURI;
private final ProcessControllerClient processControllerClient;
private final ManagedServerProxy protocolClient;
private final TransformingProxyController proxyController;
private volatile boolean requiresReload;
private volatile InternalState requiredState = InternalState.STOPPED;
private volatile InternalState internalState = InternalState.STOPPED;
private volatile int operationID = CurrentOperationIdHolder.getCurrentOperationID();
private volatile ManagedServerBootConfiguration bootConfiguration;
private volatile boolean unstable;
private final PathAddress address;
ManagedServer(final String hostControllerName, final String serverName, final String authKey,
final ProcessControllerClient processControllerClient, final URI managementURI,
final TransformationTarget transformationTarget) {
assert hostControllerName != null : "hostControllerName is null";
assert serverName != null : "serverName is null";
assert processControllerClient != null : "processControllerSlave is null";
assert managementURI != null : "managementURI is null";
this.hostControllerName = hostControllerName;
this.serverName = serverName;
this.serverProcessName = getServerProcessName(serverName);
this.processControllerClient = processControllerClient;
this.managementURI = managementURI;
this.authKey = authKey;
// Setup the proxy controller
final PathElement serverPath = PathElement.pathElement(RUNNING_SERVER, serverName);
address = PathAddress.EMPTY_ADDRESS.append(PathElement.pathElement(HOST, hostControllerName), serverPath);
this.protocolClient = new ManagedServerProxy(this);
this.proxyController = TransformingProxyController.Factory.create(protocolClient,
Transformers.Factory.create(transformationTarget), address, ProxyOperationAddressTranslator.SERVER);
}
/**
* Get the process auth key.
*
* @return the auth key
*/
String getAuthKey() {
return authKey;
}
/**
* Get the server name.
*
* @return the server name
*/
String getServerName() {
return serverName;
}
/**
* Get the transforming proxy controller instance.
*
* @return the proxy controller
*/
TransformingProxyController getProxyController() {
return proxyController;
}
/**
* Determine the current state the server is in.
*
* @return the server status
*/
ServerStatus getState() {
final InternalState requiredState = this.requiredState;
final InternalState state = internalState;
if(requiredState == InternalState.FAILED) {
return ServerStatus.FAILED;
}
switch (state) {
case STOPPED:
return ServerStatus.STOPPED;
case SERVER_STARTED:
return ServerStatus.STARTED;
default: {
if(requiredState == InternalState.SERVER_STARTED) {
return ServerStatus.STARTING;
} else {
return ServerStatus.STOPPING;
}
}
}
}
boolean isRequiresReload() {
return requiresReload;
}
/**
* Require a reload on the the next reconnect.
*/
void requireReload() {
requiresReload = true;
}
/**
* Reload a managed server.
*
* @param permit the controller permit
* @return whether the state was changed successfully or not
*/
synchronized boolean reload(int permit, boolean suspend) {
return internalSetState(new ReloadTask(permit, suspend), InternalState.SERVER_STARTED, InternalState.RELOADING);
}
/**
* Start a managed server.
*
* @param factory the boot command factory
*/
synchronized void start(final ManagedServerBootCmdFactory factory) {
final InternalState required = this.requiredState;
// Ignore if the server is already started
if(required == InternalState.SERVER_STARTED) {
return;
}
// In case the server failed to start, try to start it again
if(required != InternalState.FAILED) {
final InternalState current = this.internalState;
if(current != required) {
// TODO this perhaps should wait?
throw new IllegalStateException();
}
}
operationID = CurrentOperationIdHolder.getCurrentOperationID();
bootConfiguration = factory.createConfiguration();
requiredState = InternalState.SERVER_STARTED;
ROOT_LOGGER.startingServer(serverName);
transition();
}
/**
* Stop a managed server.
*/
synchronized void stop(int operationID, int timeout) {
final InternalState required = this.requiredState;
if(required != InternalState.STOPPED) {
this.requiredState = InternalState.STOPPED;
ROOT_LOGGER.stoppingServer(serverName);
// Only send the stop operation if the server is started
if (internalState == InternalState.SERVER_STARTED) {
internalSetState(new ServerStopTask(operationID, timeout), internalState, InternalState.PROCESS_STOPPING);
} else {
transition(false);
}
}
}
synchronized void destroy() {
InternalState required = this.requiredState;
if(required == InternalState.STOPPED) {
if(internalState != InternalState.STOPPED) {
try {
processControllerClient.destroyProcess(serverProcessName);
} catch (IOException e) {
ROOT_LOGGER.logf(DEBUG_LEVEL, e, "failed to send destroy_process message to %s", serverName);
}
}
} else {
// Do the normal stop stuff first
stop(-1, 0);
required = this.requiredState;
if (required == InternalState.STOPPED) {
// Now proceed to destroy, assuming that there was a reason the user
// invoked this op, and that stop() alone will not suffice
// The PC will give it a bit of time to do a normal stop before destroying
destroy();
} // else something is odd but avoid looping. User would have to invoke the op again
}
}
synchronized void kill() {
InternalState required = this.requiredState;
if(required == InternalState.STOPPED) {
if(internalState != InternalState.STOPPED) {
try {
processControllerClient.killProcess(serverProcessName);
} catch (IOException e) {
ROOT_LOGGER.logf(DEBUG_LEVEL, e, "failed to send kill_process message to %s", serverName);
}
}
} else {
// Do the normal stop stuff first
stop(-1, 0);
required = this.requiredState;
if (required == InternalState.STOPPED) {
// Now proceed to kill, assuming that there was a reason the user
// invoked this op, and that stop() alone will not suffice
// The PC will give it a bit of time to do a normal stop before killing
kill();
} // else something is odd but avoid looping. User would have to invoke the op again
}
}
/**
* Try to reconnect to a started server.
*/
synchronized void reconnectServerProcess(final ManagedServerBootCmdFactory factory) {
if(this.requiredState != InternalState.SERVER_STARTED) {
this.bootConfiguration = factory;
this.requiredState = InternalState.SERVER_STARTED;
ROOT_LOGGER.reconnectingServer(serverName);
internalSetState(new ReconnectTask(), InternalState.STOPPED, InternalState.SEND_STDIN);
}
}
/**
* On host controller reload, remove a not running server registered in the process controller declared as down.
*/
synchronized void removeServerProcess() {
this.requiredState = InternalState.STOPPED;
internalSetState(new ProcessRemoveTask(), InternalState.STOPPED, InternalState.PROCESS_REMOVING);
}
/**
* On host controller reload, remove a not running server registered in the process controller declared as stopping.
*/
synchronized void setServerProcessStopping() {
this.requiredState = InternalState.STOPPED;
internalSetState(null, InternalState.STOPPED, InternalState.PROCESS_STOPPING);
}
/**
* Await a state.
*
* @param expected the expected state
* @return {@code true} if the state was reached, {@code false} otherwise
*/
boolean awaitState(final InternalState expected) {
synchronized (this) {
final InternalState initialRequired = this.requiredState;
for(;;) {
final InternalState required = this.requiredState;
// Stop in case the server failed to reach the state
if(required == InternalState.FAILED) {
return false;
// Stop in case the required state changed
} else if (initialRequired != required) {
return false;
}
final InternalState current = this.internalState;
if(expected == current) {
return true;
}
try {
wait();
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
}
}
/**
* Notification that the process was added
*/
void processAdded() {
finishTransition(InternalState.PROCESS_ADDING, InternalState.PROCESS_ADDED);
}
/**
* Notification that the process was started.
*/
void processStarted() {
finishTransition(InternalState.PROCESS_STARTING, InternalState.PROCESS_STARTED);
}
/**
* Notification that the process has become unstable.
*
* @return {@code true} if this is a change in status
*/
boolean processUnstable() {
boolean change = !unstable;
if (change) { // Only once until the process is removed. A process is unstable until removed.
unstable = true;
HostControllerLogger.ROOT_LOGGER.managedServerUnstable(serverName);
}
return change;
}
synchronized TransactionalProtocolClient channelRegistered(final ManagementChannelHandler channelAssociation) {
final InternalState current = this.internalState;
// Create the remote controller client
channelAssociation.getAttachments().attach(TransactionalProtocolClient.SEND_SUBJECT, Boolean.TRUE);
final TransactionalProtocolClient remoteClient = TransactionalProtocolHandlers.createClient(channelAssociation);
if (current == InternalState.RELOADING) {
internalSetState(new TransitionTask() {
@Override
public boolean execute(ManagedServer server) throws Exception {
// Update the current remote connection
protocolClient.connected(remoteClient);
// clear reload required state
requiresReload = false;
return true;
}
}, InternalState.RELOADING, InternalState.SERVER_STARTING);
} else {
internalSetState(new TransitionTask() {
@Override
public boolean execute(final ManagedServer server) throws Exception {
// Update the current remote connection
protocolClient.connected(remoteClient);
return true;
}
// TODO we just check that we are in the correct state, perhaps introduce a new state
}, InternalState.SEND_STDIN, InternalState.SERVER_STARTING);
}
return remoteClient;
}
synchronized void serverStarted(final TransitionTask task) {
internalSetState(task, InternalState.SERVER_STARTING, InternalState.SERVER_STARTED);
}
synchronized void serverStartFailed() {
internalSetState(null, InternalState.SERVER_STARTING, InternalState.FAILED);
}
/**
* Unregister the mgmt channel.
*
* @param old the proxy controller to unregister
* @param shuttingDown whether the server inventory is shutting down
* @return whether the registration can be removed from the domain-controller
*/
boolean callbackUnregistered(final TransactionalProtocolClient old, final boolean shuttingDown) {
// Disconnect the remote connection.
// WFCORE-196 Do this out of the sync block to avoid deadlocks where in-flight requests can't
// be informed that the channel has closed
protocolClient.disconnected(old);
synchronized (this) {
// If the connection dropped without us stopping the process ask for reconnection
if (!shuttingDown && requiredState == InternalState.SERVER_STARTED) {
final InternalState state = internalState;
if (state == InternalState.PROCESS_STOPPED
|| state == InternalState.PROCESS_STOPPING
|| state == InternalState.STOPPED) {
// In case it stopped we don't reconnect
return true;
}
// In case we are reloading, it will reconnect automatically
if (state == InternalState.RELOADING) {
return true;
}
try {
ROOT_LOGGER.logf(DEBUG_LEVEL, "trying to reconnect to %s current-state (%s) required-state (%s)", serverName, state, requiredState);
internalSetState(new ReconnectTask(), state, InternalState.SEND_STDIN);
} catch (Exception e) {
ROOT_LOGGER.logf(DEBUG_LEVEL, e, "failed to send reconnect task");
}
return false;
} else {
return true;
}
}
}
/**
* Notification that the server process finished.
*/
synchronized void processFinished() {
final InternalState required = this.requiredState;
final InternalState state = this.internalState;
// If the server was not stopped
if(required == InternalState.STOPPED && state == InternalState.PROCESS_STOPPING) {
finishTransition(InternalState.PROCESS_STOPPING, InternalState.PROCESS_STOPPED);
} else {
this.requiredState = InternalState.STOPPED;
if ( !(internalSetState(getTransitionTask(InternalState.PROCESS_STOPPING), internalState, InternalState.PROCESS_STOPPING)
&& internalSetState(getTransitionTask(InternalState.PROCESS_REMOVING), internalState, InternalState.PROCESS_REMOVING)
&& internalSetState(getTransitionTask(InternalState.STOPPED), internalState, InternalState.STOPPED)) ){
this.requiredState = InternalState.FAILED;
internalSetState(null, internalState, InternalState.PROCESS_STOPPED);
}
}
}
/**
* Notification that the process got removed from the process controller.
*/
void processRemoved() {
finishTransition(InternalState.PROCESS_REMOVING, InternalState.STOPPED);
unstable = false;
}
private void transition() {
transition(true);
}
private synchronized void transition(boolean checkAsync) {
final InternalState required = this.requiredState;
final InternalState current = this.internalState;
// Check if we are waiting for a notification from the server
if(checkAsync && current.isAsync()) {
return;
}
final InternalState next = nextState(current, required);
if (next != null) {
final TransitionTask task = getTransitionTask(next);
internalSetState(task, current, next);
}
}
/**
* Notification that a state transition failed.
*
* @param state the failed transition
*/
synchronized void transitionFailed(final InternalState state) {
final InternalState current = this.internalState;
if(state == current) {
// Revert transition and mark as failed
switch (current) {
case PROCESS_ADDING:
this.internalState = InternalState.PROCESS_STOPPED;
break;
case PROCESS_STARTED:
internalSetState(getTransitionTask(InternalState.PROCESS_STOPPING), InternalState.PROCESS_STARTED, InternalState.PROCESS_ADDED);
break;
case PROCESS_STARTING:
this.internalState = InternalState.PROCESS_ADDED;
break;
case SEND_STDIN:
case SERVER_STARTING:
this.internalState = InternalState.PROCESS_STARTED;
break;
}
this.requiredState = InternalState.FAILED;
notifyAll();
}
}
private synchronized void transitionFailed(final InternalState state, Exception cause) {
if (state == this.internalState) {
switch (this.requiredState) {
case PROCESS_ADDED:
case PROCESS_ADDING:
case PROCESS_STARTED:
case PROCESS_STARTING:
case SERVER_STARTING:
case SERVER_STARTED:
ROOT_LOGGER.failedToStartServer(cause, serverName);
break;
case PROCESS_REMOVING:
case PROCESS_STOPPED:
case PROCESS_STOPPING:
case STOPPED:
ROOT_LOGGER.failedToStopServer(cause, serverName);
break;
}
}
transitionFailed(state);
}
/**
* Finish a state transition from a notification.
*
* @param current
* @param next
*/
private synchronized void finishTransition(final InternalState current, final InternalState next) {
internalSetState(getTransitionTask(next), current, next);
transition();
}
private boolean internalSetState(final TransitionTask task, final InternalState current, final InternalState next) {
assert Thread.holdsLock(this); // Call under lock
final InternalState internalState = this.internalState;
ROOT_LOGGER.logf(DEBUG_LEVEL, "changing server state (%s) from %s to %s", serverName, current, next);
if(internalState == current) {
try {
if(task != null) {
if(! task.execute(this)) {
return true; // Not a failure condition
}
}
this.internalState = next;
return true;
} catch (final Exception e) {
ROOT_LOGGER.logf(DEBUG_LEVEL, e, "transition (%s > %s) failed for server \"%s\"", current, next, serverName);
transitionFailed(current, e);
} finally {
notifyAll();
}
}
return false;
}
private TransitionTask getTransitionTask(final InternalState next) {
switch (next) {
case PROCESS_ADDING: {
return new ProcessAddTask();
} case PROCESS_STARTING: {
return new ProcessStartTask();
} case SEND_STDIN: {
return new SendStdInTask();
} case SERVER_STARTED: {
return new ServerStartedTask();
} case PROCESS_STOPPING: {
return new ServerStopTask(-1, 0);
} case PROCESS_REMOVING: {
return new ProcessRemoveTask();
} default: {
return null;
}
}
}
private static InternalState nextState(final InternalState state, final InternalState required) {
switch (state) {
case STOPPED: {
if(required == InternalState.SERVER_STARTED) {
return InternalState.PROCESS_ADDING;
}
break;
} case PROCESS_ADDING: {
if(required == InternalState.SERVER_STARTED) {
return InternalState.PROCESS_ADDED;
}
break;
} case PROCESS_ADDED: {
if(required == InternalState.SERVER_STARTED) {
return InternalState.PROCESS_STARTING;
} else if( required == InternalState.STOPPED) {
return InternalState.PROCESS_REMOVING;
}
break;
} case PROCESS_STARTING: {
if(required == InternalState.SERVER_STARTED) {
return InternalState.PROCESS_STARTED;
}
break;
} case PROCESS_STARTED: {
if(required == InternalState.SERVER_STARTED) {
return InternalState.SEND_STDIN;
} else if( required == InternalState.STOPPED) {
return InternalState.PROCESS_STOPPING;
}
break;
} case SEND_STDIN: {
if(required == InternalState.SERVER_STARTED) {
return InternalState.SERVER_STARTING;
} else if( required == InternalState.STOPPED) {
return InternalState.PROCESS_STOPPING;
}
break;
} case SERVER_STARTING: {
if(required == InternalState.SERVER_STARTED) {
return InternalState.SERVER_STARTED;
} else if( required == InternalState.STOPPED) {
return InternalState.PROCESS_STOPPING;
}
break;
} case SERVER_STARTED: {
if(required == InternalState.STOPPED) {
return InternalState.PROCESS_STOPPING;
}
break;
} case RELOADING: {
if (required == InternalState.SERVER_STARTED) {
return InternalState.SERVER_STARTED;
} else if (required == InternalState.STOPPED) {
return InternalState.PROCESS_STOPPING;
}
break;
} case PROCESS_STOPPING: {
if(required == InternalState.STOPPED) {
return InternalState.PROCESS_STOPPED;
}
break;
} case PROCESS_STOPPED: {
if(required == InternalState.SERVER_STARTED) {
return InternalState.PROCESS_STARTING;
} else if( required == InternalState.STOPPED) {
return InternalState.PROCESS_REMOVING;
}
break;
} case PROCESS_REMOVING: {
if(required == InternalState.STOPPED) {
return InternalState.STOPPED;
}
break;
} default: {
return null;
}
}
return null;
}
AsyncFuture<OperationResponse> resume(final BlockingQueueOperationListener<TransactionalProtocolClient.Operation> listener) throws IOException {
final ModelNode operation = new ModelNode();
operation.get(OP).set(RESUME);
operation.get(OP_ADDR).setEmptyList();
return protocolClient.execute(listener, operation, OperationMessageHandler.DISCARD, OperationAttachments.EMPTY);
}
AsyncFuture<OperationResponse> suspend(int timeoutInSeconds, final BlockingQueueOperationListener<TransactionalProtocolClient.Operation> listener) throws IOException {
final ModelNode operation = new ModelNode();
operation.get(OP).set(SUSPEND);
operation.get(OP_ADDR).setEmptyList();
operation.get(TIMEOUT).set(timeoutInSeconds);
return protocolClient.execute(listener, operation, OperationMessageHandler.DISCARD, OperationAttachments.EMPTY);
}
enum InternalState {
STOPPED,
PROCESS_ADDING(true),
PROCESS_ADDED,
PROCESS_STARTING(true),
PROCESS_STARTED,
SEND_STDIN(true),
SERVER_STARTING(true),
SERVER_STARTED,
RELOADING(true),
PROCESS_STOPPING(true),
PROCESS_STOPPED,
PROCESS_REMOVING(true),
SUSPENDING(true),
FAILED,
;
/** State transition creates an async task. */
private final boolean async;
InternalState() {
this(false);
}
InternalState(boolean async) {
this.async = async;
}
boolean isAsync() {
return async;
}
}
@FunctionalInterface
interface TransitionTask {
boolean execute(ManagedServer server) throws Exception;
}
private class ProcessAddTask implements TransitionTask {
@Override
public boolean execute(ManagedServer server) throws Exception {
assert Thread.holdsLock(ManagedServer.this); // Call under lock
final List<String> command = bootConfiguration.getServerLaunchCommand();
final Map<String, String> env = bootConfiguration.getServerLaunchEnvironment();
final HostControllerEnvironment environment = bootConfiguration.getHostControllerEnvironment();
// Add the process to the process controller
processControllerClient.addProcess(serverProcessName, authKey, command.toArray(new String[command.size()]), environment.getHomeDir().getAbsolutePath(), env);
return true;
}
}
private class ProcessRemoveTask implements TransitionTask {
@Override
public boolean execute(ManagedServer server) throws Exception {
assert Thread.holdsLock(ManagedServer.this); // Call under lock
// Remove process
processControllerClient.removeProcess(serverProcessName);
return true;
}
}
private class ProcessStartTask implements TransitionTask {
@Override
public boolean execute(ManagedServer server) throws Exception {
assert Thread.holdsLock(ManagedServer.this); // Call under lock
// Start the process
processControllerClient.startProcess(serverProcessName);
return true;
}
}
private class SendStdInTask implements TransitionTask {
@Override
public boolean execute(ManagedServer server) throws Exception {
assert Thread.holdsLock(ManagedServer.this); // Call under lock
// Get the standalone boot updates
final List<ModelNode> bootUpdates = Collections.emptyList(); // bootConfiguration.getBootUpdates();
final Map<String, String> launchProperties = parseLaunchProperties(bootConfiguration.getServerLaunchCommand());
final boolean useSubsystemEndpoint = bootConfiguration.isManagementSubsystemEndpoint();
final ModelNode endpointConfig = bootConfiguration.getSubsystemEndpointConfiguration();
// Send std.in
final ServiceActivator hostControllerCommActivator = DomainServerCommunicationServices.create(endpointConfig, managementURI, serverName, serverProcessName, authKey, useSubsystemEndpoint, bootConfiguration.getSSLContextSupplier());
final ServerStartTask startTask = new ServerStartTask(hostControllerName, serverName, 0, operationID, Collections.<ServiceActivator>singletonList(hostControllerCommActivator), bootUpdates, launchProperties, bootConfiguration.isSuspended());
final Marshaller marshaller = MARSHALLER_FACTORY.createMarshaller(CONFIG);
final OutputStream os = processControllerClient.sendStdin(serverProcessName);
marshaller.start(Marshalling.createByteOutput(os));
marshaller.writeObject(startTask);
marshaller.finish();
marshaller.close();
os.close();
return true;
}
}
private class ServerStartedTask implements TransitionTask {
@Override
public boolean execute(ManagedServer server) throws Exception {
return true;
}
}
private class ServerStopTask implements TransitionTask {
private final int permit;
private final int timeout;
private ServerStopTask(int permit, int timeout) {
this.permit = permit;
this.timeout = timeout;
}
@Override
public boolean execute(ManagedServer server) throws Exception {
assert Thread.holdsLock(ManagedServer.this); // Call under lock
// Stop process
try {
//graceful shutdown
//this just suspends the server, it does not actually shut it down
if (permit != -1) {
final ModelNode operation = new ModelNode();
operation.get(OP).set("shutdown");
operation.get(OP_ADDR).setEmptyList();
operation.get("operation-id").set(permit);
operation.get("timeout").set(timeout);
final TransactionalProtocolClient.PreparedOperation<?> prepared = TransactionalProtocolHandlers.executeBlocking(operation, protocolClient);
if (prepared.isFailed()) {
return true;
}
//we stop the server via an operation
prepared.commit();
prepared.getFinalResult().get();
}
} catch (Exception ignore) {
} finally {
try {
processControllerClient.stopProcess(serverProcessName);
} catch (IOException ignore) {
}
}
return true;
}
}
private class ReconnectTask implements TransitionTask {
@Override
public boolean execute(ManagedServer server) throws Exception {
assert Thread.holdsLock(ManagedServer.this); // Call under lock
// Reconnect
processControllerClient.reconnectProcess(serverProcessName, managementURI, bootConfiguration.isManagementSubsystemEndpoint(), authKey);
return true;
}
}
private class ReloadTask implements TransitionTask {
private final int permit;
private final boolean suspend;
private ReloadTask(int permit, boolean suspend) {
this.permit = permit;
this.suspend = suspend;
}
@Override
public boolean execute(ManagedServer server) throws Exception {
final ModelNode operation = new ModelNode();
operation.get(OP).set(RELOAD);
operation.get(OP_ADDR).setEmptyList();
if(suspend) {
operation.get(START_MODE).set(SUSPEND);
}
// See WFCORE-1791, Operation-id is sent back again to the HC in
// HostControllerConnection.ServerRegisterRequest.sendRequest method.
// With this operation-id ServerRegistrationStepHandler is able to acquire
// the lock and register the server in the domain.
operation.get("operation-id").set(permit);
try {
final TransactionalProtocolClient.PreparedOperation<?> prepared = TransactionalProtocolHandlers.executeBlocking(operation, protocolClient);
if (prepared.isFailed()) {
return false;
}
prepared.commit(); // Just commit and discard the result
} catch (IOException ignore) {
//
}
return true;
}
}
private static Map<String, String> parseLaunchProperties(final List<String> commands) {
final Map<String, String> result = new LinkedHashMap<String, String>();
for (String cmd : commands) {
if (cmd.startsWith("-D")) {
final String[] parts = cmd.substring(2).split("=");
if (parts.length == 2) {
result.put(parts[0], parts[1]);
} else if (parts.length == 1) {
result.put(parts[0], "true");
}
}
}
return result;
}
PathAddress getAddress(){
return address;
}
}