/*
* 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.domain.controller.operations.coordination;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DOMAIN_RESULTS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.EXECUTE_FOR_COORDINATOR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OPERATION_HEADERS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT;
import java.util.Map;
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.ProxyController;
import org.jboss.as.controller.extension.ExtensionRegistry;
import org.jboss.as.controller.logging.ControllerLogger;
import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.OperationEntry;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.domain.controller.LocalHostControllerInfo;
import org.jboss.as.domain.controller.operations.SyncModelOperationHandlerWrapper;
import org.jboss.as.host.controller.ignored.IgnoredDomainResourceRegistry;
import org.jboss.dmr.ModelNode;
/**
* Performs the host specific overall execution of an operation on a slave, on behalf of the domain controller.
*
* @author Brian Stansberry (c) 2011 Red Hat Inc.
*/
class OperationSlaveStepHandler {
private final LocalHostControllerInfo localHostControllerInfo;
private final Map<String, ProxyController> serverProxies;
private final IgnoredDomainResourceRegistry ignoredDomainResourceRegistry;
private final ExtensionRegistry extensionRegistry;
OperationSlaveStepHandler(final LocalHostControllerInfo localHostControllerInfo, Map<String, ProxyController> serverProxies,
final IgnoredDomainResourceRegistry ignoredDomainResourceRegistry,
final ExtensionRegistry extensionRegistry) {
this.localHostControllerInfo = localHostControllerInfo;
this.serverProxies = serverProxies;
this.ignoredDomainResourceRegistry = ignoredDomainResourceRegistry;
this.extensionRegistry = extensionRegistry;
}
void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
ModelNode headers = operation.get(OPERATION_HEADERS);
headers.remove(EXECUTE_FOR_COORDINATOR);
if (headers.hasDefined(DomainControllerLockIdUtils.DOMAIN_CONTROLLER_LOCK_ID)) {
int id = headers.remove(DomainControllerLockIdUtils.DOMAIN_CONTROLLER_LOCK_ID).asInt();
context.attach(DomainControllerLockIdUtils.DOMAIN_CONTROLLER_LOCK_ID_ATTACHMENT, id);
}
final MultiPhaseLocalContext localContext = new MultiPhaseLocalContext(false);
final HostControllerExecutionSupport hostControllerExecutionSupport = addSteps(context, operation, localContext);
final boolean reloadRequired = hostControllerExecutionSupport.isReloadRequired();
if (reloadRequired) {
context.reloadRequired();
}
context.completeStep(new OperationContext.ResultHandler() {
@Override
public void handleResult(OperationContext.ResultAction resultAction, OperationContext context, ModelNode operation) {
hostControllerExecutionSupport.complete(resultAction == OperationContext.ResultAction.ROLLBACK);
if (resultAction == OperationContext.ResultAction.KEEP) {
// Replace the special format response ServerOperationsResolverHandler
// used to send the prepared response to the coordinator with one that
// has the final data. To save bandwidth we also drop any 'server-operations'
// that were in the prepared response as those are no longer relevant.
ModelNode result = context.getResult();
result.setEmptyObject();
ModelNode domainFormatted = hostControllerExecutionSupport.getFormattedDomainResult(localContext.getLocalResponse().get(RESULT));
result.get(DOMAIN_RESULTS).set(domainFormatted);
} else {
if (reloadRequired) {
context.revertReloadRequired();
}
// The actual operation failed but make sure the result still gets formatted
if (hostControllerExecutionSupport.getDomainOperation() != null) {
ModelNode localResponse = localContext.getLocalResponse();
if (localResponse.has(FAILURE_DESCRIPTION)) {
context.getFailureDescription().set(localResponse.get(FAILURE_DESCRIPTION));
}
if (localResponse.has(RESULT)) {
context.getResult().set(localResponse.get(RESULT));
}
}
}
}
});
}
HostControllerExecutionSupport addSteps(final OperationContext context, final ModelNode operation, final MultiPhaseLocalContext multiPhaseLocalContext) throws OperationFailedException {
final PathAddress originalAddress = PathAddress.pathAddress(operation.get(OP_ADDR));
final ImmutableManagementResourceRegistration originalRegistration = context.getResourceRegistration();
final ModelNode localResponse = multiPhaseLocalContext.getLocalResponse();
final HostControllerExecutionSupport hostControllerExecutionSupport =
HostControllerExecutionSupport.Factory.create(context, operation, localHostControllerInfo.getLocalHostName(),
new LazyDomainModelProvider(context), ignoredDomainResourceRegistry, !localHostControllerInfo.isMasterDomainController() && localHostControllerInfo.isRemoteDomainControllerIgnoreUnaffectedConfiguration(),
extensionRegistry);
ModelNode domainOp = hostControllerExecutionSupport.getDomainOperation();
if (domainOp != null) {
// Only require an existing registration if the domain op is not ignored
if (originalRegistration == null) {
throw new OperationFailedException(ControllerLogger.ROOT_LOGGER.noSuchResourceType(originalAddress));
}
addBasicStep(context, domainOp, localResponse);
}
ServerOperationResolver resolver = new ServerOperationResolver(localHostControllerInfo.getLocalHostName(), serverProxies);
ServerOperationsResolverHandler sorh = new ServerOperationsResolverHandler(
resolver, hostControllerExecutionSupport, originalAddress, originalRegistration, multiPhaseLocalContext);
context.addStep(sorh, OperationContext.Stage.DOMAIN);
return hostControllerExecutionSupport;
}
/**
* Directly handles the op in the standard way the default prepare step handler would
* @param context the operation execution context
* @param operation the operation
* @throws OperationFailedException if no handler is registered for the operation
*/
private void addBasicStep(OperationContext context, ModelNode operation, ModelNode localReponse) throws OperationFailedException {
final String operationName = operation.require(OP).asString();
final OperationEntry entry = context.getRootResourceRegistration().getOperationEntry(PathAddress.pathAddress(operation.get(OP_ADDR)), operationName);
if(entry != null) {
if (context.isBooting() || localHostControllerInfo.isMasterDomainController()) {
context.addModelStep(localReponse, operation, entry.getOperationDefinition(), entry.getOperationHandler(), false);
} else {
final OperationStepHandler wrapper;
// For slave host controllers wrap the operation handler to synchronize missing configuration
// TODO better configuration of ignore unaffected configuration
if (localHostControllerInfo.isRemoteDomainControllerIgnoreUnaffectedConfiguration()) {
final PathAddress address = PathAddress.pathAddress(operation.require(OP_ADDR));
wrapper = SyncModelOperationHandlerWrapper.wrapHandler(localHostControllerInfo.getLocalHostName(), operationName, address, entry);
} else {
wrapper = entry.getOperationHandler();
}
context.addModelStep(localReponse, operation, entry.getOperationDefinition(), wrapper, false);
}
} else {
throw new OperationFailedException(ControllerLogger.ROOT_LOGGER.noHandlerForOperation(operationName, PathAddress.pathAddress(operation.get(OP_ADDR))));
}
}
/** Lazily provides a copy of the domain model */
private static class LazyDomainModelProvider implements HostControllerExecutionSupport.DomainModelProvider {
private final OperationContext context;
private Resource domainModelResource;
private LazyDomainModelProvider(OperationContext context) {
this.context = context;
}
public Resource getDomainModel() {
if (domainModelResource == null) {
domainModelResource = context.readResourceFromRoot(PathAddress.EMPTY_ADDRESS, true);
}
return domainModelResource;
}
}
}