All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xbean.kernel.standard.ServiceManager Maven / Gradle / Ivy

The newest version!
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.xbean.kernel.standard;

import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Collections;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.xbean.kernel.ForcedStopException;
import org.apache.xbean.kernel.IllegalServiceStateException;
import org.apache.xbean.kernel.Kernel;
import org.apache.xbean.kernel.KernelOperationInterruptedException;
import org.apache.xbean.kernel.KernelOperationTimoutException;
import org.apache.xbean.kernel.ServiceEvent;
import org.apache.xbean.kernel.ServiceFactory;
import org.apache.xbean.kernel.ServiceMonitor;
import org.apache.xbean.kernel.ServiceName;
import org.apache.xbean.kernel.ServiceNotFoundException;
import org.apache.xbean.kernel.ServiceState;
import org.apache.xbean.kernel.StartStrategies;
import org.apache.xbean.kernel.StartStrategy;
import org.apache.xbean.kernel.StopStrategy;
import org.apache.xbean.kernel.UnregisterServiceException;
import org.apache.xbean.kernel.UnsatisfiedConditionsException;
import org.apache.xbean.kernel.InvalidServiceTypeException;

/**
 * The ServiceManager handles the life cycle of a single service.   The manager is responsible for gaurenteeing that
 * all start conditions have been satisfied before the service is constructed, and that all stop conditions have been
 * satisfied before the service is destroyed.  The ServiceManager can be started and stopped several times, but once
 * destroyed no methods may be called.
 *
 * @author Dain Sundstrom
 * @version $Id$
 * @since 2.0
 */
public class ServiceManager implements Comparable {
    /**
     * The kernel in which this service is registered.
     */
    private final Kernel kernel;

    /**
     * The unique id of this service in the kernel.
     */
    private final long serviceId;

    /**
     * The unique name of this service in the kernel.
     */
    private final ServiceName serviceName;

    /**
     * The factory used to create and destroy the service instance.
     */
    private final ServiceFactory serviceFactory;

    /**
     * The type of service this service manager will create.  This value is cached from the serviceFactory.getT
     */
    private final Set serviceTypes;

    /**
     * The monitor to which we fire service events.  The ServiceManager requires an asynchronous monitor becuse events are
     * fired from within the lock.  This helps to reduce complexity but will cause more services to sit in the
     * {@link ServiceState#STARTING} and {@link ServiceState#STOPPING} states since events are propagated in a separate
     * thread.
     */
    private final ServiceMonitor serviceMonitor;

    /**
     * The service context given to the service factory.  This contans a reference to the kernel, serviceName and
     * classloader.
     */
    private final StandardServiceContext standardServiceContext;

    /**
     * Current state of this service.
     */
    private volatile ServiceState state = ServiceState.STOPPED;

    /**
     * The time the service was started or 0 if not started.
     */
    private volatile long startTime;

    /**
     * The {@link ServiceCondition) objects required to be ready before this service can be completely started.
     */
    private AggregateCondition startCondition;

    /**
     * The {@link ServiceCondition) objects required to be ready before this service can be completely stopped.
     */
    private AggregateCondition stopCondition;

    /**
     * The service instance.
     */
    private volatile Object service;

    /**
     * The single lock we use.
     */
    private final ReentrantLock lock = new ReentrantLock();

    /**
     * The maximum duration to wait for the lock.
     */
    private final long timeoutDuration;

    /**
     * The unit of measure for the {@link #timeoutDuration}.
     */
    private final TimeUnit timeoutUnits;

    /**
     * The name of the operation for which the lock is held; this is used in the reentrant exception message.
     */
    private String currentLockHolderOperation = "NOT-HELD";

    /**
     * Sequence number for service event objects.
     */
    private final AtomicLong eventId = new AtomicLong(0);

    /**
     * If true, when start is successful we will startRecusrive all of the services owned by this service.
     */
    private boolean recursive = false;

