Package org.jboss.as.domain.controller

Source Code of org.jboss.as.domain.controller.DomainModelImpl$TransactionalMultiStepOperationController

/*
* 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;

import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.COMPENSATING_OPERATION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.COMPOSITE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DOMAIN_RESULTS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.EXTENSION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.GROUP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HOST;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.IGNORED;
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.OUTCOME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNNING_SERVER;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER_CONFIG;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER_OPERATIONS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.STEPS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS;

import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jboss.as.controller.BasicTransactionalModelController;
import org.jboss.as.controller.ControllerResource;
import org.jboss.as.controller.ControllerTransactionContext;
import org.jboss.as.controller.Extension;
import org.jboss.as.controller.ExtensionContext;
import org.jboss.as.controller.ModelProvider;
import org.jboss.as.controller.ModelUpdateOperationHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.OperationHandler;
import org.jboss.as.controller.OperationResult;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ResultHandler;
import org.jboss.as.controller.client.Operation;
import org.jboss.as.controller.descriptions.common.ExtensionDescription;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.persistence.ConfigurationPersistenceException;
import org.jboss.as.controller.persistence.ConfigurationPersister;
import org.jboss.as.controller.persistence.ConfigurationPersisterProvider;
import org.jboss.as.controller.persistence.ExtensibleConfigurationPersister;
import org.jboss.as.controller.registry.ModelNodeRegistration;
import org.jboss.as.domain.controller.descriptions.DomainDescriptionProviders;
import org.jboss.as.server.deployment.api.DeploymentRepository;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoadException;

/**
* @author Emanuel Muckenhuber
* @author Brian Stansberry (c) 2011 Red Hat Inc.
*/
public class DomainModelImpl extends BasicTransactionalModelController implements DomainModel {

    private final ExtensionContext extensionContext;
    private final ServerOperationResolver serverOperationResolver;
    private final String localHostName;
    private final ModelNode hostModel;
    private final Map<String, DomainControllerSlaveClient> hosts;
    private final ExtensibleConfigurationPersister injectedHostPersister;
    private final boolean master;
    private final ConfigurationPersister delegatingHostPersister = new ConfigurationPersister() {

        @Override
        public void store(ModelNode model) throws ConfigurationPersistenceException {
            // We'll be given the whole model but we only persist the host part
            injectedHostPersister.store(model.get(HOST, localHostName));
        }

        @Override
        public void marshallAsXml(ModelNode model, OutputStream output) throws ConfigurationPersistenceException {
            injectedHostPersister.marshallAsXml(model, output);
        }

        @Override
        public List<ModelNode> load() throws ConfigurationPersistenceException {
            throw new UnsupportedOperationException("load() should not be called as part of operation handling");
        }

    };
    private final ConfigurationPersisterProvider hostPersisterProvider = new ConfigurationPersisterProvider() {
        @Override
        public ConfigurationPersister getConfigurationPersister() {
            return delegatingHostPersister;
        }
    };

    /** Constructor for a master DC. */
    protected DomainModelImpl(final ExtensibleConfigurationPersister configurationPersister, final LocalHostModel localHostProxy,
            final DeploymentRepository deploymentRepo, final FileRepository fileRepository, final Map<String, DomainControllerSlaveClient> hosts) {
        this(null, configurationPersister, localHostProxy, deploymentRepo, fileRepository, hosts, true);
    }

    /** Constructor for a slave DC. */
    protected DomainModelImpl(final ModelNode model, final ExtensibleConfigurationPersister configurationPersister, final LocalHostModel localHostProxy,
            final DeploymentRepository deploymentRepo, final FileRepository fileRepository, final Map<String, DomainControllerSlaveClient> hosts) {
        this(model, configurationPersister, localHostProxy, deploymentRepo, fileRepository, hosts, false);
    }

