Package org.jboss.as.controller

Source Code of org.jboss.as.controller.OperationContextImpl$ContextServiceBuilder

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

import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CANCELLED;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILED;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OPERATION_REQUIRES_RELOAD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OPERATION_REQUIRES_RESTART;
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.RESPONSE_HEADERS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ROLLED_BACK;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNTIME_UPDATE_SKIPPED;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS;

import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.atomic.AtomicReference;

import org.jboss.as.controller.client.MessageSeverity;
import org.jboss.as.controller.client.OperationAttachments;
import org.jboss.as.controller.client.OperationMessageHandler;
import org.jboss.as.controller.persistence.ConfigurationPersistenceException;
import org.jboss.as.controller.persistence.ConfigurationPersister;
import org.jboss.as.controller.registry.DelegatingImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
import org.jboss.logging.Logger;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.service.AbstractServiceListener;
import org.jboss.msc.service.BatchServiceTarget;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceListener;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceRegistry;
import org.jboss.msc.service.ServiceRegistryException;
import org.jboss.msc.service.ServiceTarget;
import org.jboss.msc.value.ImmediateValue;
import org.jboss.msc.value.Value;

/**
* Operation context implementation.
*
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
*/
final class OperationContextImpl implements OperationContext {

    private static final Logger log = Logger.getLogger("org.jboss.as.controller");

    private final ModelControllerImpl modelController;
    private final Type contextType;
    private final EnumSet<ContextFlag> contextFlags;
    private final OperationMessageHandler messageHandler;
    private final Thread initiatingThread;
    private final EnumMap<Stage, Deque<Step>> steps;
    private final ModelController.OperationTransactionControl transactionControl;
    private final ServiceTarget serviceTarget;
    private final Map<ServiceName, ServiceController<?>> realRemovingControllers = new HashMap<ServiceName, ServiceController<?>>();
    private final boolean booting;
    private final OperationAttachments attachments;
    private final ControlledProcessState processState;
    /** Tracks whether any steps have gotten write access to the model */
    private final Set<PathAddress> affectsModel;
    /** Tracks whether any steps have gotten write access to the management resource registration*/
    private boolean affectsResourceRegistration;

    private boolean respectInterruption = true;
    private PathAddress modelAddress;
    private Stage currentStage = Stage.MODEL;
    /**
     * The result of the current operation.
     */
    private ModelNode response;
    /**
     * The current operation body being executed.
     */
    private ModelNode operation;

    private Resource model;
    private ResultAction resultAction;
    /** Tracks whether any steps have gotten write access to the runtime */
    private boolean affectsRuntime;
    /** Tracks whether we've detected cancellation */
    private boolean cancelled;
    /** Current number of nested levels of completeStep() calls */
    private int depth;
    /** Write lock acquisition depth */
    private int lockDepth;
    /** Container monitor acquisition depth */
    private int containerMonitorDepth;
    /** Stamp to hand back to revert a reload/restartRequired call */
    private StampHolder restartStampHolder;

    enum ContextFlag {
        ROLLBACK_ON_FAIL,
    }

    OperationContextImpl(final ModelControllerImpl modelController, final Type contextType, final EnumSet<ContextFlag> contextFlags,
                         final OperationMessageHandler messageHandler, final OperationAttachments attachments,
                         final Resource model, final ModelController.OperationTransactionControl transactionControl,
                         final ControlledProcessState processState, final boolean booting) {
        this.contextType = contextType;
        this.transactionControl = transactionControl;
        this.booting = booting;
        this.model = model;
        this.modelController = modelController;
        this.messageHandler = messageHandler;
        this.attachments = attachments;
        this.processState = processState;
        response = new ModelNode().setEmptyObject();
        steps = new EnumMap<Stage, Deque<Step>>(Stage.class);
        for (Stage stage : Stage.values()) {
            steps.put(stage, new ArrayDeque<Step>());
        }
        affectsModel = new HashSet<PathAddress>(1);
        initiatingThread = Thread.currentThread();
        this.contextFlags = contextFlags;
        serviceTarget = new ContextServiceTarget(modelController);
    }

    public InputStream getAttachmentStream(final int index) {
        if (attachments == null) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        return attachments.getInputStreams().get(index);
    }

    public int getAttachmentStreamCount() {
        return attachments == null ? 0 : attachments.getInputStreams().size();
    }

    public void addStep(final OperationStepHandler step, final Stage stage) throws IllegalArgumentException {
        addStep(response, operation, step, stage);
    }

    public void addStep(final ModelNode operation, final OperationStepHandler step, final Stage stage) throws IllegalArgumentException {
        addStep(response, operation, step, stage);
    }