    /**
     * Creates a service manager for a single service.
     *
     * @param kernel the kernel in which this wraper will be registered
     * @param serviceId the unique id of this service in the kernel
     * @param serviceName the unique name of this service in the kernel
     * @param serviceFactory the factory used to create and destroy the service instance
     * @param serviceMonitor the monitor of service events
     * @param timeoutDuration the maximum duration to wait for a lock
     * @param timeoutUnits the unit of measure for the timeoutDuration
     */
    public ServiceManager(Kernel kernel,
            long serviceId,
            ServiceName serviceName,
            ServiceFactory serviceFactory,
            ServiceMonitor serviceMonitor,
            long timeoutDuration,
            TimeUnit timeoutUnits) {

        this.kernel = kernel;
        this.serviceId = serviceId;
        this.serviceName = serviceName;
        this.serviceFactory = serviceFactory;
        this.serviceMonitor = serviceMonitor;
        this.timeoutDuration = timeoutDuration;
        this.timeoutUnits = timeoutUnits;
        standardServiceContext = new StandardServiceContext(kernel, serviceName, serviceFactory.getClassLoader());
        serviceTypes = Collections.unmodifiableSet(new LinkedHashSet(Arrays.asList(serviceFactory.getTypes())));
    }

    /**
     * Initializes the service.
     *
     * @throws IllegalServiceStateException if the service is not restartable and is disabled
     * @throws UnsatisfiedConditionsException if the service is not restartable and there were unsatisfied start conditions
     * @throws Exception if the service is not restartable and service construction threw an exception
     * @see Kernel#registerService(ServiceName, ServiceFactory, ClassLoader)
     */
    public void initialize() throws IllegalServiceStateException, UnsatisfiedConditionsException, Exception {
        if (!serviceFactory.isRestartable() && !serviceFactory.isEnabled()) {
            throw new IllegalServiceStateException("A disabled non-restartable service factory can not be initalized", serviceName);
        }

        serviceMonitor.serviceRegistered(createServiceEvent());

        // if we are not restartable, we need to start immediately, otherwise we are not going to register this service
        if (!serviceFactory.isRestartable()) {
            try {
                start(false, StartStrategies.UNREGISTER);
            } catch (UnregisterServiceException e) {
                serviceMonitor.serviceUnregistered(createServiceEvent());
                Throwable cause = e.getCause();
                if (cause instanceof Exception) {
                    throw (Exception) cause;
                } else if (cause instanceof Error) {
                    throw (Error) cause;
                } else {
                    throw new AssertionError(cause);
                }
            }

            // a non restartable service uses a special stop conditions object that picks up stop conditions as they
            // are added.  When the stop() method is called on a non-restartable service all of the stop conditions
            // registered with the service factory are initialized (if not already initialized), and the isSatisfied
            // method is called.  This should cause the stop logic of a stop condition to fire.
            lock("initialize");
            try {
                stopCondition = new NonRestartableStopCondition(kernel, serviceName, serviceFactory.getClassLoader(), lock, serviceFactory);
            } finally {
                unlock();
            }
        }
    }

    /**
     * Attempts to stop and destroy the service.
     *
     * @param stopStrategy the strategy used to determine how to handle unsatisfied stop conditions
     * @throws IllegalServiceStateException is the service did not stop
     * @throws UnsatisfiedConditionsException if there were unsatisfied stop conditions
     * @see Kernel#unregisterService(ServiceName, StopStrategy)
     */
    public void destroy(StopStrategy stopStrategy) throws IllegalServiceStateException, UnsatisfiedConditionsException {
        // if we are not restartable, we need to stop
        try {
            if (!stop(stopStrategy)) {
                throw new IllegalServiceStateException("Service did not stop", serviceName);
            }
        } catch (UnsatisfiedConditionsException e) {
            throw e;
        }

        if (!serviceFactory.isRestartable()) {
            lock("destroy");
            try {
                if (state != ServiceState.STOPPED) {
                    state = ServiceState.STARTING;
                    serviceMonitor.serviceStopping(createServiceEvent());
                    if (service != null) {
                        try {
                            // destroy the service
                            serviceFactory.destroyService(standardServiceContext);
                        } catch (Throwable e) {
                            serviceMonitor.serviceStopError(createErrorServiceEvent(e));
                        }
                    }

                    destroyAllConditions(serviceMonitor);

                    service = null;
                    startTime = 0;
                    state = ServiceState.STOPPED;
                    serviceMonitor.serviceStopped(createServiceEvent());
                }
            } finally {
                unlock();
            }
        }

        // cool we can unregistered
        serviceMonitor.serviceUnregistered(createServiceEvent());
    }

