Package org.jboss.as.domain.controller

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

/*
* 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.HTTP_INTERFACE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.IGNORED;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.MANAGEMENT_INTERFACES;
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 java.util.concurrent.Semaphore;

import org.jboss.as.controller.BasicModelController;
import org.jboss.as.controller.ControllerResource;
import org.jboss.as.controller.ControllerTransactionContext;
import org.jboss.as.controller.ControllerTransactionSynchronization;
import org.jboss.as.controller.Extension;
import org.jboss.as.controller.ExtensionContext;
import org.jboss.as.controller.ModelController;
import org.jboss.as.controller.ModelProvider;
import org.jboss.as.controller.ModelRemoveOperationHandler;
import org.jboss.as.controller.ModelUpdateOperationHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationContextFactory;
import org.jboss.as.controller.OperationContextImpl;
import org.jboss.as.controller.OperationControllerContext;
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.RuntimeOperationContext;
import org.jboss.as.controller.RuntimeTask;
import org.jboss.as.controller.RuntimeTaskContext;
import org.jboss.as.controller.SynchronousOperationSupport;
import org.jboss.as.controller.client.Operation;
import org.jboss.as.controller.client.OperationAttachments;
import org.jboss.as.controller.descriptions.common.CommonDescriptions;
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.controller.registry.OperationEntry;
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;
import org.jboss.msc.service.ServiceContainer;
import org.jboss.msc.service.ServiceRegistry;
import org.jboss.msc.service.ServiceTarget;

/**
* @author Emanuel Muckenhuber
* @author Brian Stansberry (c) 2011 Red Hat Inc.
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
*/
public class DomainModelImpl extends BasicModelController implements DomainModel {

    // Member Variables Handled Post Refactor
    private ServiceContainer serviceContainer;

    private final OperationContextFactory contextFactory = new OperationContextFactory() {
        @Override
        public OperationContext getOperationContext(final ModelProvider modelSource, final PathAddress address,
                                                    final OperationHandler operationHandler, final Operation operation) {
            final ModelNode subModel = getOperationSubModel(modelSource, operationHandler, address);
            if (provideRuntimeContext(operation.getOperation()) == true) {
                return new RuntimeOperationContextImpl(DomainModelImpl.this, getRegistry(), subModel, modelSource, operation);
            } else {
                return DomainModelImpl.this.getOperationContext(subModel, operationHandler, operation, modelSource);
            }
        }
    };

    // The host name for the host this DomainModel is being use on.
    private String localHostName;
    // Temporary indicator to decide if a runtime context should be provided to operations executed on this model.
    private boolean alwaysProvideRuntimeContext = true;
    // Is this DomainModel running within the HostController of the master DomainController?
    private boolean master;
    // Used for request operation handling.
    private ServerOperationResolver serverOperationResolver;
    // Map of hosts.
    private Map<String, DomainControllerSlaveClient> hosts;
    // The ExtensionContext for this DC.
    private ExtensionContext extensionContext;
    // The write lock we use. Not thread based to allow transaction commit from another thread
    private final Semaphore mutex = new Semaphore(1);

    // The persister for the domain configuration.
    private DelegatingConfigurationPersister domainPersister;
    // The persister for the local host configuration.
    private ExtensibleConfigurationPersister hostPersister;