    public void addStep(final ModelNode response, final ModelNode operation, final OperationStepHandler step, final Stage stage) throws IllegalArgumentException {
        assert Thread.currentThread() == initiatingThread;
        if (response == null) {
            throw new IllegalArgumentException("response is null");
        }
        if (operation == null) {
            throw new IllegalArgumentException("operation is null");
        }
        if (step == null) {
            throw new IllegalArgumentException("step is null");
        }
        if (stage == null) {
            throw new IllegalArgumentException("stage is null");
        }
        if (currentStage == Stage.DONE) {
            throw new IllegalStateException("Operation already complete");
        }
        if (stage.compareTo(currentStage) < 0 && (stage != Stage.IMMEDIATE || currentStage == Stage.DONE)) {
            throw new IllegalStateException("Stage " + stage + " is already complete");
        }
        if (contextType == Type.MANAGEMENT && stage.compareTo(Stage.MODEL) > 0) {
            throw new IllegalArgumentException("Invalid step stage for this context type");
        }
        if (stage == Stage.DOMAIN && contextType != Type.HOST) {
            throw new IllegalStateException("Stage " + stage + " is not valid for context type " + contextType);
        }
        if (stage == Stage.DONE) {
            throw new IllegalArgumentException("Invalid step stage specified");
        }
        if (stage == Stage.IMMEDIATE) {
            steps.get(currentStage).addFirst(new Step(step, response, operation));
        } else {
            steps.get(stage).addLast(new Step(step, response, operation));
        }
    }

    public ModelNode getFailureDescription() {
        return response.get(FAILURE_DESCRIPTION);
    }

    public boolean hasFailureDescription() {
        return response.has(FAILURE_DESCRIPTION);
    }

    public ResultAction completeStep() {
        try {
            ResultAction action = doCompleteStep();
            if (action == ResultAction.KEEP) {
                report(MessageSeverity.INFO, "Operation succeeded, committing");
            } else {
                report(MessageSeverity.INFO, "Operation rolling back");
            }
            return action;
        } finally {
            respectInterruption = false;
        }
    }

    /**
     * Perform the work of completing a step.
     *
     * @return the result action for the step which has just completed
     */
    private ResultAction doCompleteStep() {
        assert Thread.currentThread() == initiatingThread;
        // If the operation is done, fail.
        if (currentStage == null) {
            throw new IllegalStateException("Operation already complete");
        }
        // Cancellation is detected via interruption.
        if (Thread.currentThread().isInterrupted()) {
            cancelled = true;
        }
        // Rollback when any of:
        // 1. operation is cancelled
        // 2. operation failed in model phase
        // 3. operation failed in runtime/verify and rollback_on_fail is set
        // 4. isRollbackOnly
        ModelNode response = this.response;
        if (cancelled) {
            response.get(OUTCOME).set(CANCELLED);
            response.get(FAILURE_DESCRIPTION).set("Operation cancelled");
            response.get(ROLLED_BACK).set(true);
            resultAction = ResultAction.ROLLBACK;
            return resultAction;
        }
        if (response.hasDefined(FAILURE_DESCRIPTION) && (contextFlags.contains(ContextFlag.ROLLBACK_ON_FAIL) || currentStage == Stage.MODEL)) {
            response.get(OUTCOME).set(FAILED);
            response.get(ROLLED_BACK).set(true);
            resultAction = ResultAction.ROLLBACK;
            return resultAction;
        }
        if (resultAction == ResultAction.ROLLBACK) {
            return ResultAction.ROLLBACK;
        }
        // Locate the next step to execute.
        Step step = null;
        do {
            step = steps.get(currentStage).pollFirst();
            if (step == null) {
                // No steps remain in this stage; proceed to the next stage.
                if (currentStage.hasNext()) {
                    currentStage = currentStage.next();
                    if (contextType == Type.MANAGEMENT && currentStage == Stage.MODEL.next()) {
                        // Management mode; we do not proceed past the MODEL stage.
                        currentStage = Stage.DONE;
                    } else if (affectsRuntime && currentStage == Stage.VERIFY) {
                        // a change was made to the runtime.  Thus, we must wait for stability before resuming in to verify.
                        try {
                            modelController.awaitContainerMonitor(true, 1);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            cancelled = true;
                            response.get(OUTCOME).set(CANCELLED);
                            response.get(FAILURE_DESCRIPTION).set("Operation cancelled");
                            response.get(ROLLED_BACK).set(true);
                            return ResultAction.ROLLBACK;
                        }
                    }
                }
            } else {
                return executeStep(step);
            }
        } while (currentStage != Stage.DONE);
        final AtomicReference<ResultAction> ref = new AtomicReference<ResultAction>(transactionControl == null ? ResultAction.KEEP : ResultAction.ROLLBACK);
        // No more steps, verified operation is a success!
        ConfigurationPersister.PersistenceResource persistenceResource = null;
        if (isModelAffected() && resultAction != ResultAction.ROLLBACK) try {
            persistenceResource = modelController.writeModel(model, affectsModel);
        } catch (ConfigurationPersistenceException e) {
            response.get(OUTCOME).set(FAILED);
            response.get(FAILURE_DESCRIPTION).set("Failed to persist configuration change: " + e);
            return resultAction = ResultAction.ROLLBACK;
        }
        if (transactionControl != null) {
            if (log.isTraceEnabled()) {
                log.trace("Prepared response is " + response);
            }
            transactionControl.operationPrepared(new ModelController.OperationTransaction() {
                public void commit() {
                    ref.set(ResultAction.KEEP);
                }

                public void rollback() {
                    ref.set(ResultAction.ROLLBACK);
                }
            }, response);
        }
        resultAction = ref.get();
        if (persistenceResource != null) {
            if (resultAction == ResultAction.ROLLBACK) {
                persistenceResource.rollback();
            } else {
                persistenceResource.commit();
            }
        }
        return resultAction;
    }