    /**
     * Gets the unique id of this service in the kernel.
     *
     * @return the unique id of this service in the kernel
     */
    public long getServiceId() {
        return serviceId;
    }

    /**
     * Gets the unique name of this service in the kernel.
     *
     * @return the unique name of this servce in the kernel
     */
    public ServiceName getServiceName() {
        return serviceName;
    }

    /**
     * Gets the types of the service that will be managed by this service manager.
     * @return the types of the service
     */
    public Set getServiceTypes() {
        return serviceTypes;
    }

    /**
     * Gets the factory used to create and destroy the service instance.
     *
     * @return the factory for the service instance
     * @see Kernel#getServiceFactory(ServiceName)
     */
    public ServiceFactory getServiceFactory() {
        return serviceFactory;
    }

    /**
     * Gets the class loader for this service.  This class loader is provided to the service factory in the
     * ServiceContext object.
     *
     * @return the classloader for this service
     * @see Kernel#getClassLoaderFor(ServiceName)
     */
    public ClassLoader getClassLoader() {
        return serviceFactory.getClassLoader();
    }

    /**
     * Gets the service instance.
     *
     * @return the service instance
     * @see Kernel#getService(ServiceName)
     */
    public Object getService() {
        return service;
    }

    /**
     * Gets the current state of this service.
     *
     * @return the current state of this service
     * @see Kernel#getServiceState(ServiceName)
     */
    public ServiceState getState() {
        return state;
    }

    /**
     * Gets the time at which this service entered the STARTING state or 0 if the service is STOPPED.
     *
     * @return the start time or 0 if the service is stopped
     * @see Kernel#getServiceStartTime(ServiceName)
     */
    public long getStartTime() {
        return startTime;
    }

