Package org.jboss.as.controller

Source Code of org.jboss.as.controller.ServiceVerificationHandler

/*
* 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 org.jboss.dmr.ModelNode;
import org.jboss.msc.service.AbstractServiceListener;
import org.jboss.msc.service.ServiceContainer;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceListener;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartException;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import static org.jboss.as.controller.ControllerMessages.MESSAGES;

/**
* Tracks the status of a service installed by an {@link OperationStepHandler}, recording a failure desription
* if the service has a problme starting.
*
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
*/
public final class ServiceVerificationHandler extends AbstractServiceListener<Object> implements ServiceListener<Object>, OperationStepHandler {
    private final Set<ServiceController<?>> set = new HashSet<ServiceController<?>>();
    private final Set<ServiceController<?>> failed = new HashSet<ServiceController<?>>();
    private final Set<ServiceController<?>> problem = new HashSet<ServiceController<?>>();
    private int outstanding;

    public synchronized void execute(final OperationContext context, final ModelNode operation) {

        // Wait for services to reach rest state.
        // Additionally...
        // Temp workaround to MSC issue of geting STARTING_TO_STARTED notification for parent service before
        // getting the PROBLEM_TO_START_REQUESTED notification for dependent services. If there are
        // services in PROBLEM state, wait up to 100ms to give them a chance to transition to START_REQUESTED.

        long start = 0;
        long settleTime = 100;
        while (outstanding > 0 || (settleTime > 0 && !problem.isEmpty())) {
            try {
                long wait = outstanding > 0 ? 0 : settleTime;
                wait(wait);
                if (outstanding == 0) {
                    if (start == 0) {
                        start = System.currentTimeMillis();
                    } else {
                        settleTime -= System.currentTimeMillis() - start;
                    }
                } else {
                    start = 0;
                    settleTime = 100;
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                context.getFailureDescription().set(MESSAGES.operationCancelled());
                context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
                return;
            }
        }

        if (!failed.isEmpty() || !problem.isEmpty()) {
            Set<ServiceController<?>> missingTransitive = null;
            Set<ServiceName> trackedServices = new HashSet<ServiceName>();
            final ModelNode failureDescription = context.getFailureDescription();
            ModelNode failedList = null;
            for (ServiceController<?> controller : failed) {
                if (failedList == null) {
                    failedList = failureDescription.get(MESSAGES.failedServices());
                }
                ServiceName serviceName = controller.getName();
                trackedServices.add(serviceName);
                failedList.get(serviceName.getCanonicalName()).set(getServiceFailureDescription(controller.getStartException()));
            }
            ModelNode problemList = null;
            for (ServiceController<?> controller : problem) {

                Set<ServiceName> immediatelyUnavailable = controller.getImmediateUnavailableDependencies();
                if (!immediatelyUnavailable.isEmpty()) {
                    if (problemList == null) {
                        problemList = failureDescription.get(MESSAGES.servicesMissingDependencies());
                    }

                    StringBuilder missing = new StringBuilder();
                    for(Iterator<ServiceName> i = immediatelyUnavailable.iterator(); i.hasNext(); ) {
                        ServiceName missingSvc = i.next();
                        trackedServices.add(missingSvc);
                        missing.append(missingSvc.getCanonicalName());
                        if(i.hasNext()) {
                            missing.append(", ");
                        }
                    }

                    final StringBuilder problem = new StringBuilder();
                    problem.append(controller.getName().getCanonicalName());
                    problem.append(" ").append(MESSAGES.servicesMissing(missing));
                    problemList.add(problem.toString());

                } else {
                    if (missingTransitive == null) {
                        missingTransitive = new HashSet<ServiceController<?>>();
                    }
                    missingTransitive.add(controller);
                }
            }

            if (missingTransitive != null) {
                // See if any other services are known to the service container as being missing and aren't already
                // tracked by this SVH as failed or directly missing. If any are found, that is some additional
                // info to the user, so report that
                SortedSet<ServiceName> allMissing = findAllMissingServices(missingTransitive, trackedServices);
                if (!allMissing.isEmpty()) {
                    ModelNode missingTransitiveDesc = failureDescription.get(MESSAGES.missingTransitiveDependencyProblem());
                    ModelNode missingTransitiveDeps = missingTransitiveDesc.get(MESSAGES.missingTransitiveDependendents());
                    Set<ServiceName> sortedNames = new TreeSet<ServiceName>();
                    for (ServiceController<?> serviceController : missingTransitive) {
                        sortedNames.add(serviceController.getName());
                    }
                    for (ServiceName serviceName : sortedNames) {
                        missingTransitiveDeps.add(serviceName.getCanonicalName());
                    }
                    ModelNode allMissingList = missingTransitiveDesc.get(MESSAGES.missingTransitiveDependencies());
                    for (ServiceName serviceName : allMissing) {
                        allMissingList.add(serviceName.getCanonicalName());
                    }
                }
            }

            if (context.isRollbackOnRuntimeFailure()) {
                context.setRollbackOnly();
            }
        }
        for (ServiceController<?> controller : set) {
            controller.removeListener(this);
        }
        context.stepCompleted();
    }

    public synchronized void listenerAdded(final ServiceController<?> controller) {
        set.add(controller);
        if (!controller.getSubstate().isRestState()) {
            outstanding++;
        }
    }

    public synchronized void transition(final ServiceController<?> controller, final ServiceController.Transition transition) {
        switch (transition) {
            case STARTING_to_START_FAILED: {
                failed.add(controller);
                break;
            }
            case START_FAILED_to_STARTING: {
                failed.remove(controller);
                break;
            }
            case START_REQUESTED_to_PROBLEM: {
                problem.add(controller);
                break;
            }
            case PROBLEM_to_START_REQUESTED: {
                problem.remove(controller);
                break;
            }
        }
        if (transition.leavesRestState()) {
            outstanding++;
        } else if (transition.entersRestState()) {
            if (outstanding-- == 1) {
                notifyAll();
            }
        }
    }

    private static ModelNode getServiceFailureDescription(final StartException exception) {
        final ModelNode result = new ModelNode();
        if (exception != null) {
            StringBuilder sb = new StringBuilder(exception.toString());
            Throwable cause = exception.getCause();
            while (cause != null) {
                sb.append("\n    Caused by: ");
                sb.append(cause.toString());
                cause = cause.getCause();
            }
            result.set(sb.toString());
        }
        return result;
    }

    private static SortedSet<ServiceName> findAllMissingServices(Set<ServiceController<?>> missingTransitive, Set<ServiceName> alreadyTracked) {
        // Check all relevant service containers. This is a bit silly since in reality there
        // should only be one that is associated with every SC that is passed in,
        // but I'm being anal and vaguely future-proofing a bit
        Set<ServiceContainer> examined = new HashSet<ServiceContainer>();
        SortedSet<ServiceName> result = new TreeSet<ServiceName>();
        for (ServiceController<?> controller : missingTransitive) {
            ServiceContainer container = controller.getServiceContainer();
            if (examined.add(container)) {
                result.addAll(findAllMissingServices(container));
            }
        }

        // Only report the missing services if the list includes ones not already tracked
        // as failed or directly missing
        Set<ServiceName> retain = new HashSet<ServiceName>(result);
        retain.removeAll(alreadyTracked);
        if (retain.size() == 0) {
            result.clear();
        }

        return result;
    }

    private static Set<ServiceName> findAllMissingServices(ServiceContainer container) {
        Set<ServiceName> result = new HashSet<ServiceName>();
        for (ServiceName serviceName : container.getServiceNames()) {
            ServiceController<?> controller = container.getService(serviceName);
            if (controller != null && controller.getMode() != ServiceController.Mode.NEVER && controller.getMode() != ServiceController.Mode.REMOVE
                    && controller.getSubstate() == ServiceController.Substate.PROBLEM) {
                result.addAll(controller.getImmediateUnavailableDependencies());
            }
        }
        return  result;
    }
}
TOP

Related Classes of org.jboss.as.controller.ServiceVerificationHandler

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.