    private ResultAction executeStep(final Step step) {
        PathAddress oldModelAddress = modelAddress;
        ModelNode oldOperation = operation;
        ModelNode oldResponse = this.response;
        StampHolder oldRestartStamp = restartStampHolder;
        Stage stepStage = null;
        ModelNode response = null;
        try {
            // next step runs at the next depth level
            depth++;
            response = this.response = step.response;
            this.restartStampHolder = step.restartStamp;
            ModelNode newOperation = operation = step.operation;
            modelAddress = PathAddress.pathAddress(newOperation.get(OP_ADDR));
            try {
                ClassLoader oldTccl = SecurityActions.setThreadContextClassLoader(step.getClass());
                try {
                    step.handler.execute(this, newOperation);
                } finally {
                    SecurityActions.setThreadContextClassLoader(oldTccl);
                }
            } catch (OperationFailedException ofe) {
                if (currentStage != Stage.DONE) {
                    // Handler threw OFE before calling completeStep(); that's equivalent to
                    // a request that we set the failure description and call completeStep()
                    response.get(FAILURE_DESCRIPTION).set(ofe.getFailureDescription());
                    log.errorf("Operation (%s) failed - address: (%s) - failure description: %s", operation.get(OP), operation.get(OP_ADDR), response.get(FAILURE_DESCRIPTION));
                    completeStep();
                }
                else {
                    // Handler threw OFE after calling completeStep()
                    // Throw it on and let standard error handling deal with it
                    throw ofe;
                }
            }
            assert resultAction != null;
        } catch (Throwable t) {
            log.errorf(t, "Operation (%s) failed - address: (%s)", operation.get(OP), operation.get(OP_ADDR));
            // If this block is entered, then the next step failed
            // The question is, did it fail before or after calling completeStep()?
            if (currentStage != Stage.DONE) {
                // It failed before, so consider the operation a failure.
                if (! response.hasDefined(FAILURE_DESCRIPTION)) {
                    response.get(FAILURE_DESCRIPTION).set("Operation handler failed: " + t);
                }
                response.get(OUTCOME).set(FAILED);
                resultAction = getFailedResultAction(t);
                if (resultAction == ResultAction.ROLLBACK) {
                    response.get(ROLLED_BACK).set(true);
                }
                return resultAction;
            } else {
                if (resultAction != ResultAction.KEEP) {
                    response.get(ROLLED_BACK).set(true);
                }
                response.get(OUTCOME).set(response.hasDefined(FAILURE_DESCRIPTION) ? FAILED : SUCCESS);
                // It failed after!  Just return, ignore the failure
                report(MessageSeverity.WARN, "Step handler " + step.handler + " failed after completion");
                return resultAction;
            }
        } finally {
            modelAddress = oldModelAddress;
            operation = oldOperation;
            this.response = oldResponse;
            this.restartStampHolder = oldRestartStamp;
            if (lockDepth == depth) {
                modelController.releaseLock();
                lockDepth = 0;
            }
            if (containerMonitorDepth == depth) {
                awaitContainerMonitor();
                modelController.releaseContainerMonitor();
                containerMonitorDepth = 0;
            }
            stepStage = currentStage;
            if (--depth == 0) {
                // We're returning from the outermost completeStep()
                // Null out the current stage to disallow further access to the context
                currentStage = null;
            }
        }

        if (stepStage != Stage.DONE) {
            // This is a failure because the next step failed to call completeStep().
            // Either an exception occurred beforehand, or the implementer screwed up.
            // If an exception occurred, then this will have no effect.
            // If the implementer screwed up, then we're essentially fixing the context state and treating
            // the overall operation as a failure.
            currentStage = currentStage != null ? Stage.DONE : null;
            if (! response.hasDefined(FAILURE_DESCRIPTION)) {
                response.get(FAILURE_DESCRIPTION).set("Operation handler failed to complete");
            }
            response.get(OUTCOME).set(FAILED);
            response.get(ROLLED_BACK).set(true);
            resultAction = getFailedResultAction(null);
            return resultAction;
        } else {
            response.get(OUTCOME).set(response.hasDefined(FAILURE_DESCRIPTION) ? FAILED : SUCCESS);
        }
        if (resultAction == ResultAction.ROLLBACK) {
            response.get(OUTCOME).set(FAILED);
            response.get(ROLLED_BACK).set(true);
        }
        return resultAction;
    }