    /**
     * Attempts to starts the service.
     *
     * @param recursive if start is successful should we start recursive the services owned by this servic
     * @param startStrategy the strategy used to determine how to handle unsatisfied start conditions and start errors
     * @throws IllegalServiceStateException if the service is in a state in which it can not be started
     * @throws UnregisterServiceException if the kernel should unregister this service
     * @throws UnsatisfiedConditionsException if there were unsatisfied start conditions
     * @throws Exception it service creation threw an exception
     * @see Kernel#startService(ServiceName)
     * @see Kernel#startServiceRecursive(ServiceName)
     */
    public void start(boolean recursive, StartStrategy startStrategy) throws IllegalServiceStateException, UnregisterServiceException, UnsatisfiedConditionsException, Exception {
        // verify that it is possible to start this service in the current state before obtaining the lock
        if (!verifyStartable(state)) {
            if (recursive) {
                startOwnedServices(startStrategy);
            }
            return;
        }

        boolean shouldStartRecursive = false;
        lock("start");
        try {
            // update the recursive flag
            this.recursive = this.recursive || recursive;

            Throwable startError = null;
            try {
                //
                // Loop until all start conditions have been satified.  The start strategy can break this loop.
                //
                boolean satisfied = false;
                while (!satisfied) {
                    // do we still want to start?
                    if (!verifyStartable(state)) {
                        // assume someone else called startOwnedServices
                        return;
                    }

                    // if we are in the STOPPED state, we need to move to the STARTING state
                    if (state == ServiceState.STOPPED) {
                        // we are now officially starting
                        state = ServiceState.STARTING;
                        serviceMonitor.serviceStarting(createServiceEvent());

                        // initialize the start conditions
                        startCondition = new AggregateCondition(kernel, serviceName, serviceFactory.getClassLoader(), lock, serviceFactory.getStartConditions());
                        startCondition.initialize();
                    }

                    // are we satisfied?
                    Set unsatisfiedConditions = startCondition.getUnsatisfied();
                    satisfied = unsatisfiedConditions.isEmpty();
                    if (!satisfied) {
                        // if the stragegy wants us to wait for conditions to be satisfied, it will return true
                        if (startStrategy.waitForUnsatisfiedConditions(serviceName, unsatisfiedConditions)) {
                            // wait for satisfaction and loop
                            startCondition.awaitSatisfaction();
                        } else {
                            // no wait, notify the monitor and exit
                            serviceMonitor.serviceWaitingToStart(createWaitingServiceEvent(unsatisfiedConditions));
                            return;
                        }
                    }
                }

                // we are ready to create the service
                service = serviceFactory.createService(standardServiceContext);

                // verify that the service implements all of the types
                if (service == null) {
                    throw new NullPointerException("Service factory return null from createService for service " + serviceName);
                }
                for (Iterator iterator = serviceTypes.iterator(); iterator.hasNext();) {
                    Class type = (Class) iterator.next();
                    if (!type.isInstance(service)) {
                        throw new InvalidServiceTypeException(serviceName, type, service.getClass());
                    }
                }

                // success transition to running
                startTime = System.currentTimeMillis();
                state = ServiceState.RUNNING;
                serviceMonitor.serviceRunning(createServiceEvent());

                // should we recursively start our children
                shouldStartRecursive = this.recursive || recursive;
                this.recursive = false;
            } catch (UnsatisfiedConditionsException e) {
                // thrown from waitForUnsatisfiedConditions
                throw e;
            } catch (IllegalServiceStateException e) {
                // this can be thrown while awaiting satisfaction
                throw e;
            } catch (Exception e) {
                startError = e;
            } catch (Error e) {
                startError = e;
            }

            if (startError != null) {
                try {
                    if (startError instanceof UnregisterServiceException) {
                        throw (UnregisterServiceException) startError;
                    } else {
                        // the strategy will normally rethrow the startError, but if it doesn't notify the service monitor
                        startStrategy.startError(serviceName, startError);
                        serviceMonitor.serviceStartError(createErrorServiceEvent(startError));
                    }
                } finally {
                    // we are now STOPPING
                    state = ServiceState.STOPPING;
                    serviceMonitor.serviceStopping(createServiceEvent());

                    // clean up the conditons
                    destroyAllConditions(serviceMonitor);

                    // transition to the STOPPED state
                    service = null;
                    startTime = 0;
                    state = ServiceState.STOPPED;
                    serviceMonitor.serviceStopped(createServiceEvent());
                }
            }
        } finally {
            unlock();
        }


        // startRecursive all of the owned services
        if (shouldStartRecursive) {
            startOwnedServices(startStrategy);
        }
    }

    private void startOwnedServices(StartStrategy startStrategy) throws IllegalServiceStateException, UnsatisfiedConditionsException, Exception {
        Set ownedServices = serviceFactory.getOwnedServices();
        if (ownedServices == null) throw new NullPointerException("serviceFactory.getOwnedServices() returned null");
        for (Iterator iterator = ownedServices.iterator(); iterator.hasNext();) {
            ServiceName ownedService = (ServiceName) iterator.next();
            try {
                kernel.startServiceRecursive(ownedService, startStrategy);
            } catch (ServiceNotFoundException ignored) {
                // this is ok -- service unregistered
            } catch (IllegalServiceStateException ignored) {
                // ownedService is disabled or stopping -- anyway we don't care
            }
        }
    }