    protected DomainModelImpl(final ModelNode model, final ExtensibleConfigurationPersister configurationPersister, final LocalHostModel localHostProxy,
            final DeploymentRepository deploymentRepo, final FileRepository fileRepository, final Map<String, DomainControllerSlaveClient> hosts, final boolean master) {
        super(getInitialModel(model), configurationPersister, DomainDescriptionProviders.ROOT_PROVIDER);
        this.localHostName = localHostProxy.getName();
        ModelNodeRegistration registry = getRegistry();
        if (model == null) {
            this.extensionContext = DomainModelUtil.initializeMasterDomainRegistry(registry, configurationPersister, deploymentRepo, fileRepository, this);
        }
        else {
            this.extensionContext = DomainModelUtil.initializeSlaveDomainRegistry(registry, configurationPersister, deploymentRepo, fileRepository, this);
        }
        registry.registerSubModel(PathElement.pathElement(HOST, localHostName), localHostProxy.getRegistry());
        registerInternalOperations();
        this.hostModel = localHostProxy.getHostModel();
        this.injectedHostPersister = localHostProxy.getConfigurationPersister();
        ModelNode ourModel = getModel();
        ourModel.get(HOST, localHostName).set(this.hostModel);
        this.serverOperationResolver = new ServerOperationResolver(localHostName);
        if (model == null) {
            initializeExtensions(ourModel);
        }
        this.hosts = Collections.unmodifiableMap(hosts);
        this.master = master;
    }

    private static ModelNode getInitialModel(final ModelNode model) {
        return model == null ? DomainModelUtil.createCoreModel() : model;
    }

    public boolean isMaster() {
        return master;
    }

    public ModelNode getDomainAndHostModel() {
        return super.getModel().clone();
    }

    @Override
    public ModelNode getDomainModel() {
        ModelNode model = super.getModel().clone();
        // trim off the host model
        model.get(HOST).set(new ModelNode());
        return model;
    }

    // FIXME the domainModel really should not expose hosts
    public Set<String> getHostNames() {
        return new HashSet<String>(hosts.keySet());
    }

    // FIXME the domainModel really should not expose hosts
    public Map<String, DomainControllerSlaveClient> getRemoteHosts(){
        if (hosts.size() == 1 && hosts.containsKey(localHostName)) {
            return Collections.emptyMap();
        }
        Map<String, DomainControllerSlaveClient> hosts = new HashMap<String, DomainControllerSlaveClient>(this.hosts);
        hosts.remove(localHostName);
        return hosts;
    }

    @Override
    protected MultiStepOperationController getMultiStepOperationController(Operation executionContext, ResultHandler handler,
            ModelProvider modelSource, final ConfigurationPersisterProvider persisterProvider, ControllerTransactionContext transaction) throws OperationFailedException {

        DualRootConfigurationPersisterProvider confPerstProvider = (DualRootConfigurationPersisterProvider) persisterProvider;
        return new TransactionalMultiStepOperationController(executionContext, handler, modelSource, confPerstProvider, transaction);
    }

    @Override
    protected ControllerResource getControllerResource(final OperationContext context, final ModelNode operation, final OperationHandler operationHandler,
            final ResultHandler resultHandler, final PathAddress address, final ModelProvider modelProvider, final ConfigurationPersisterProvider persisterProvider) {
        ControllerResource resource = null;

        if (operationHandler instanceof ModelUpdateOperationHandler) {
            resource = new DomainModelControllerResource(operationHandler, address, context.getSubModel(), modelProvider, persisterProvider);
        }

        return resource;
    }