    /**
     * Decide whether failure should trigger a rollback.
     *
     * @param cause the cause of the failure, or {@code null} if failure is not the result of catching a throwable
     * @return the result action
     */
    private ResultAction getFailedResultAction(Throwable cause) {
        if (currentStage == Stage.MODEL || cancelled || contextFlags.contains(ContextFlag.ROLLBACK_ON_FAIL)
                || isRollbackOnly() || (cause != null && !(cause instanceof OperationFailedException))) {
            return ResultAction.ROLLBACK;
        }
        return ResultAction.KEEP;
    }

    public Type getType() {
        assert Thread.currentThread() == initiatingThread;
        return contextType;
    }

    public boolean isBooting() {
        return booting;
    }

    public boolean isRollbackOnly() {
        return resultAction == ResultAction.ROLLBACK;
    }

    public void setRollbackOnly() {
        resultAction = ResultAction.ROLLBACK;
    }

    private boolean isRollingBack() {
        return currentStage == Stage.DONE && resultAction == ResultAction.ROLLBACK;
    }

    @Override
    public void reloadRequired() {
        if (processState.isReloadSupported()) {
            this.restartStampHolder.restartStamp = processState.setReloadRequired();
            this.response.get(RESPONSE_HEADERS, OPERATION_REQUIRES_RELOAD).set(true);
        } else {
            restartRequired();
        }
    }

    @Override
    public void restartRequired() {
        this.restartStampHolder.restartStamp = processState.setRestartRequired();
        this.response.get(RESPONSE_HEADERS, OPERATION_REQUIRES_RESTART).set(true);
    }

    @Override
    public void revertReloadRequired() {
        if (processState.isReloadSupported()) {
            processState.revertReloadRequired(this.restartStampHolder.restartStamp);
            if (this.response.get(RESPONSE_HEADERS).hasDefined(OPERATION_REQUIRES_RELOAD)) {
                this.response.get(RESPONSE_HEADERS).remove(OPERATION_REQUIRES_RELOAD);
                if (this.response.get(RESPONSE_HEADERS).asInt() == 0) {
                    this.response.remove(RESPONSE_HEADERS);
                }
            }
        }
        else {
            revertRestartRequired();
        }
    }

    @Override
    public void revertRestartRequired() {
        processState.revertRestartRequired(this.restartStampHolder.restartStamp);
        if (this.response.get(RESPONSE_HEADERS).hasDefined(OPERATION_REQUIRES_RESTART)) {
            this.response.get(RESPONSE_HEADERS).remove(OPERATION_REQUIRES_RESTART);
            if (this.response.get(RESPONSE_HEADERS).asInt() == 0) {
                this.response.remove(RESPONSE_HEADERS);
            }
        }
    }

    @Override
    public void runtimeUpdateSkipped() {
        this.response.get(RESPONSE_HEADERS, RUNTIME_UPDATE_SKIPPED).set(true);
    }


    public ManagementResourceRegistration getResourceRegistrationForUpdate() {
        final PathAddress address = modelAddress;
        assert Thread.currentThread() == initiatingThread;
        Stage currentStage = this.currentStage;
        if (currentStage == null) {
            throw new IllegalStateException("Operation already complete");
        }
        if (currentStage != Stage.MODEL) {
            throw new IllegalStateException("Stage MODEL is already complete");
        }
        if (!affectsResourceRegistration) {
            takeWriteLock();
            affectsResourceRegistration = true;
        }
        return modelController.getRootRegistration().getSubModel(address);
    }


    public ImmutableManagementResourceRegistration getResourceRegistration() {
        final PathAddress address = modelAddress;
        assert Thread.currentThread() == initiatingThread;
        Stage currentStage = this.currentStage;
        if (currentStage == null || currentStage == Stage.DONE) {
            throw new IllegalStateException("Operation already complete");
        }
        ImmutableManagementResourceRegistration delegate = modelController.getRootRegistration().getSubModel(address);
        return delegate == null ? null : new DelegatingImmutableManagementResourceRegistration(delegate);
    }

    public ServiceRegistry getServiceRegistry(final boolean modify) throws UnsupportedOperationException {
        assert Thread.currentThread() == initiatingThread;
        Stage currentStage = this.currentStage;
        if (currentStage == null) {
            throw new IllegalStateException("Operation already complete");
        }
        if (! (currentStage == Stage.RUNTIME || currentStage == Stage.VERIFY || isRollingBack() && ! modify)) {
            throw new IllegalStateException("Get service registry only supported in runtime operations");
        }
        if (modify && !affectsRuntime) {
            takeWriteLock();
            affectsRuntime = true;
            acquireContainerMonitor();
            awaitContainerMonitor();
        }
        return modelController.getServiceRegistry();
    }

    public ServiceController<?> removeService(final ServiceName name) throws UnsupportedOperationException {
        assert Thread.currentThread() == initiatingThread;
        Stage currentStage = this.currentStage;
        if (currentStage == null) {
            throw new IllegalStateException("Operation already complete");
        }
        if (currentStage != Stage.RUNTIME && currentStage != Stage.VERIFY && !isRollingBack()) {
            throw new IllegalStateException("Service removal only supported in runtime operations");
        }
        if (!affectsRuntime) {
            takeWriteLock();
            affectsRuntime = true;
            acquireContainerMonitor();
            awaitContainerMonitor();
        }
        ServiceController<?> controller = modelController.getServiceRegistry().getService(name);
        if (controller != null) {
            doRemove(controller);
        }
        return controller;
    }

    public void removeService(final ServiceController<?> controller) throws UnsupportedOperationException {
        assert Thread.currentThread() == initiatingThread;
        Stage currentStage = this.currentStage;
        if (currentStage == null) {
            throw new IllegalStateException("Operation already complete");
        }
        if (currentStage != Stage.RUNTIME && currentStage != Stage.VERIFY && !isRollingBack()) {
            throw new IllegalStateException("Service removal only supported in runtime operations");
        }
        if (!affectsRuntime) {
            takeWriteLock();
            affectsRuntime = true;
            acquireContainerMonitor();
            awaitContainerMonitor();
        }
        doRemove(controller);
    }

    private void doRemove(final ServiceController<?> controller) {
        controller.addListener(new AbstractServiceListener<Object>() {
            public void listenerAdded(final ServiceController<?> controller) {
                final Map<ServiceName, ServiceController<?>> map = realRemovingControllers;
                synchronized (map) {
                    map.put(controller.getName(), controller);
                    controller.setMode(ServiceController.Mode.REMOVE);
                }
            }

            public void transition(final ServiceController<? extends Object> controller, final ServiceController.Transition transition) {
                switch (transition) {
                    case REMOVING_to_REMOVED:
                    case REMOVING_to_DOWN: {
                        final Map<ServiceName, ServiceController<?>> map = realRemovingControllers;
                        synchronized (map) {
                            if (map.get(controller.getName()) == controller) {
                                map.remove(controller.getName());
                                map.notifyAll();
                            }
                        }
                        break;
                    }
                }
            }
        });
    }

    public ServiceTarget getServiceTarget() throws UnsupportedOperationException {
        assert Thread.currentThread() == initiatingThread;
        Stage currentStage = this.currentStage;
        if (currentStage == null) {
            throw new IllegalStateException("Operation already complete");
        }
        if (currentStage != Stage.RUNTIME && currentStage != Stage.VERIFY && !isRollingBack()) {
            throw new IllegalStateException("Get service target only supported in runtime operations");
        }
        if (!affectsRuntime) {
            takeWriteLock();
            affectsRuntime = true;
            acquireContainerMonitor();
            awaitContainerMonitor();
        }
        return serviceTarget;
    }

    private void takeWriteLock() {
        if (lockDepth == 0) {
            if (currentStage == Stage.DONE) {
                throw new IllegalStateException("Invalid modification after completed step");
            }
            try {
                modelController.acquireLock(respectInterruption);
                lockDepth = depth;
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new CancellationException("Operation cancelled asynchronously");
            }
        }
    }

    private void acquireContainerMonitor() {
        if (containerMonitorDepth == 0) {
            if (currentStage == Stage.DONE) {
                throw new IllegalStateException("Invalid modification after completed step");
            }
            modelController.acquireContainerMonitor();
            containerMonitorDepth = depth;
        }
    }

    private void awaitContainerMonitor() {
        try {
            modelController.awaitContainerMonitor(respectInterruption, 1);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new CancellationException("Operation cancelled asynchronously");
        }
    }

    public ModelNode readModel(final PathAddress requestAddress) {
        final PathAddress address = modelAddress.append(requestAddress);
        assert Thread.currentThread() == initiatingThread;
        Stage currentStage = this.currentStage;
        if (currentStage == null) {
            throw new IllegalStateException("Operation already complete");
        }
        Resource model = this.model;
        for (final PathElement element : address) {
            model = model.requireChild(element);
        }
        // recursively read the model
        return Resource.Tools.readModel(model);
    }

    public ModelNode readModelForUpdate(final PathAddress requestAddress) {
        final PathAddress address = modelAddress.append(requestAddress);
        assert Thread.currentThread() == initiatingThread;
        Stage currentStage = this.currentStage;
        if (currentStage == null) {
            throw new IllegalStateException("Operation already complete");
        }
        if (currentStage != Stage.MODEL) {
            throw new IllegalStateException("Stage MODEL is already complete");
        }
        if (affectsModel.size() == 0) {
            takeWriteLock();
            model = model.clone();
        }
        affectsModel.add(address);
        Resource model = this.model;
        final Iterator<PathElement> i = address.iterator();
        while (i.hasNext()) {
            final PathElement element = i.next();
            if (element.isMultiTarget()) {
                throw new IllegalArgumentException("Cannot write to *");
            }
            if (! i.hasNext()) {
                final String key = element.getKey();
                if(! model.hasChild(element)) {
                    final PathAddress parent = address.subAddress(0, address.size() -1);
                    final Set<String> childrenNames = modelController.getRootRegistration().getChildNames(parent);
                    if(!childrenNames.contains(key)) {
                        throw new IllegalStateException("no child-type " + key);
                    }
                    // TODO check cardinality
                    final Resource newModel = Resource.Factory.create();
                    model.registerChild(element, newModel);
                    model = newModel;
                } else {
                    model = model.requireChild(element);
                }
            } else {
                model = model.requireChild(element);
            }
        }
        if(model == null) {
            throw new IllegalStateException();
        }
        return model.getModel();
    }

    public Resource readResource(PathAddress requestAddress) {
        final PathAddress address = modelAddress.append(requestAddress);
        assert Thread.currentThread() == initiatingThread;
        Stage currentStage = this.currentStage;
        if (currentStage == null) {
            throw new IllegalStateException("Operation already complete");
        }
        Resource model = this.model;
        for (final PathElement element : address) {
            model = model.requireChild(element);
        }
        return model.clone();
    }

    public Resource readResourceForUpdate(PathAddress requestAddress) {
        final PathAddress address = modelAddress.append(requestAddress);
        assert Thread.currentThread() == initiatingThread;
        Stage currentStage = this.currentStage;
        if (currentStage == null) {
            throw new IllegalStateException("Operation already complete");
        }
        if (currentStage != Stage.MODEL) {
            throw new IllegalStateException("Stage MODEL is already complete");
        }
        if (affectsModel.size() == 0) {
            takeWriteLock();
            model = model.clone();
        }
        affectsModel.add(address);
        Resource resource = this.model;
        final Iterator<PathElement> i = address.iterator();
        while (i.hasNext()) {
            final PathElement element = i.next();
            if (element.isMultiTarget()) {
                throw new IllegalArgumentException("Cannot write to *");
            }
            resource = resource.requireChild(element);
        }
        return resource;
    }

    public Resource createResource(PathAddress requestAddress) {
        final PathAddress address = modelAddress.append(requestAddress);
        assert Thread.currentThread() == initiatingThread;
        Stage currentStage = this.currentStage;
        if (currentStage == null) {
            throw new IllegalStateException("Operation already complete");
        }
        if (currentStage != Stage.MODEL) {
            throw new IllegalStateException("Stage MODEL is already complete");
        }
        if (affectsModel.size() == 0) {
            takeWriteLock();
            model = model.clone();
        }
        affectsModel.add(address);
        Resource model = this.model;
        final Iterator<PathElement> i = address.iterator();
        while (i.hasNext()) {
            final PathElement element = i.next();
            if (element.isMultiTarget()) {
                throw new IllegalArgumentException("Cannot write to *");
            }
            if (! i.hasNext()) {
                final String key = element.getKey();
                if(model.hasChild(element)) {
                    throw new IllegalStateException("duplicate resource " + address);
                } else {
                    final PathAddress parent = address.subAddress(0, address.size() -1);
                    final Set<String> childrenNames = modelController.getRootRegistration().getChildNames(parent);
                    if(!childrenNames.contains(key)) {
                        throw new IllegalStateException("no child-type " + key);
                    }
                    // TODO check cardinality
                    final Resource newModel = Resource.Factory.create();
                    model.registerChild(element, newModel);
                    model = newModel;
                }
            } else {
                model = model.requireChild(element);
            }
        }
        return model;
    }

    public Resource removeResource(final PathAddress requestAddress) {
        final PathAddress address = modelAddress.append(requestAddress);
        assert Thread.currentThread() == initiatingThread;
        Stage currentStage = this.currentStage;
        if (currentStage == null) {
            throw new IllegalStateException("Operation already complete");
        }
        if (currentStage != Stage.MODEL) {
            throw new IllegalStateException("Stage MODEL is already complete");
        }
        if (affectsModel.size() == 0) {
            takeWriteLock();
            model = model.clone();
        }
        affectsModel.add(address);
        Resource model = this.model;
        final Iterator<PathElement> i = address.iterator();
        while (i.hasNext()) {
            final PathElement element = i.next();
            if (element.isMultiTarget()) {
                throw new IllegalArgumentException("Cannot remove *");
            }
            if (! i.hasNext()) {
                model = model.removeChild(element);
            } else {
                model = model.requireChild(element);
            }
        }
        return model;
    }

    public void acquireControllerLock() {
        takeWriteLock();
    }

    public Resource getRootResource() {
        final Resource readOnlyModel = this.model;
        return readOnlyModel.clone();
    }

    public boolean isModelAffected() {
        return affectsModel.size() > 0;
    }

    public boolean isRuntimeAffected() {
        return affectsRuntime;
    }

    public boolean isResourceRegistryAffected() {
        return affectsResourceRegistration;
    }

    public Stage getCurrentStage() {
        return currentStage;
    }

    public void report(final MessageSeverity severity, final String message) {
        try {
            messageHandler.handleReport(severity, message);
        } catch (Throwable t) {
            // ignored
        }
    }

    public ModelNode getResult() {
        return response.get(RESULT);
    }

    public boolean hasResult() {
        return response.has(RESULT);
    }

    static class Step {
        private final OperationStepHandler handler;
        private final ModelNode response;
        private final ModelNode operation;
        private final StampHolder restartStamp  = new StampHolder();

        private Step(final OperationStepHandler handler, final ModelNode response, final ModelNode operation) {
            this.handler = handler;
            this.response = response;
            this.operation = operation;
            // Create the outcome node early so it appears at the top of the response
            response.get(OUTCOME);
        }
    }

    /**
     *  Simple wrapper object to allow the context and the current Step to share a reference to the object returned by
     *  {@link ModelControllerImpl#setReloadRequired()} or
     *  {@link ModelControllerImpl#setRestartRequired()}
     */
    static class StampHolder {
        private Object restartStamp;
    }

    class ContextServiceTarget implements ServiceTarget {

        private final ModelControllerImpl modelController;

        ContextServiceTarget(final ModelControllerImpl modelController) {
            this.modelController = modelController;
        }

        public <T> ServiceBuilder<T> addServiceValue(final ServiceName name, final Value<? extends Service<T>> value) {
            final ServiceBuilder<T> realBuilder = modelController.getServiceTarget().addServiceValue(name, value);
            return new ContextServiceBuilder<T>(realBuilder, name);
        }

        public <T> ServiceBuilder<T> addService(final ServiceName name, final Service<T> service) {
            return addServiceValue(name, new ImmediateValue<Service<T>>(service));
        }

        public ServiceTarget addListener(final ServiceListener<Object> listener) {
            throw new UnsupportedOperationException();
        }

        public ServiceTarget addListener(final ServiceListener<Object>... listeners) {
            throw new UnsupportedOperationException();
        }

        public ServiceTarget addListener(final Collection<ServiceListener<Object>> listeners) {
            throw new UnsupportedOperationException();
        }

        public ServiceTarget addListener(final ServiceListener.Inheritance inheritance, final ServiceListener<Object> listener) {
            throw new UnsupportedOperationException();
        }

        public ServiceTarget addListener(final ServiceListener.Inheritance inheritance, final ServiceListener<Object>... listeners) {
            throw new UnsupportedOperationException();
        }

        public ServiceTarget addListener(final ServiceListener.Inheritance inheritance, final Collection<ServiceListener<Object>> listeners) {
            throw new UnsupportedOperationException();
        }

        public ServiceTarget removeListener(final ServiceListener<Object> listener) {
            throw new UnsupportedOperationException();
        }

        public Set<ServiceListener<Object>> getListeners() {
            throw new UnsupportedOperationException();
        }

        public ServiceTarget addDependency(final ServiceName dependency) {
            throw new UnsupportedOperationException();
        }

        public ServiceTarget addDependency(final ServiceName... dependencies) {
            throw new UnsupportedOperationException();
        }

        public ServiceTarget addDependency(final Collection<ServiceName> dependencies) {
            throw new UnsupportedOperationException();
        }

        public ServiceTarget removeDependency(final ServiceName dependency) {
            throw new UnsupportedOperationException();
        }

        public Set<ServiceName> getDependencies() {
            throw new UnsupportedOperationException();
        }

        public ServiceTarget subTarget() {
            throw new UnsupportedOperationException();
        }

        public BatchServiceTarget batchTarget() {
            throw new UnsupportedOperationException();
        }
    }

    class ContextServiceBuilder<T> implements ServiceBuilder<T> {

        private final ServiceBuilder<T> realBuilder;
        private final ServiceName name;

        ContextServiceBuilder(final ServiceBuilder<T> realBuilder, final ServiceName name) {
            this.realBuilder = realBuilder;
            this.name = name;
        }

        public ServiceBuilder<T> addAliases(final ServiceName... aliases) {
            realBuilder.addAliases(aliases);
            return this;
        }

        public ServiceBuilder<T> setInitialMode(final ServiceController.Mode mode) {
            realBuilder.setInitialMode(mode);
            return this;
        }

        public ServiceBuilder<T> addDependencies(final ServiceName... dependencies) {
            realBuilder.addDependencies(dependencies);
            return this;
        }

        public ServiceBuilder<T> addDependencies(final DependencyType dependencyType, final ServiceName... dependencies) {
            realBuilder.addDependencies(dependencyType, dependencies);
            return this;
        }

        public ServiceBuilder<T> addDependencies(final Iterable<ServiceName> dependencies) {
            realBuilder.addDependencies(dependencies);
            return this;
        }

        public ServiceBuilder<T> addDependencies(final DependencyType dependencyType, final Iterable<ServiceName> dependencies) {
            realBuilder.addDependencies(dependencyType, dependencies);
            return this;
        }

        public ServiceBuilder<T> addDependency(final ServiceName dependency) {
            realBuilder.addDependency(dependency);
            return this;
        }

        public ServiceBuilder<T> addDependency(final DependencyType dependencyType, final ServiceName dependency) {
            realBuilder.addDependency(dependencyType, dependency);
            return this;
        }

        public ServiceBuilder<T> addDependency(final ServiceName dependency, final Injector<Object> target) {
            realBuilder.addDependency(dependency, target);
            return this;
        }

        public ServiceBuilder<T> addDependency(final DependencyType dependencyType, final ServiceName dependency, final Injector<Object> target) {
            realBuilder.addDependency(dependencyType, dependency, target);
            return this;
        }

        public <I> ServiceBuilder<T> addDependency(final ServiceName dependency, final Class<I> type, final Injector<I> target) {
            realBuilder.addDependency(dependency, type, target);
            return this;
        }

        public <I> ServiceBuilder<T> addDependency(final DependencyType dependencyType, final ServiceName dependency, final Class<I> type, final Injector<I> target) {
            realBuilder.addDependency(dependencyType, dependency, type, target);
            return this;
        }

        public <I> ServiceBuilder<T> addInjection(final Injector<? super I> target, final I value) {
            realBuilder.addInjection(target, value);
            return this;
        }

        public <I> ServiceBuilder<T> addInjectionValue(final Injector<? super I> target, final Value<I> value) {
            realBuilder.addInjectionValue(target, value);
            return this;
        }

        public ServiceBuilder<T> addInjection(final Injector<? super T> target) {
            realBuilder.addInjection(target);
            return this;
        }

        public ServiceBuilder<T> addListener(final ServiceListener<? super T> listener) {
            realBuilder.addListener(listener);
            return this;
        }

        public ServiceBuilder<T> addListener(final ServiceListener<? super T>... listeners) {
            realBuilder.addListener(listeners);
            return this;
        }

        public ServiceBuilder<T> addListener(final Collection<? extends ServiceListener<? super T>> listeners) {
            realBuilder.addListener(listeners);
            return this;
        }

        public ServiceBuilder<T> addListener(final ServiceListener.Inheritance inheritance, final ServiceListener<? super T> listener) {
            realBuilder.addListener(inheritance, listener);
            return this;
        }

        public ServiceBuilder<T> addListener(final ServiceListener.Inheritance inheritance, final ServiceListener<? super T>... listeners) {
            realBuilder.addListener(inheritance, listeners);
            return this;
        }

        public ServiceBuilder<T> addListener(final ServiceListener.Inheritance inheritance, final Collection<? extends ServiceListener<? super T>> listeners) {
            realBuilder.addListener(inheritance, listeners);
            return this;
        }

        public ServiceController<T> install() throws ServiceRegistryException, IllegalStateException {
            final Map<ServiceName, ServiceController<?>> map = realRemovingControllers;
            synchronized (map) {
                // Wait for removal to complete
                while (map.containsKey(name)) try {
                    map.wait();
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    throw new CancellationException("Service install was cancelled");
                }
                boolean intr = false;
                try {
                    while (map.containsKey(name)) try {
                        map.wait();
                        return realBuilder.install();
                    } catch (InterruptedException e) {
                        if (respectInterruption) {
                            Thread.currentThread().interrupt();
                            throw new CancellationException("Service install was cancelled");
                        } else {
                            intr = true;
                        }
                    }
                } finally {
                    if (intr) {
                        Thread.currentThread().interrupt();
                    }
                }
                return realBuilder.install();
            }
        }
    }
}
TOP

Related Classes of org.jboss.as.controller.OperationContextImpl$ContextServiceBuilder

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.
AnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');