    /**
     * Verifies that the service is startable.  This can be used out side a lock to avoid unecessary locking.
     *
     * @param state the state of the service
     * @return true if it is possible to start a service in the specifiec state
     * @throws IllegalServiceStateException if it is illegal to start a service in the specified state
     */
    private boolean verifyStartable(ServiceState state) throws IllegalServiceStateException {
        // if we are alredy in the running state, there is nothing to do
        if (state == ServiceState.RUNNING) {
            return false;
        }

        // if we are in the stopping states, that is an error
        if (state == ServiceState.STOPPING) {
            throw new IllegalServiceStateException("A stopping service can not be started", serviceName);
        }

        // is this service enabled?
        if (state == ServiceState.STOPPED && !serviceFactory.isEnabled()) {
            throw new IllegalServiceStateException("Service is disabled", serviceName);
        }

        return true;
    }

    /**
     * Attempts to stop the service.
     *
     * @param stopStrategy the strategy used to determine how to handle unsatisfied stop conditions
     * @return true if the service was sucessfully stopped; false otherwise
     * @throws UnsatisfiedConditionsException if there were unsatisfied stop conditions
     * @see Kernel#stopService(ServiceName)
     */
    public boolean stop(StopStrategy stopStrategy) throws UnsatisfiedConditionsException {
        // check that we aren't already stopped before attempting to acquire the lock
        ServiceState initialState = state;
        if (initialState == ServiceState.STOPPED) {
            return true;
        }

        lock("stop");
        try {
            try {
                //
                // Loop until all stop conditions have been satified.  The stop strategy can break this loop.
                //
                boolean satisfied = false;
                while (!satisfied) {
                    // do we still want to stop?
                    if (state == ServiceState.STOPPED) {
                        return true;
                    }

                    // if we are not the STOPPING state, transition to it
                    // we check on the stopConditions variable because non-restartable services preset this in the
                    // intialization method
                    if (stopCondition == null) {
                        // we are not officially stopping
                        serviceMonitor.serviceStopping(createServiceEvent());
                        state = ServiceState.STOPPING;

                        // initialize all of the stop conditions
                        stopCondition = new AggregateCondition(kernel, serviceName, serviceFactory.getClassLoader(), lock, serviceFactory.getStopConditions());
                        stopCondition.initialize();
                    }

                    // are we satisfied?
                    Set unsatisfiedConditions = stopCondition.getUnsatisfied();
                    satisfied = unsatisfiedConditions.isEmpty();
                    if (!satisfied) {
                        // if the stragegy wants us to wait for conditions to be satisfied, it will return true
                        if (stopStrategy.waitForUnsatisfiedConditions(serviceName, unsatisfiedConditions)) {
                            // wait for satisfaction and loop
                            stopCondition.awaitSatisfaction();
                        } else {
                            // no wait, notify the monitor and exit
                            serviceMonitor.serviceWaitingToStop(createWaitingServiceEvent(unsatisfiedConditions));
                            return false;
                        }
                    }
                }
            } catch (UnsatisfiedConditionsException e) {
                throw e;
            } catch (ForcedStopException e) {
                serviceMonitor.serviceStopError(createErrorServiceEvent(e));
            } catch (Exception e) {
                serviceMonitor.serviceStopError(createErrorServiceEvent(e));
            } catch (Error e) {
                serviceMonitor.serviceStopError(createErrorServiceEvent(e));
            }

            if (serviceFactory.isRestartable()) {
                if (service != null) {
                    try {
                        // destroy the service
                        serviceFactory.destroyService(standardServiceContext);
                    } catch (Throwable e) {
                        serviceMonitor.serviceStopError(createErrorServiceEvent(e));
                    }
                }

                destroyAllConditions(serviceMonitor);

                service = null;
                startTime = 0;
                state = ServiceState.STOPPED;
                serviceMonitor.serviceStopped(createServiceEvent());
            }
            return true;
        } finally {
            unlock();
        }
    }