    private void initializeExtensions(ModelNode model) {
        // If we were provided a model, we're a slave and need to initialize all extensions
        if (model != null && model.hasDefined(EXTENSION)) {
            for (Property prop : model.get(EXTENSION).asPropertyList()) {
                try {
                    String module = prop.getValue().get(ExtensionDescription.MODULE).asString();
                    for (Extension extension : Module.loadServiceFromCallerModuleLoader(ModuleIdentifier.fromString(module), Extension.class)) {
                        extension.initialize(extensionContext);
                    }
                } catch (ModuleLoadException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    void setInitialDomainModel(ModelNode domainModel) {
        ModelNode root = getModel();
        // Preserve the "host" subtree
        ModelNode host = root.get(HOST);
        root.set(domainModel);
        root.get(HOST).set(host);
        // Now we know what extensions are needed
        initializeExtensions(domainModel);
    }

    /** {@inheritDoc} */
    @Override
    public OperationResult execute(final Operation operation, final ResultHandler handler, final ControllerTransactionContext transaction) {
        boolean forHost = isOperationForHost(operation.getOperation());
        ConfigurationPersisterProvider persisterProvider = new DualRootConfigurationPersisterProvider(getConfigurationPersisterProvider(), hostPersisterProvider, forHost);
        return execute(operation, handler, getModelProvider(), getOperationContextFactory(), persisterProvider, transaction);
    }

    private static boolean isOperationForHost(ModelNode operation) {
        if (operation.hasDefined(OP_ADDR)) {
            ModelNode address = operation.get(OP_ADDR);
            if (address.asInt() > 0 && HOST.equals(address.get(0).asProperty().getName())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public ModelNode executeForDomain(final Operation operation, final ControllerTransactionContext transaction) {
        ModelNode op = operation.getOperation();
        ParsedOp parsedOp = parseOperation(op, 0);
        ModelNode domainOp = parsedOp.getDomainOperation();
        ModelNode overallResult = null;
        if (domainOp != null) {
            DelegatingControllerTransactionContext delegateTx = new DelegatingControllerTransactionContext(transaction);
            ModelNode opResult = super.execute(operation.clone(domainOp), delegateTx);
            overallResult = createOverallResult(opResult, parsedOp, delegateTx);
        }
        else {
            overallResult = new ModelNode();
            overallResult.get(OUTCOME).set(IGNORED);
        }
        return overallResult;
    }

    private ParsedOp parseOperation(ModelNode operation, int index) {
        String targetHost = null;
        String runningServerTarget = null;
        ModelNode runningServerOp = null;
        PathAddress address = PathAddress.pathAddress(operation.get(OP_ADDR));
        if (address.size() > 0) {
            PathElement first = address.getElement(0);
            if (HOST.equals(first.getKey())) {
                targetHost = first.getValue();
                if (address.size() > 1 && RUNNING_SERVER.equals(address.getElement(1).getKey())) {
                    runningServerTarget = address.getElement(1).getValue();
                    ModelNode relativeAddress = new ModelNode();
                    for (int i = 2; i < address.size(); i++) {
                        PathElement element = address.getElement(i);
                        relativeAddress.add(element.getKey(), element.getValue());
                    }
                    runningServerOp = operation.clone();
                    runningServerOp.get(OP_ADDR).set(relativeAddress);
                }
            }
        }

        ParsedOp result = null;

        if (targetHost != null && !localHostName.equals(targetHost)) {
            result = new SimpleParsedOp(index);
        }
        else if (runningServerTarget != null) {
            result = new SimpleParsedOp(index, runningServerTarget, runningServerOp);
        }
        else if (COMPOSITE.equals(operation.require(OP).asString())) {
            // Recurse into the steps to see what's required
            if (operation.hasDefined(STEPS)) {
                int stepNum = 0;
                List<ParsedOp> parsedSteps = new ArrayList<ParsedOp>();
                for (ModelNode step : operation.get(STEPS).asList()) {
                    parsedSteps.add(parseOperation(step, stepNum++));
                }
                result = new ParsedMultiStepOp(index, parsedSteps);
            }
            else {
                // Will fail later
                result = new SimpleParsedOp(index, operation, address);
            }
        }
        else {
            result = new SimpleParsedOp(index, operation, address);
        }

        return result;
    }

    private ModelNode createOverallResult(ModelNode opResult, ParsedOp parsedOp, DelegatingControllerTransactionContext tx) {
        if (!SUCCESS.equals(opResult.get(OUTCOME).asString())) {
            return opResult;
        }
        ModelNode resultNode = opResult.get(RESULT);

        ModelNode overallResult = new ModelNode();
        overallResult.get(OUTCOME).set(SUCCESS);
        ModelNode domainResult = parsedOp.getFormattedDomainResult(resultNode);
        overallResult.get(RESULT, DOMAIN_RESULTS).set(domainResult);
        ModelNode fullModel = tx.targetResource == null ? null : tx.targetResource.getUncommittedModel();
        if (fullModel == null) {
            fullModel = getDomainAndHostModel();
        }
        ModelNode hostModel = fullModel.get(HOST, localHostName);
        Map<Set<ServerIdentity>, ModelNode> serverOps = parsedOp.getServerOps(fullModel, hostModel);
        ModelNode serverOpsNode = overallResult.get(RESULT, SERVER_OPERATIONS);
        for (Map.Entry<Set<ServerIdentity>, ModelNode> entry : serverOps.entrySet()) {
            ModelNode setNode = serverOpsNode.add();
            ModelNode serverNode = setNode.get("servers");
            for (ServerIdentity server : entry.getKey()) {
                serverNode.add(server.getServerName(), server.getServerGroupName());
            }
            setNode.get(OP).set(entry.getValue());
        }
        ModelNode compOp = opResult.has(COMPENSATING_OPERATION) ? opResult.get(COMPENSATING_OPERATION) : null;
        if (compOp != null) {
            if (compOp.isDefined()) {
                compOp = parsedOp.getFormattedDomainCompensatingOp(compOp);
            }
            overallResult.get(COMPENSATING_OPERATION).set(compOp);
        }
        return overallResult;
    }

    private String getServerGroup(String serverName) {
        return getModel().require(HOST).require(localHostName).require(SERVER_CONFIG).require(serverName).require(GROUP).asString();
    }

    private Map<Set<ServerIdentity>, ModelNode> getServerOperations(ModelNode domainOp, PathAddress domainOpAddress, ModelNode domainModel, ModelNode hostModel) {
        Map<Set<ServerIdentity>, ModelNode> result = null;
        OperationHandler handler = getRegistry().getOperationHandler(domainOpAddress, domainOp.require(OP).asString());
        if (!(handler instanceof ModelUpdateOperationHandler)) {
            result = Collections.emptyMap();
        }
        if (result == null) {
            result = serverOperationResolver.getServerOperations(domainOp, domainOpAddress, domainModel, hostModel);
        }
        return result;
    }

    private interface ParsedOp {
        ModelNode getDomainOperation();
        Map<Set<ServerIdentity>, ModelNode> getServerOps(ModelNode domainModel, ModelNode hostModel);
        ModelNode getFormattedDomainResult(ModelNode resultNode);
        ModelNode getFormattedDomainCompensatingOp(ModelNode unformatted);
    }

    private class SimpleParsedOp implements ParsedOp {
        private final String domainStep;
        private final ModelNode domainOp;
        private final PathAddress domainOpAddress;
        private Map<Set<ServerIdentity>, ModelNode> serverOps;

        private SimpleParsedOp(int index) {
            this.domainStep = "step-" + (index + 1);
            this.domainOp = null;
            this.domainOpAddress = null;
            this.serverOps = Collections.emptyMap();
        }

        private SimpleParsedOp(int index, ModelNode domainOp, final PathAddress domainOpAddress) {
            this.domainStep = "step-" + (index + 1);
            this.domainOp = domainOp;
            this.domainOpAddress = domainOpAddress;
            this.serverOps = null;
        }

        private SimpleParsedOp(int index, String serverName, ModelNode serverOp) {
            this.domainStep = "step-" + (index + 1);
            this.domainOp = null;
            this.domainOpAddress = null;
            final ServerIdentity serverIdentity = new ServerIdentity(localHostName, getServerGroup(serverName), serverName);
            this.serverOps = Collections.singletonMap(Collections.singleton(serverIdentity), serverOp);
        }

        @Override
        public Map<Set<ServerIdentity>, ModelNode> getServerOps(ModelNode domainModel, ModelNode hostModel) {
            Map<Set<ServerIdentity>, ModelNode> result = serverOps;
            if (serverOps == null) {
                result = DomainModelImpl.this.getServerOperations(domainOp, domainOpAddress, domainModel, hostModel);
            }
            return result;
        }

        @Override
        public ModelNode getDomainOperation() {
            return domainOp;
        }

        @Override
        public ModelNode getFormattedDomainResult(ModelNode resultNode) {
            ModelNode formatted = new ModelNode();
            formatted.get(domainStep).set(resultNode);
            return formatted;
        }

        @Override
        public ModelNode getFormattedDomainCompensatingOp(ModelNode unformatted) {
            return unformatted;
        }
    }

    private class ParsedMultiStepOp implements ParsedOp {

        private final String domainStep;
        private final List<ParsedOp> steps;

        private ParsedMultiStepOp(final int index, final List<ParsedOp> steps) {
            this.domainStep = "step-" + (index + 1);
            this.steps = steps;
        }

        @Override
        public Map<Set<ServerIdentity>, ModelNode> getServerOps(ModelNode domainModel, ModelNode hostModel) {
            Map<Set<ServerIdentity>, List<ModelNode>> buildingBlocks = new HashMap<Set<ServerIdentity>, List<ModelNode>>();
            for (ParsedOp step : steps) {
                Map<Set<ServerIdentity>, ModelNode> stepResult = step.getServerOps(domainModel, hostModel);
                if (stepResult.size() == 0) {
                    continue;
                }
                else if (buildingBlocks.size() == 0) {
                    for (Map.Entry<Set<ServerIdentity>, ModelNode> entry : stepResult.entrySet()) {
                        List<ModelNode> list = new ArrayList<ModelNode>();
                        list.add(entry.getValue());
                        buildingBlocks.put(entry.getKey(), list);
                    }
                }
                else {
                    for (Map.Entry<Set<ServerIdentity>, ModelNode> entry : stepResult.entrySet()) {
                        List<ModelNode> existingOp = buildingBlocks.get(entry.getKey());
                        if (existingOp != null) {
                            existingOp.add(entry.getValue());
                        }
                        else {
                            Set<ServerIdentity> newSet = new HashSet<ServerIdentity>(entry.getKey());
                            Set<Set<ServerIdentity>> existingSets = new HashSet<Set<ServerIdentity>>(buildingBlocks.keySet());
                            for (Set<ServerIdentity> existing : existingSets) {
                                Set<ServerIdentity> copy = new HashSet<ServerIdentity>(existing);
                                copy.retainAll(newSet);
                                if (copy.size() > 0) {
                                    if (copy.size() == existing.size()) {
                                        // Just add the new step and store back
                                        buildingBlocks.get(existing).add(entry.getValue());
                                    }
                                    else {
                                        // Add the new step to the intersection; store the old set of steps
                                        // under a key that includes the remainder
                                        List<ModelNode> existingSteps = buildingBlocks.remove(existing);
                                        List<ModelNode> newSteps = new ArrayList<ModelNode>(existingSteps);
                                        buildingBlocks.put(copy, newSteps);
                                        existing.removeAll(copy);
                                        buildingBlocks.put(existing, existingSteps);
                                    }

                                    // no longer track the servers we've stored
                                    newSet.removeAll(copy);
                                }
                            }

                            // Any servers not stored above get their own entry
                            if (newSet.size() > 0) {
                                List<ModelNode> toAdd = new ArrayList<ModelNode>();
                                toAdd.add(entry.getValue());
                                buildingBlocks.put(newSet, toAdd);
                            }
                        }
                    }
                }
            }

            Map<Set<ServerIdentity>, ModelNode> result = null;
            if (buildingBlocks.size() > 0) {
                result = new HashMap<Set<ServerIdentity>, ModelNode>();
                for (Map.Entry<Set<ServerIdentity>, List<ModelNode>> entry : buildingBlocks.entrySet()) {
                    List<ModelNode> ops = entry.getValue();
                    if (ops.size() == 1) {
                        result.put(entry.getKey(), ops.get(0));
                    }
                    else {
                        ModelNode composite = Util.getEmptyOperation(COMPOSITE, new ModelNode());
                        ModelNode steps = composite.get(STEPS);
                        for (ModelNode step : entry.getValue()) {
                            steps.add(step);
                        }
                        result.put(entry.getKey(), composite);
                    }
                }
            }
            else {
                result = Collections.emptyMap();
            }
            return result;
        }

        @Override
        public ModelNode getDomainOperation() {
            ModelNode result = null;
            List<ModelNode> domainSteps = new ArrayList<ModelNode>();
            for (ParsedOp step : steps) {
                ModelNode stepNode = step.getDomainOperation();
                if (stepNode != null) {
                    domainSteps.add(stepNode);
                }
            }
            if (domainSteps.size() == 1) {
                result = domainSteps.get(0);
            }
            else if (domainSteps.size() > 1) {
                ModelNode stepsParam = new ModelNode();
                for (ModelNode stepNode : domainSteps) {
                    stepsParam.add(stepNode);
                }
                result = Util.getEmptyOperation(COMPOSITE, new ModelNode());
                result.get(STEPS).set(stepsParam);
            }
            return result;
        }

        @Override
        public ModelNode getFormattedDomainResult(ModelNode resultNode) {
            ModelNode formatted = new ModelNode();
            int resultStep = 0;
            for (int i = 0; i < steps.size(); i++) {
                ParsedOp po = steps.get(i);
                if (po.getDomainOperation() != null) {
                    String label = "step-" + (++resultStep);
                    ModelNode stepResultNode = resultNode.get(label);
                    ModelNode formattedStepResultNode = po.getFormattedDomainResult(stepResultNode);
                    formatted.get("step-" + (i+1)).set(formattedStepResultNode);
                }
                else {
                    formatted.get("step-" + (i+1), OUTCOME).set(IGNORED);
                }
            }
            return formatted;
        }

        @Override
        public ModelNode getFormattedDomainCompensatingOp(ModelNode unformatted) {
            ModelNode formatted = new ModelNode();
            int resultStep = 0;
            for (int i = 0; i < steps.size(); i++) {
                ParsedOp po = steps.get(i);
                if (po.getDomainOperation() != null) {
                    String label = "step-" + (++resultStep);
                    ModelNode stepResultNode = unformatted.get(label);
                    formatted.get("step-" + (i+1)).set(stepResultNode);
                }
                else {
                    formatted.get("step-" + (i+1)).set(IGNORED);
                }
            }
            return formatted;
        }
    }

    private static interface UncommittedModelProviderControllerResource extends ControllerResource {
        ModelNode getUncommittedModel();
    }

    private class DelegatingControllerTransactionContext implements ControllerTransactionContext {

        private final ControllerTransactionContext delegate;
        private UncommittedModelProviderControllerResource targetResource;

        private DelegatingControllerTransactionContext(final ControllerTransactionContext delegate) {
            this.delegate = delegate;
        }

        @Override
        public ModelNode getTransactionId() {
            return delegate.getTransactionId();
        }

        @Override
        public void registerResource(ControllerResource resource) {
            delegate.registerResource(resource);
            if (resource instanceof UncommittedModelProviderControllerResource) {
                if (targetResource != null) {
                    throw new IllegalStateException("UncommittedModelProviderControllerResource already registered");
                }
                targetResource = (UncommittedModelProviderControllerResource) resource;
            }
        }

        @Override
        public void deregisterResource(ControllerResource resource) {
            delegate.deregisterResource(resource);
        }

        @Override
        public void setRollbackOnly() {
            delegate.setRollbackOnly();
        }

    }

    /**
     * Hack to pass both the domain and host configuration persisters through to
     * TransactionalMultiStepOperationController via the getMultiStepOperationController method.
     */
    private static class DualRootConfigurationPersisterProvider implements ConfigurationPersisterProvider {

        private final ConfigurationPersisterProvider domainProvider;
        private final ConfigurationPersisterProvider hostProvider;
        private final boolean forHost;

        DualRootConfigurationPersisterProvider(final ConfigurationPersisterProvider domainProvider,
                final ConfigurationPersisterProvider hostProvider, final boolean forHost) {
            this.domainProvider = domainProvider;
            this.hostProvider = hostProvider;
            this.forHost = forHost;
        }

        @Override
        public ConfigurationPersister getConfigurationPersister() {
            return forHost ? hostProvider.getConfigurationPersister() : domainProvider.getConfigurationPersister();
        }

    }

    protected class TransactionalMultiStepOperationController extends MultiStepOperationController {

        protected final ControllerTransactionContext transaction;
        protected boolean hostModelUpdated;
        protected final ConfigurationPersisterProvider hostPersisterProvider;
        /** Instead of persisting, this persister records that host model was modified and needs to be persisted when all steps are done. */
        protected final ConfigurationPersisterProvider localHostConfigPersisterProvider = new ConfigurationPersisterProvider() {
            @Override
            public org.jboss.as.controller.persistence.ConfigurationPersister getConfigurationPersister() {
                return new ConfigurationPersister() {
                    @Override
                    public void store(ModelNode model) throws ConfigurationPersistenceException {
                        hostModelUpdated = true;
                    }

                    @Override
                    public void marshallAsXml(ModelNode model, OutputStream output) throws ConfigurationPersistenceException {
                        // an UnsupportedOperationException is also fine if this delegation needs to be removed
                        // in some refactor someday
                        DomainModelImpl.this.injectedHostPersister.marshallAsXml(model, output);
                    }

                    @Override
                    public List<ModelNode> load() throws ConfigurationPersistenceException {
                        throw new UnsupportedOperationException("load() should not be called as part of operation handling");
                    }
                };
            }
        };

        protected TransactionalMultiStepOperationController(final Operation operation, final ResultHandler resultHandler,
                final ModelProvider modelSource, final DualRootConfigurationPersisterProvider persisterProvider,
                final ControllerTransactionContext transaction) throws OperationFailedException {

            super(operation, resultHandler, modelSource, persisterProvider.domainProvider);
            this.transaction = transaction;
            this.hostPersisterProvider = persisterProvider.hostProvider;
        }

        @Override
        protected OperationResult executeStep(final ModelNode step, final ResultHandler stepResultHandler) {
            boolean forHost = isOperationForHost(operationContext.getOperation());
            ConfigurationPersisterProvider persisterProvider = new DualRootConfigurationPersisterProvider(this, localHostConfigPersisterProvider, forHost);
            return DomainModelImpl.this.execute(operationContext.clone(step), stepResultHandler, this, this, persisterProvider, resolve);
        }

        @Override
        protected boolean isModelUpdated() {
            return modelUpdated || hostModelUpdated;
        }

        /** Instead of updating and persisting, we register a resource that does it at commit */
        @Override
        protected void updateModelAndPersist() {
            ControllerResource resource = new UncommittedModelProviderControllerResource() {

                @Override
                public void commit() {
                    TransactionalMultiStepOperationController.this.commit();
                }

                @Override
                public void rollback() {
                    // no-op
                }

                @Override
                public ModelNode getUncommittedModel() {
                    return localModel;
                }

            };
            transaction.registerResource(resource);
        }

        private void commit() {

            final ModelNode model = modelSource.getModel();
            synchronized (model) {
                model.set(localModel);
            }
            if (modelUpdated) {
                DomainModelImpl.this.persistConfiguration(model, injectedConfigPersisterProvider);
            }
            if (hostModelUpdated) {
                DomainModelImpl.this.persistConfiguration(model, hostPersisterProvider);
            }
        }

    }

    private class DomainModelControllerResource extends UpdateModelControllerResource implements UncommittedModelProviderControllerResource {

        public DomainModelControllerResource(final OperationHandler handler, final PathAddress address, final ModelNode subModel,
                final ModelProvider modelProvider, final ConfigurationPersisterProvider persisterProvider) {
            super(handler, address, subModel, modelProvider, persisterProvider);
        }

        @Override
        public ModelNode getUncommittedModel() {
            ModelNode model = null;
            if (address != null) {
                model = modelProvider.getModel();
                synchronized (model) {
                    model = model.clone();
                }
                if (isRemove) {
                    address.remove(model);
                } else {
                    address.navigate(model, true).set(subModel);
                }
            }
            return model;
        }
    }
}
TOP

Related Classes of org.jboss.as.domain.controller.DomainModelImpl$TransactionalMultiStepOperationController

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.