    private 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
            if (hostPersister != null) {
                hostPersister.store(model.get(HOST, localHostName));
            }
        }

        @Override
        public void marshallAsXml(ModelNode model, OutputStream output) throws ConfigurationPersistenceException {
            if (hostPersister != null) {
                hostPersister.marshallAsXml(model, output);
            }
        }

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

        @Override
        public void successfulBoot() throws ConfigurationPersistenceException {
        }

        @Override
        public String snapshot() {
            return null;
        }

        @Override
        public SnapshotInfo listSnapshots() {
            return NULL_SNAPSHOT_INFO;
        }

        @Override
        public void deleteSnapshot(String name) {
        }

    };

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


    /*
     * The private constructor here allows us to both create and retain a reference to the DelegatingConfigurationPersister.
     */

    private DomainModelImpl(ModelNodeRegistration rootRegistry, ServiceContainer serviceContainer, ExtensibleConfigurationPersister hostPersister, DelegatingConfigurationPersister domainPersister) {
        super(getInitialModel(), domainPersister, rootRegistry);
        this.serviceContainer = serviceContainer;
        this.hostPersister = hostPersister;
        this.domainPersister = domainPersister;

    }

    /**
     * Constructor for use starting at HostControllerBootstrap.
     * <p/>
     * A template model will be started to hold the local host configuration, this will then build up
     * the domain configuration.
     */
    public DomainModelImpl(ModelNodeRegistration rootRegistry, ServiceContainer serviceContainer, ExtensibleConfigurationPersister hostPersister) {
        this(rootRegistry, serviceContainer, hostPersister, new DelegatingConfigurationPersister());
    }


    /**
     * Initialise the remaining domain management aspects of this DomainModel for use with a master domain controller.
     */
    public void initialiseAsMasterDC(final ExtensibleConfigurationPersister configurationPersister, final DeploymentRepository deploymentRepo, final FileRepository fileRepository, final Map<String, DomainControllerSlaveClient> hosts) {
        // Disable providing a RuntimeContext to operation handlers once DC initialisation occurs.
        alwaysProvideRuntimeContext = false;

        ModelNode ourModel = super.getModel();
        DomainModelUtil.updateCoreModel(ourModel);
        master = true;
        domainPersister.setDelegate(configurationPersister);
        ModelNodeRegistration registry = getRegistry();
        extensionContext = DomainModelUtil.initializeMasterDomainRegistry(registry, configurationPersister, deploymentRepo, fileRepository, this);

        registerInternalOperations();
        ModelNodeRegistration hostRegistry = registry.getSubModel(PathAddress.pathAddress(PathElement.pathElement(HOST,getLocalHostName())));
        XmlMarshallingHandler xmlHandler = new XmlMarshallingHandler(this.hostPersister,getHostModel());
        hostRegistry.registerOperationHandler(CommonDescriptions.READ_CONFIG_AS_XML, xmlHandler, xmlHandler, false, OperationEntry.EntryType.PRIVATE);

        this.serverOperationResolver = new ServerOperationResolver(getLocalHostName());
        initializeExtensions(ourModel, extensionContext);
        this.hosts = Collections.unmodifiableMap(hosts);
    }

    /**
     * Initialise the remaining domain management aspects of this DomainMode for use with a slave domain controller.
     */
    public void initialiseAsSlaveDC(final ExtensibleConfigurationPersister configurationPersister, final DeploymentRepository deploymentRepo, final FileRepository fileRepository, final Map<String, DomainControllerSlaveClient> hosts) {
        // Disable providing a RuntimeContext to operation handlers once DC initialisation occurs.
        alwaysProvideRuntimeContext = false;

        master = false;
        domainPersister.setDelegate(configurationPersister);
        ModelNodeRegistration registry = getRegistry();
        extensionContext = DomainModelUtil.initializeSlaveDomainRegistry(registry, configurationPersister, deploymentRepo, fileRepository, this);

        registerInternalOperations();
        ModelNodeRegistration hostRegistry = registry.getSubModel(PathAddress.pathAddress(PathElement.pathElement(HOST,getLocalHostName())));
        XmlMarshallingHandler xmlHandler = new XmlMarshallingHandler(this.hostPersister,getHostModel());
        hostRegistry.registerOperationHandler(CommonDescriptions.READ_CONFIG_AS_XML, xmlHandler, xmlHandler, false, OperationEntry.EntryType.PRIVATE);

        this.serverOperationResolver = new ServerOperationResolver(getLocalHostName());
        this.hosts = Collections.unmodifiableMap(hosts);
    }

    /**
     * Obtains the initial model ready for host registration.
     */
    private static ModelNode getInitialModel() {
        return DomainModelUtil.createBaseModel();
    }

    public boolean isMaster() {
        return master;
    }

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

    public ModelNode getHostModel() {
        ModelNode model = super.getModel().clone();
        // extract host model.
        return model.get(HOST, getLocalHostName());
    }

    public final String getLocalHostName() {
        if (this.localHostName == null) {
            throw new IllegalStateException("LocalHostName not set.");
        }
        return localHostName;
    }

    public void setLocalHostName(String localHostName) {
        if (this.localHostName != null) {
            throw new IllegalStateException("LocalHostName already set.");
        }
        this.localHostName = localHostName;
    }

    @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() {
        String localHostName = getLocalHostName();
        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 OperationControllerContext getOperationControllerContext(Operation operation) {
        return getOperationControllerContext(operation, null);
    }

    @Override
    protected OperationContextFactory getOperationContextFactory() {
        return this.contextFactory;
    }

    protected OperationControllerContext getOperationControllerContext(final Operation operation, final ControllerTransactionContext transaction) {
        boolean forHost = isOperationForHost(operation.getOperation());
        final ConfigurationPersisterProvider persisterProvider = new DualRootConfigurationPersisterProvider(getConfigurationPersisterProvider(), hostPersisterProvider, forHost);
        final TransactionAwareOperationControllerContext occ = new TransactionAwareOperationControllerContext(persisterProvider, transaction);
        if (transaction != null) {
            transaction.registerSynchronization(new ControllerTransactionSynchronization() {

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

                @Override
                public void afterCompletion(boolean status) {
                    occ.doUnlock();
                }});
        }
        return occ;
    }

    @Override
    protected OperationResult doExecute(final OperationContext operationHandlerContext, final Operation operation,
            final OperationHandler operationHandler, final ResultHandler resultHandler,
            final PathAddress address, final OperationControllerContext operationControllerContext) throws OperationFailedException {

        final OperationResult result;

        ControllerTransactionContext transaction = operationControllerContext.getControllerTransactionContext();
        if (transaction == null) {
            result = super.doExecute(operationHandlerContext, operation, operationHandler, resultHandler, address, operationControllerContext);
        } else {

            try {
                ModelNode opNode = operation.getOperation();

                result = operationHandler.execute(operationHandlerContext, opNode, resultHandler);
                ControllerResource txResource = getControllerResource(operationHandlerContext, opNode, operationHandler, resultHandler,
                        address, operationControllerContext);
                if (txResource != null) {
                    transaction.registerResource(txResource);
                }
                return result;
            } catch (OperationFailedException e) {
                transaction.setRollbackOnly();
                throw e;
            }
        }

        if (operationHandlerContext instanceof RuntimeOperationContextImpl) {
            final RuntimeOperationContextImpl runtimeOperationContext = RuntimeOperationContextImpl.class.cast(operationHandlerContext);
            if (runtimeOperationContext.getRuntimeTask() != null) {
                try {
                    runtimeOperationContext.getRuntimeTask().execute(new RuntimeTaskContext() {
                        @Override
                        public ServiceTarget getServiceTarget() {
                            return serviceContainer;
                        }

                        @Override
                        public ServiceRegistry getServiceRegistry() {
                            return serviceContainer;
                        }
                    });
                } catch (OperationFailedException e) {
                    resultHandler.handleFailed(e.getFailureDescription());
                } catch (Exception e) {
                    resultHandler.handleFailed(new ModelNode().set(e.toString()));
                }
            }
        }

        return result;
    }

    @Override
    protected MultiStepOperationController getMultiStepOperationController(Operation executionContext, ResultHandler handler,
            final OperationControllerContext operationControllerContext) throws OperationFailedException {

        DualRootConfigurationPersisterProvider confPerstProvider =
            (DualRootConfigurationPersisterProvider) operationControllerContext.getConfigurationPersisterProvider();
        return new TransactionalMultiStepOperationController(executionContext, handler, operationControllerContext, confPerstProvider);
    }

    protected ControllerResource getControllerResource(final OperationContext context, final ModelNode operation, final OperationHandler operationHandler,
            final ResultHandler resultHandler, final PathAddress address, final OperationControllerContext operationControllerContext) {
        ControllerResource resource = null;

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

        return resource;
    }

    private void initializeExtensions(ModelNode model, ExtensionContext extensionContext) {
        // 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, extensionContext);
    }

    /**
     * Should a RuntimeContext be provided for the execution of this operation?
     *
     * Initially a RuntimeContext is only supplied during the HostController phase of bootstrap,
     * this will be expanded to provide a RuntimeContext for specific operations on the HostController.
     *
     * @param operation
     * @return true if a RuntimeContext should be supplied.
     */
    private boolean provideRuntimeContext(ModelNode operation) {
        return alwaysProvideRuntimeContext;
    }

    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 = SynchronousOperationSupport.execute(operation.clone(domainOp), getOperationControllerContext(operation, delegateTx), this);
            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 && !getLocalHostName().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, getLocalHostName());
        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");
            serverNode.setEmptyList();
            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(getLocalHostName()).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(getLocalHostName(), 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();
        }

        @Override
        public void registerSynchronization(ControllerTransactionSynchronization synchronization) {
            delegate.registerSynchronization(synchronization);
        }

    }

    /**
     * OperationContext for the handlers that should be given access to a RuntimeContext.
     */
    private class RuntimeOperationContextImpl extends OperationContextImpl implements RuntimeOperationContext {

        private RuntimeTask runtimeTask;

        /**
         * Construct a new instance.
         */
        public RuntimeOperationContextImpl(final ModelController controller, final ModelNodeRegistration registry, final ModelNode subModel, ModelProvider modelProvider, final OperationAttachments executionAttachments) {
            super(controller, registry, subModel, modelProvider, executionAttachments);
        }

        @Override
        public RuntimeOperationContext getRuntimeContext() {
            return this;
        }

        RuntimeTask getRuntimeTask() {
            return runtimeTask;
        }

        @Override
        public void setRuntimeTask(RuntimeTask runtimeTask) {
            this.runtimeTask = runtimeTask;
        }

    }

    /**
     * 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();
        }

    }

    private class TransactionAwareOperationControllerContext implements OperationControllerContext {

        private final ConfigurationPersisterProvider persisterProvider;
        private final ControllerTransactionContext transaction;
        private boolean locked = false;

        private TransactionAwareOperationControllerContext(final ConfigurationPersisterProvider persisterProvider,
                final ControllerTransactionContext transaction) {
            this.persisterProvider = persisterProvider;
            this.transaction = transaction;
        }

        @Override
        public ModelProvider getModelProvider() {
            return DomainModelImpl.this.getModelProvider();
        }

        @Override
        public OperationContextFactory getOperationContextFactory() {
            return DomainModelImpl.this.getOperationContextFactory();
        }

        @Override
        public ConfigurationPersisterProvider getConfigurationPersisterProvider() {
            return persisterProvider;
        }

        @Override
        public ControllerTransactionContext getControllerTransactionContext() {
            return transaction;
        }

        @Override
        public boolean lockInterruptibly() throws InterruptedException {
            boolean acquire = !locked;
            if (acquire) {
                mutex.acquireUninterruptibly();
                locked = true;
            }
            return acquire;
        }

        @Override
        public void unlock() {
            if (transaction == null) {
                doUnlock();
            }
        }

        // Hook for the transaction synchronization
        void doUnlock() {
            if (locked) {
                mutex.release();
            }
        }

    }

    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.hostPersister.marshallAsXml(model, output);
                    }

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

                    @Override
                    public void successfulBoot() throws ConfigurationPersistenceException {
                    }

                    @Override
                    public String snapshot() {
                        return null;
                    }

                    @Override
                    public SnapshotInfo listSnapshots() {
                        return NULL_SNAPSHOT_INFO;
                    }

                    @Override
                    public void deleteSnapshot(String name) {
                    }
                };
            }
        };

        protected TransactionalMultiStepOperationController(final Operation operation, final ResultHandler resultHandler,
                final OperationControllerContext injectedOperationControllerContext,
                final DualRootConfigurationPersisterProvider persisterProvider) throws OperationFailedException {

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

        @Override
        protected OperationResult executeStep(final ModelNode step, final ResultHandler stepResultHandler) {
            boolean forHost = isOperationForHost(operation.getOperation());
            return DomainModelImpl.this.execute(operation.clone(step), stepResultHandler, getOperationControllerContext(forHost), resolve);
        }

        private OperationControllerContext getOperationControllerContext(boolean forHost) {
            final ConfigurationPersisterProvider persisterProvider = new DualRootConfigurationPersisterProvider(this, localHostConfigPersisterProvider, forHost);
            return new OperationControllerContext() {

                @Override
                public OperationContextFactory getOperationContextFactory() {
                    return TransactionalMultiStepOperationController.this;
                }

                @Override
                public ModelProvider getModelProvider() {
                    return TransactionalMultiStepOperationController.this;
                }

                @Override
                public ControllerTransactionContext getControllerTransactionContext() {
                    return null;
                }

                @Override
                public ConfigurationPersisterProvider getConfigurationPersisterProvider() {
                    return persisterProvider;
                }

                @Override
                public boolean lockInterruptibly() throws InterruptedException {
                    // Ignore the request for nested calls. The outermost call gets the lock.
                    // This allows controllers to control the locking using whatever impl
                    // they have for the outer call's OperationControllerContext
                    return false;
                }

                @Override
                public void unlock() {
                    // Ignore the request for nested calls. See lockInterruptibly()
                }
            };
        }

        @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() {
            if (transaction == null) {
                TransactionalMultiStepOperationController.this.commit();
            }
            else {
                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 implements UncommittedModelProviderControllerResource {

        private final PathAddress address;
        private final ModelNode subModel;
        private final boolean isRemove;
        private final OperationControllerContext operationControllerContext;

        public DomainModelControllerResource(final OperationHandler handler, final PathAddress address, final ModelNode subModel,
                final OperationControllerContext operationControllerContext) {
            if (handler instanceof ModelUpdateOperationHandler) {
                this.address = address;
                this.subModel = subModel;
                this.isRemove = (handler instanceof ModelRemoveOperationHandler);
                this.operationControllerContext = operationControllerContext;
            }
            else {
                this.address = null;
                this.subModel = null;
                this.isRemove = false;
                this.operationControllerContext = null;
            }
        }

        @Override
        public void commit() {
            if (address != null) {
                final ModelNode model = operationControllerContext.getModelProvider().getModel();
                synchronized (model) {
                    if (isRemove) {
                        address.remove(model);
                    } else {
                        address.navigate(model, true).set(subModel);
                    }
                    persistConfiguration(model, operationControllerContext.getConfigurationPersisterProvider());
                }

            }
        }

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

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

    /**
     * A configuration persister to delegate to another configuration persister once set, this allows
     * a configuration persister to be 'available' before it really is.
     */
    private static class DelegatingConfigurationPersister implements ConfigurationPersister {

        private ConfigurationPersister delegate;

        @Override
        public void store(ModelNode model) throws ConfigurationPersistenceException {
            if (delegate != null) {
                delegate.store(model);
            }
        }

        @Override
        public void marshallAsXml(ModelNode model, OutputStream output) throws ConfigurationPersistenceException {
            if (delegate != null) {
                delegate.marshallAsXml(model, output);
            }
        }

        @Override
        public List<ModelNode> load() throws ConfigurationPersistenceException {
            if (delegate == null) {
                throw new IllegalStateException("Delegate ConfigurationPersister not set.");
            }

            return delegate.load();
        }

        @Override
        public void successfulBoot() throws ConfigurationPersistenceException {
            if (delegate != null) {
                delegate.successfulBoot();
            }
        }

        @Override
        public String snapshot() throws ConfigurationPersistenceException {
            if (delegate == null) {
                throw new IllegalStateException("Delegate ConfigurationPersister not set.");
            }

            return delegate.snapshot();
        }

        @Override
        public SnapshotInfo listSnapshots() {
            if (delegate == null) {
                throw new IllegalStateException("Delegate ConfigurationPersister not set.");
            }

            return delegate.listSnapshots();
        }

        @Override
        public void deleteSnapshot(String name) {
            if (delegate == null) {
                throw new IllegalStateException("Delegate ConfigurationPersister not set.");
            }

            delegate.deleteSnapshot(name);
        }

        void setDelegate(ConfigurationPersister delegate) {
            this.delegate = delegate;
        }
    }
}
TOP

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

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.