    private void destroyAllConditions(ServiceMonitor monitor) {
        if (!lock.isHeldByCurrentThread()) {
            throw new IllegalStateException("Current thread must hold lock before calling destroyAllConditions");
        }

        if (startCondition != null) {
            List errors = startCondition.destroy();
            // errors from destroying the start conditions are stop errors because destroy is only called while
            // stopping the service
            for (Iterator iterator = errors.iterator(); iterator.hasNext();) {
                Throwable stopError = (Throwable) iterator.next();
                monitor.serviceStopError(createErrorServiceEvent(stopError));
            }
            startCondition = null;
        }
        if (stopCondition != null) {
            List errors = stopCondition.destroy();
            for (Iterator iterator = errors.iterator(); iterator.hasNext();) {
                Throwable stopError = (Throwable) iterator.next();
                monitor.serviceStopError(createErrorServiceEvent(stopError));
            }
            stopCondition = null;
        }
    }

    /**
     * Obtain the lock for the specified operation.
     *
     * @param operationName name of the operation that lock will be used for - this is only used for exception messages
     * @throws IllegalStateException if thread tries to reenter while holding the lock
     * @throws KernelOperationTimoutException if lock could not be obtained in {@link #timeoutDuration} {@link #timeoutUnits}
     * @throws KernelOperationInterruptedException if the thread was interrupted while waiting for the lock
     */
    private void lock(String operationName) throws IllegalStateException, KernelOperationTimoutException, KernelOperationInterruptedException {
        if (lock.isHeldByCurrentThread()) {
            throw new IllegalStateException("Current thread holds lock for " + currentLockHolderOperation +
                    " and lock can not be reacquired for " + operationName + " on " + serviceName);
        }

        try {
            if (!lock.tryLock(timeoutDuration, timeoutUnits)) {
                throw new KernelOperationTimoutException("Could not obtain lock for " + operationName + " operation on " +
                        serviceName + " within " + timeoutDuration + " " + timeoutUnits.toString().toLowerCase(),
                        serviceName,
                        operationName);
            }
            currentLockHolderOperation = operationName;
        } catch (InterruptedException e) {
            throw new KernelOperationInterruptedException("Interrupted while attempting to obtain lock for " + operationName +
                    " operation on " + serviceName,
                    e,
                    serviceName,
                    operationName);

        }
    }

    /**
     * Unlock the lock and clear the currentLockHolderOperation name.
     */
    private void unlock() {
        if (!lock.isHeldByCurrentThread()) {
            throw new IllegalMonitorStateException("Not owner");
        }

        currentLockHolderOperation = "NOT-HELD";
        lock.unlock();
    }

    private ServiceEvent createServiceEvent() {
        return new ServiceEvent(eventId.getAndIncrement(), kernel, serviceName, serviceFactory, serviceFactory.getClassLoader(), service, null, null);
    }

    private ServiceEvent createWaitingServiceEvent(Set unsatisfiedConditions) {
        return new ServiceEvent(eventId.getAndIncrement(), kernel, serviceName, serviceFactory, serviceFactory.getClassLoader(), service, null, unsatisfiedConditions);
    }

    private ServiceEvent createErrorServiceEvent(Throwable cause) {
        return new ServiceEvent(eventId.getAndIncrement(), kernel, serviceName, serviceFactory, serviceFactory.getClassLoader(), null, cause, null);
    }

    public int hashCode() {
        return (int) (serviceId ^ (serviceId >>> 32));
    }

    public boolean equals(Object o) {
        if (o instanceof ServiceManager) {
            return serviceId == ((ServiceManager)o).serviceId;
        }
        return false;
    }

    public int compareTo(Object o) {
        ServiceManager serviceManager = (ServiceManager) o;

        if (serviceId < serviceManager.serviceId) {
            return -1;
        } else if (serviceId > serviceManager.serviceId) {
            return 1;
        } else {
            return 0;
        }
    }

    public String toString() {
        return "[ServiceManager: serviceId=" + serviceId + ", serviceName=" + serviceName + ", state=" + state + "]";
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy