/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, 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.mgmt;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.EXTENSION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILED;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS;
import static org.jboss.as.host.controller.logging.HostControllerLogger.DOMAIN_LOGGER;
import static org.jboss.as.process.protocol.ProtocolUtils.expectHeader;
import java.io.DataInput;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import org.jboss.as.controller.ModelController;
import org.jboss.as.controller.ModelVersion;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.RunningMode;
import org.jboss.as.controller.client.Operation;
import org.jboss.as.controller.client.OperationBuilder;
import org.jboss.as.controller.client.OperationMessageHandler;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.extension.ExtensionRegistry;
import org.jboss.as.controller.logging.ControllerLogger;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.controller.remote.TransactionalProtocolClient;
import org.jboss.as.controller.transform.TransformationTarget;
import org.jboss.as.controller.transform.TransformationTargetImpl;
import org.jboss.as.controller.transform.TransformerRegistry;
import org.jboss.as.controller.transform.Transformers;
import org.jboss.as.domain.controller.DomainController;
import org.jboss.as.domain.controller.HostConnectionInfo;
import org.jboss.as.domain.controller.HostRegistrations;
import org.jboss.as.domain.controller.SlaveRegistrationException;
import org.jboss.as.domain.controller.logging.DomainControllerLogger;
import org.jboss.as.domain.controller.operations.ReadMasterDomainModelHandler;
import org.jboss.as.host.controller.logging.HostControllerLogger;
import org.jboss.as.protocol.StreamUtils;
import org.jboss.as.protocol.mgmt.ActiveOperation;
import org.jboss.as.protocol.mgmt.FlushableDataOutput;
import org.jboss.as.protocol.mgmt.ManagementChannelHandler;
import org.jboss.as.protocol.mgmt.ManagementProtocol;
import org.jboss.as.protocol.mgmt.ManagementRequestContext;
import org.jboss.as.protocol.mgmt.ManagementRequestHandler;
import org.jboss.as.protocol.mgmt.ManagementRequestHandlerFactory;
import org.jboss.as.protocol.mgmt.ManagementRequestHeader;
import org.jboss.as.protocol.mgmt.ManagementResponseHeader;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
import org.jboss.remoting3.Channel;
import org.jboss.remoting3.CloseHandler;
import org.jboss.threads.AsyncFutureTask;
import org.wildfly.common.Assert;
/**
* Handler responsible for the host-controller registration process. This may involve assembling the correct
* {@code ManagementRequestHandlerFactory} based on the version of the host-controller registering.
*
* @author Emanuel Muckenhuber
*/
public class HostControllerRegistrationHandler implements ManagementRequestHandlerFactory {
private static final Operation READ_DOMAIN_MODEL;
private static final ModelNode SUCCESSFUL_RESULT = new ModelNode();
static {
ModelNode mn = new ModelNode();
mn.get(ModelDescriptionConstants.OP).set(ReadMasterDomainModelHandler.OPERATION_NAME);
mn.get(ModelDescriptionConstants.OP_ADDR).setEmptyList();
mn.protect();
READ_DOMAIN_MODEL = OperationBuilder.create(mn).build();
SUCCESSFUL_RESULT.get(OUTCOME).set(SUCCESS);
SUCCESSFUL_RESULT.get(RESULT).setEmptyObject();
SUCCESSFUL_RESULT.protect();
}
private final ManagementChannelHandler handler;
private final OperationExecutor operationExecutor;
private final DomainController domainController;
private final Executor registrationExecutor;
private final HostRegistrations slaveHostRegistrations;
private final String address;
private final DomainHostExcludeRegistry domainHostExcludeRegistry;
public HostControllerRegistrationHandler(ManagementChannelHandler handler, DomainController domainController, OperationExecutor operationExecutor,
Executor registrations, HostRegistrations slaveHostRegistrations,
DomainHostExcludeRegistry domainHostExcludeRegistry) {
this.handler = handler;
this.operationExecutor = operationExecutor;
this.domainController = domainController;
this.registrationExecutor = registrations;
this.slaveHostRegistrations = slaveHostRegistrations;
this.domainHostExcludeRegistry = domainHostExcludeRegistry;
this.address = HostControllerRegistrationHandler.this.handler.getRemoteAddress().getHostAddress();
}
@Override
public ManagementRequestHandler<?, ?> resolveHandler(final RequestHandlerChain handlers, final ManagementRequestHeader header) {
if (header.getVersion() != 1) {
// Send subject
handler.getAttachments().attach(TransactionalProtocolClient.SEND_SUBJECT, Boolean.TRUE);
}
final byte operationId = header.getOperationId();
switch (operationId) {
case DomainControllerProtocol.REGISTER_HOST_CONTROLLER_REQUEST: {
// Start the registration process
final RegistrationContext context = new RegistrationContext(domainController.getExtensionRegistry(),
true, domainHostExcludeRegistry);
context.activeOperation = handlers.registerActiveOperation(header.getBatchId(), context, context);
return new InitiateRegistrationHandler();
}
case DomainControllerProtocol.FETCH_DOMAIN_CONFIGURATION_REQUEST: {
// Start the fetch the domain model process
final RegistrationContext context = new RegistrationContext(domainController.getExtensionRegistry(),
false, domainHostExcludeRegistry);
context.activeOperation = handlers.registerActiveOperation(header.getBatchId(), context, context);
return new InitiateRegistrationHandler();
}
case DomainControllerProtocol.REQUEST_SUBSYSTEM_VERSIONS:
// register the subsystem versions
return new RegisterSubsystemVersionsHandler();
case DomainControllerProtocol.COMPLETE_HOST_CONTROLLER_REGISTRATION:
// Complete the registration process
return new CompleteRegistrationHandler();
}
return handlers.resolveNext();
}
/**
* Wrapper to the DomainController and the underlying {@code ModelController} to execute
* a {@code OperationStepHandler} implementation directly, bypassing normal domain coordination layer.
* TODO This interface probably should be adapted to provide use-case-specific methods instead of generic
* "execute whatever I hand you" ones. The "installSlaveExtensions" method needed to be non-generic unless
* I was willing to hand a ref to the root MRR to RemoteDomainConnnectionService.
*/
public interface OperationExecutor {
/**
* Execute the operation.
*
* @param operation operation
* @param handler the message handler
* @param control the transaction control
* @param step the step to be executed
* @return the result
*/
ModelNode execute(Operation operation, OperationMessageHandler handler, ModelController.OperationTransactionControl control, OperationStepHandler step);
/**
* Execute the operation to install extensions provided by a remote domain controller.
*
*
* @param extensions@return the result
*/
ModelNode installSlaveExtensions(List<ModelNode> extensions);
/**
* Execute an operation using the current management model.
*
* @param operation the operation
* @param handler the operation handler to use
* @return the operation result
*/
ModelNode executeReadOnly(ModelNode operation, OperationStepHandler handler, ModelController.OperationTransactionControl control);
/**
* Execute an operation using given resource model.
*
* @param operation the operation
* @param model the resource model
* @param handler the operation handler to use
* @return the operation result
*/
ModelNode executeReadOnly(ModelNode operation, Resource model, OperationStepHandler handler, ModelController.OperationTransactionControl control);
/**
* Attempts to acquire a non-exclusive read lock. After any operations requiring this lock have completed, #releaseReadlock
* must be called to release the lock.
* @param operationID - the operationID for this registration. Cannot be {@code null}.
* @throws IllegalArgumentException - if operationID is null.
* @throws InterruptedException - if the lock is not acquired.
*/
void acquireReadlock(final Integer operationID) throws IllegalArgumentException, InterruptedException;
/**
* Release the non-exclusive read lock obtained from #acquireReadlock. This method must be called after any locked
* operations have completed or aborted to release the shared lock.
* @param operationID - the operationID for this registration. Cannot be {@code null}.
* @throws IllegalArgumentException - if if the operationId is null.
* @throws IllegalStateException - if the shared lock was not held.
*/
void releaseReadlock(final Integer operationID) throws IllegalArgumentException;
}
class InitiateRegistrationHandler implements ManagementRequestHandler<Void, RegistrationContext> {
@Override
public void handleRequest(final DataInput input, final ActiveOperation.ResultHandler<Void> resultHandler, final ManagementRequestContext<RegistrationContext> context) throws IOException {
expectHeader(input, DomainControllerProtocol.PARAM_HOST_ID);
final String hostName = input.readUTF();
final ModelNode hostInfo = new ModelNode();
hostInfo.readExternal(input);
final RegistrationContext registration = context.getAttachment();
registration.initialize(hostName, hostInfo, context);
if (domainController.getCurrentRunningMode() == RunningMode.ADMIN_ONLY) {
registration.failed(SlaveRegistrationException.ErrorCode.MASTER_IS_ADMIN_ONLY, DomainControllerLogger.ROOT_LOGGER.adminOnlyModeCannotAcceptSlaves(RunningMode.ADMIN_ONLY));
return;
}
if (!domainController.getLocalHostInfo().isMasterDomainController()) {
registration.failed(SlaveRegistrationException.ErrorCode.HOST_IS_NOT_MASTER, DomainControllerLogger.ROOT_LOGGER.slaveControllerCannotAcceptOtherSlaves());
return;
}
// Read the domain model async, this will block until the registration process is complete
context.executeAsync(new ManagementRequestContext.AsyncTask<RegistrationContext>() {
@Override
public void execute(ManagementRequestContext<RegistrationContext> context) throws Exception {
if (Thread.currentThread().isInterrupted()) throw new IllegalStateException("interrupted");
registration.processRegistration();
}
}, registrationExecutor);
}
}
class RegisterSubsystemVersionsHandler implements ManagementRequestHandler<Void, RegistrationContext> {
@Override
public void handleRequest(DataInput input, ActiveOperation.ResultHandler<Void> resultHandler, ManagementRequestContext<RegistrationContext> context) throws IOException {
final byte status = input.readByte();
final ModelNode subsystems = new ModelNode();
subsystems.readExternal(input);
final RegistrationContext registration = context.getAttachment();
if(status == DomainControllerProtocol.PARAM_OK) {
registration.setSubsystems(subsystems, context);
} else {
registration.setSubsystems(null, context);
}
}
}
/**
* Handler responsible for completing the registration request.
*/
static class CompleteRegistrationHandler implements ManagementRequestHandler<Void, RegistrationContext> {
@Override
public void handleRequest(final DataInput input, final ActiveOperation.ResultHandler<Void> resultHandler, final ManagementRequestContext<RegistrationContext> context) throws IOException {
final byte status = input.readByte();
final String message = input.readUTF(); // Perhaps use message when the host failed
final RegistrationContext registration = context.getAttachment();
registration.completeRegistration(context, status == DomainControllerProtocol.PARAM_OK);
}
}
class HostRegistrationStepHandler implements OperationStepHandler {
private final TransformerRegistry transformerRegistry;
private final RegistrationContext registrationContext;
protected HostRegistrationStepHandler(final TransformerRegistry transformerRegistry, final RegistrationContext registrationContext) {
this.registrationContext = registrationContext;
this.transformerRegistry = transformerRegistry;
}
@Override
public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException {
assert registrationContext != null;
assert registrationContext.activeOperation != null;
// acquires the shared-mode read lock.
boolean locked = false;
Integer operationID = registrationContext.activeOperation.getOperationId();
Assert.checkNotNullParam("operationID", operationID);
try {
try {
operationExecutor.acquireReadlock(operationID);
} catch (IllegalArgumentException e) {
throw new OperationFailedException(e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw ControllerLogger.ROOT_LOGGER.operationCancelledAsynchronously();
}
locked = true;
if (domainController.isHostRegistered(registrationContext.hostName)) {
final String failureDescription = DomainControllerLogger.ROOT_LOGGER.slaveAlreadyRegistered(registrationContext.hostName);
registrationContext.failed(SlaveRegistrationException.ErrorCode.HOST_ALREADY_EXISTS, failureDescription);
context.getFailureDescription().set(failureDescription);
return;
}
// Read the extensions (with recursive true, otherwise the entries are runtime=true - which are going to be ignored for transformation)
final Resource root = context.readResourceFromRoot(PathAddress.EMPTY_ADDRESS.append(PathElement.pathElement(EXTENSION)), true);
// Check the mgmt version
final HostInfo hostInfo = registrationContext.hostInfo;
final int major = hostInfo.getManagementMajorVersion();
final int minor = hostInfo.getManagementMinorVersion();
final int micro = hostInfo.getManagementMicroVersion();
boolean as711 = (major == 1 && minor == 1);
if (as711) {
final OperationFailedException failure = HostControllerLogger.ROOT_LOGGER.unsupportedManagementVersionForHost(major, minor, 1, 2);
registrationContext.failed(SlaveRegistrationException.ErrorCode.INCOMPATIBLE_VERSION, failure.getMessage());
throw failure;
}
// Initialize the transformers
final TransformationTarget target = TransformationTargetImpl.createForHost(hostInfo.getHostName(), transformerRegistry,
ModelVersion.create(major, minor, micro), Collections.<PathAddress, ModelVersion>emptyMap(), hostInfo);
final Transformers transformers = Transformers.Factory.create(target);
try {
SlaveChannelAttachments.attachSlaveInfo(handler.getChannel(), registrationContext.hostName, transformers);
} catch (IOException e) {
throw new OperationFailedException(e.getLocalizedMessage());
}
// Build the extensions list
final ModelNode extensions = new ModelNode();
final Transformers.TransformationInputs transformationInputs = Transformers.TransformationInputs.getOrCreate(context);
final Resource transformed = transformers.transformRootResource(transformationInputs, root);
final Collection<Resource.ResourceEntry> resources = transformed.getChildren(EXTENSION);
for (final Resource.ResourceEntry entry : resources) {
if (!hostInfo.isResourceTransformationIgnored(PathAddress.pathAddress(entry.getPathElement()))) {
extensions.add(entry.getName());
}
}
if (!extensions.isDefined()) {
// TODO add a real error message
throw new OperationFailedException(extensions.toString(), extensions);
}
// Remotely resolve the subsystem versions and create the transformation
registrationContext.processSubsystems(transformers, extensions);
// Now run the read-domain model operation
final ReadMasterDomainModelHandler handler = new ReadMasterDomainModelHandler(hostInfo, transformers, domainController.getExtensionRegistry(), false);
context.addStep(READ_DOMAIN_MODEL.getOperation(), handler, OperationContext.Stage.MODEL);
context.completeStep(new OperationContext.ResultHandler() {
@Override
public void handleResult(OperationContext.ResultAction resultAction, OperationContext context, ModelNode operation) {
try {
operationExecutor.releaseReadlock(operationID);
} catch (IllegalArgumentException e) {
HostControllerLogger.ROOT_LOGGER.hostRegistrationCannotReleaseSharedLock(operationID);
}
}
});
locked = false;
} finally {
if (locked) {
operationExecutor.releaseReadlock(operationID);
}
}
}
}
private class RegistrationContext implements ModelController.OperationTransactionControl, ActiveOperation.CompletedCallback<Void> {
private final ExtensionRegistry extensionRegistry;
private final boolean registerProxyController;
private volatile String hostName;
private volatile HostInfo hostInfo;
private ManagementRequestContext<RegistrationContext> responseChannel;
private volatile IOTask<?> task;
private volatile boolean failed;
private volatile Transformers transformers;
private ActiveOperation<Void, RegistrationContext> activeOperation;
private final AtomicBoolean completed = new AtomicBoolean();
private final DomainHostExcludeRegistry domainHostExcludeRegistry;
private RegistrationContext(ExtensionRegistry extensionRegistry,
boolean registerProxyController,
DomainHostExcludeRegistry domainHostExcludeRegistry) {
this.extensionRegistry = extensionRegistry;
this.registerProxyController = registerProxyController;
this.domainHostExcludeRegistry = domainHostExcludeRegistry;
}
private synchronized void initialize(final String hostName, final ModelNode hostInfo, final ManagementRequestContext<RegistrationContext> responseChannel) {
this.hostName = hostName;
this.hostInfo = HostInfo.fromModelNode(hostInfo, domainHostExcludeRegistry);
this.responseChannel = responseChannel;
}
@Override
public void completed(Void result) {
//
}
@Override
public void failed(Exception e) {
failed(SlaveRegistrationException.ErrorCode.UNKNOWN, e.getClass().getName() + ":" + e.getMessage());
}
@Override
public void cancelled() {
//
}
@Override
public void operationPrepared(final ModelController.OperationTransaction transaction, final ModelNode result) {
if(failed) {
transaction.rollback();
} else {
try {
registerHost(transaction, result);
} catch (SlaveRegistrationException e) {
failed(e.getErrorCode(), e.getErrorMessage());
} catch (Exception e) {
failed(SlaveRegistrationException.ErrorCode.UNKNOWN, e.getClass().getName() + ":" + e.getMessage());
}
if(failed) {
transaction.rollback();
}
}
}
/**
* Process the registration of the slave whose information was provided to {@code initialize()}.
*/
private void processRegistration() {
// Check for duplicate registrations
if (domainController.isHostRegistered(hostName)) {
// asynchronously ping the existing host to validate it's still connected
// If not, the ping will remove it and a subsequent attempt by the new host will succeed
// TODO look into doing the ping synchronously
domainController.pingRemoteHost(hostName);
// Quick hack -- wait a bit to let async ping detect a re-registration. This can easily be improved
// via the TODO above
boolean inter = false; // TODO this is not used
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // TODO why not set inter = true and do this in finally?
} finally {
// Now see if the existing registration has been removed
if (domainController.isHostRegistered(hostName)) {
failed(SlaveRegistrationException.ErrorCode.HOST_ALREADY_EXISTS, DomainControllerLogger.ROOT_LOGGER.slaveAlreadyRegistered(hostName));
}
}
}
if (!failed) {
try {
// The domain model is going to be sent as part of the prepared notification
final OperationStepHandler handler = new HostRegistrationStepHandler(extensionRegistry.getTransformerRegistry(), this);
ModelNode result = operationExecutor.execute(READ_DOMAIN_MODEL, OperationMessageHandler.logging, this, handler);
if (FAILED.equals(result.get(OUTCOME).asString())) {
failed(SlaveRegistrationException.ErrorCode.UNKNOWN, result.get(FAILURE_DESCRIPTION).asString());
return;
}
} catch (Exception e) {
failed(e);
return;
}
// Send a registered notification back
sendCompletedMessage();
// Make sure that the host controller gets unregistered when the channel is closed
responseChannel.getChannel().addCloseHandler(new CloseHandler<Channel>() {
@Override
public void handleClose(Channel closed, IOException exception) {
boolean cleanShutdown = ! domainController.isHostRegistered(hostName);
domainController.unregisterRemoteHost(hostName, getRemoteConnectionId(), cleanShutdown);
}
});
}
}
/**
* Create the transformers. This will remotely resolve the subsystem versions.
*
* @param extensions the extensions
* @throws OperationFailedException
*/
private void processSubsystems(final Transformers transformers, final ModelNode extensions) throws OperationFailedException {
this.transformers = transformers;
final ModelNode subsystems = executeBlocking(new IOTask<ModelNode>() {
@Override
void sendMessage(FlushableDataOutput output) throws IOException {
sendResponse(output, DomainControllerProtocol.PARAM_OK, extensions);
}
});
if(failed) {
throw new OperationFailedException("failed to setup transformers");
}
final TransformationTarget target = transformers.getTarget();
for(final Property subsystem : subsystems.asPropertyList()) {
final String subsystemName = subsystem.getName();
final ModelNode version = subsystem.getValue();
target.addSubsystemVersion(subsystemName, ModelVersion.fromString(version.asString()));
}
}
protected void setSubsystems(final ModelNode resolved, final ManagementRequestContext<RegistrationContext> responseChannel) {
this.responseChannel = responseChannel;
completeTask(resolved);
}
/**
* Once the "read-domain-mode" operation is in operationPrepared, send the model back to registering HC.
* When the model was applied successfully on the client, we process registering the proxy in the domain,
* otherwise we rollback.
*
* @param transaction the model controller tx
* @param result the prepared result (domain model)
* @throws SlaveRegistrationException
*/
void registerHost(final ModelController.OperationTransaction transaction, final ModelNode result) throws SlaveRegistrationException {
//
if (sendResultToHost(transaction, result)) return;
synchronized (this) {
Long pingPongId = hostInfo.getRemoteConnectionId();
// Register the slave
domainController.registerRemoteHost(hostName, handler, transformers, pingPongId, registerProxyController);
// Complete registration
if(! failed) {
transaction.commit();
} else {
transaction.rollback();
return;
}
}
if (registerProxyController) {
DOMAIN_LOGGER.registeredRemoteSlaveHost(hostName, hostInfo.getPrettyProductName());
}
}
private boolean sendResultToHost(ModelController.OperationTransaction transaction, final ModelNode result) {
final Boolean registered = executeBlocking(new IOTask<Boolean>() {
@Override
void sendMessage(final FlushableDataOutput output) throws IOException {
sendResponse(output, DomainControllerProtocol.PARAM_OK, result);
}
});
if(! registered) {
transaction.rollback();
return true;
}
return false;
}
void completeRegistration(final ManagementRequestContext<RegistrationContext> responseChannel, boolean commit) {
this.responseChannel = responseChannel;
failed |= ! commit;
completeTask(!failed);
}
void failed(SlaveRegistrationException.ErrorCode error, String message) {
byte errorCode = error.getCode();
if(completed.compareAndSet(false, true)) {
failed = true;
final IOTask<?> task = this.task;
if(task != null) {
task.setFailed();
}
try {
sendFailedResponse(responseChannel, errorCode, message);
} catch (IOException e) {
DOMAIN_LOGGER.debugf(e, "failed to process message");
}
activeOperation.getResultHandler().done(null);
addFailureEvent(error);
}
}
void addFailureEvent(SlaveRegistrationException.ErrorCode error) {
final HostConnectionInfo.EventType eventType;
switch (error) {
case HOST_ALREADY_EXISTS:
eventType = HostConnectionInfo.EventType.REGISTRATION_EXISTING;
break;
case INCOMPATIBLE_VERSION:
eventType = HostConnectionInfo.EventType.REGISTRATION_REJECTED;
break;
default:
eventType = HostConnectionInfo.EventType.REGISTRATION_FAILED;
}
slaveHostRegistrations.addHostEvent(hostName, HostConnectionInfo.Events.create(eventType, address));
}
void sendCompletedMessage() {
if(completed.compareAndSet(false, true)) {
try {
sendResponse(responseChannel, DomainControllerProtocol.PARAM_OK, null);
} catch (IOException e) {
DOMAIN_LOGGER.debugf(e, "failed to process message");
}
activeOperation.getResultHandler().done(null);
}
}
Long getRemoteConnectionId() {
return hostInfo.getRemoteConnectionId();
}
protected boolean completeTask(Object result) {
synchronized (this) {
if(failed) {
return false;
}
if(task != null) {
return task.completeStep(result);
}
}
return false;
}
/**
* Execute a task and wait for the response.
*
* @param task the task to execute
* @param <T> the response type
* @return the result
*/
protected <T> T executeBlocking(final IOTask<T> task) {
synchronized (this) {
this.task = task;
try {
final ManagementResponseHeader header = ManagementResponseHeader.create(responseChannel.getRequestHeader());
final FlushableDataOutput output = responseChannel.writeMessage(header);
try {
task.sendMessage(output);
} catch (IOException e) {
failed(SlaveRegistrationException.ErrorCode.UNKNOWN, DomainControllerLogger.ROOT_LOGGER.failedToSendMessage(e.getMessage()));
throw new IllegalStateException(e);
} finally {
StreamUtils.safeClose(output);
}
} catch (IOException e) {
failed(SlaveRegistrationException.ErrorCode.UNKNOWN, DomainControllerLogger.ROOT_LOGGER.failedToSendResponseHeader(e.getMessage()));
throw new IllegalStateException(e);
}
}
try {
return task.get();
} catch (InterruptedException e) {
failed(SlaveRegistrationException.ErrorCode.UNKNOWN, DomainControllerLogger.ROOT_LOGGER.registrationTaskGotInterrupted());
throw new IllegalStateException(e);
} catch (ExecutionException e) {
failed(SlaveRegistrationException.ErrorCode.UNKNOWN, DomainControllerLogger.ROOT_LOGGER.registrationTaskFailed(e.getMessage()));
throw new IllegalStateException(e);
}
}
}
abstract static class IOTask<T> extends AsyncFutureTask<T> {
IOTask() {
super(null);
}
abstract void sendMessage(final FlushableDataOutput output) throws IOException;
@SuppressWarnings("unchecked")
boolean completeStep(Object result) {
return setResult((T) result);
}
boolean setFailed() {
return setFailed(null);
}
}
/**
* Send an operation response.
*
* @param context the request context
* @param responseType the response type
* @param response the operation response
* @throws IOException for any error
*/
static void sendResponse(final ManagementRequestContext<RegistrationContext> context, final byte responseType, final ModelNode response) throws IOException {
final ManagementResponseHeader header = ManagementResponseHeader.create(context.getRequestHeader());
final FlushableDataOutput output = context.writeMessage(header);
try {
sendResponse(output, responseType, response);
} finally {
StreamUtils.safeClose(output);
}
}
static void sendResponse(final FlushableDataOutput output, final byte responseType, final ModelNode response) throws IOException {
// response type
output.writeByte(responseType);
if(response != null) {
// operation result
response.writeExternal(output);
}
// response end
output.writeByte(ManagementProtocol.RESPONSE_END);
output.close();
}
/**
* Send a failed operation response.
*
* @param context the request context
* @param errorCode the error code
* @param message the operation message
* @throws IOException for any error
*/
static void sendFailedResponse(final ManagementRequestContext<RegistrationContext> context, final byte errorCode, final String message) throws IOException {
final ManagementResponseHeader header = ManagementResponseHeader.create(context.getRequestHeader());
final FlushableDataOutput output = context.writeMessage(header);
try {
// This is an error
output.writeByte(DomainControllerProtocol.PARAM_ERROR);
// send error code
output.writeByte(errorCode);
// error message
if (message == null) {
output.writeUTF("unknown error");
} else {
output.writeUTF(message);
}
// response end
output.writeByte(ManagementProtocol.RESPONSE_END);
output.close();
} finally {
StreamUtils.safeClose(output);
}
}
}