blob: 8fdfde29c971141c33625186215caa2104ead024 [file] [log] [blame]
/**
*
* Copyright 2005 the original author or authors.
*
* Licensed 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.gbean.kernel.standard;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import edu.emory.mathcs.backport.java.util.concurrent.ExecutionException;
import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicLong;
import org.gbean.kernel.IllegalServiceStateException;
import org.gbean.kernel.KernelErrorsError;
import org.gbean.kernel.KernelOperationInterruptedException;
import org.gbean.kernel.ServiceAlreadyExistsException;
import org.gbean.kernel.ServiceFactory;
import org.gbean.kernel.ServiceName;
import org.gbean.kernel.ServiceNotFoundException;
import org.gbean.kernel.ServiceRegistrationException;
import org.gbean.kernel.StopStrategies;
import org.gbean.kernel.StopStrategy;
import org.gbean.kernel.UnsatisfiedConditionsException;
/**
* The StandardServiceRegistry manages the registration of ServiceManagers for the kernel.
*
* @author Dain Sundstrom
* @version $Id$
* @since 1.0
*/
public class ServiceManagerRegistry {
/**
* The sequence used for the serviceId assigned to service managers.
*/
private final AtomicLong serviceId = new AtomicLong(1);
/**
* The factory used to create service managers.
*/
private final ServiceManagerFactory serviceManagerFactory;
/**
* The registered service managers.
*/
private final Map serviceManagers = new HashMap();
/**
* The service managers indexed by the service type. This map is populated when a service enters the running state.
*/
private final Map serviceManagersByType = new HashMap();
/**
* Creates a ServiceManagerRegistry that uses the specified service manager factory to create new service managers.
*
* @param serviceManagerFactory the factory for new service managers
*/
public ServiceManagerRegistry(ServiceManagerFactory serviceManagerFactory) {
this.serviceManagerFactory = serviceManagerFactory;
}
/**
* Stops and destroys all services service managers. This method will FORCE stop the services if necessary.
*
* @throws KernelErrorsError if any errors occur while stopping or destroying the service managers
*/
public void destroy() throws KernelErrorsError {
// we gather all errors that occur during shutdown and throw them as on huge exception
List errors = new ArrayList();
List managerFutures;
synchronized (serviceManagers) {
managerFutures = new ArrayList(serviceManagers.values());
serviceManagers.clear();
}
List managers = new ArrayList(managerFutures.size());
for (Iterator iterator = managerFutures.iterator(); iterator.hasNext();) {
RegistryFutureTask registryFutureTask = (RegistryFutureTask) iterator.next();
try {
managers.add(registryFutureTask.get());
} catch (InterruptedException e) {
// ignore -- this should not happen
errors.add(new AssertionError(e));
} catch (ExecutionException e) {
// good -- one less manager to deal with
}
}
// Be nice and try to stop asynchronously
errors.addAll(stopAll(managers, StopStrategies.ASYNCHRONOUS));
// Be really nice and try to stop asynchronously again
errors.addAll(stopAll(managers, StopStrategies.ASYNCHRONOUS));
// We have been nice enough now nuke them
errors.addAll(stopAll(managers, StopStrategies.FORCE));
// All managers are gaurenteed to be destroyed now
for (Iterator iterator = managers.iterator(); iterator.hasNext();) {
ServiceManager serviceManager = (ServiceManager) iterator.next();
try {
serviceManager.destroy(StopStrategies.FORCE);
} catch (UnsatisfiedConditionsException e) {
// this should not happen, because we force stopped
errors.add(new AssertionError(e));
} catch (IllegalServiceStateException e) {
// this should not happen, because we force stopped
errors.add(new AssertionError(e));
} catch (RuntimeException e) {
errors.add(new AssertionError(e));
} catch (Error e) {
errors.add(new AssertionError(e));
}
}
if (!errors.isEmpty()) {
throw new KernelErrorsError(errors);
}
}
private List stopAll(List managers, StopStrategy stopStrategy) {
List errors = new ArrayList();
for (Iterator iterator = managers.iterator(); iterator.hasNext();) {
ServiceManager serviceManager = (ServiceManager) iterator.next();
try {
serviceManager.stop(stopStrategy);
} catch (UnsatisfiedConditionsException e) {
// this should not happen in with an asynchronous strategy
errors.add(new AssertionError(e));
} catch (RuntimeException e) {
errors.add(new AssertionError(e));
} catch (Error e) {
errors.add(new AssertionError(e));
}
}
return errors;
}
/**
* Determines if there is a service registered under the specified name.
*
* @param serviceName the unique name of the service
* @return true if there is a service registered with the specified name; false otherwise
*/
public boolean isRegistered(ServiceName serviceName) {
if (serviceName == null) throw new NullPointerException("serviceName is null");
RegistryFutureTask registryFutureTask;
synchronized (serviceManagers) {
registryFutureTask = (RegistryFutureTask) serviceManagers.get(serviceName);
}
try {
// the service is registered if we have a non-null future value
return registryFutureTask != null && registryFutureTask.get() != null;
} catch (InterruptedException e) {
throw new KernelOperationInterruptedException(e, serviceName, "isRegistered");
} catch (ExecutionException e) {
return false;
}
}
/**
* Gets the service manager registered under the specified name.
*
* @param serviceName the unique name of the service
* @return the ServiceManager
* @throws ServiceNotFoundException if there is no service registered under the specified name
*/
public ServiceManager getServiceManager(ServiceName serviceName) throws ServiceNotFoundException {
if (serviceName == null) throw new NullPointerException("serviceName is null");
RegistryFutureTask registryFutureTask;
synchronized (serviceManagers) {
registryFutureTask = (RegistryFutureTask) serviceManagers.get(serviceName);
}
// this service has no future
if (registryFutureTask == null) {
throw new ServiceNotFoundException(serviceName);
}
try {
ServiceManager serviceManager = (ServiceManager) registryFutureTask.get();
if (serviceManager == null) {
throw new ServiceNotFoundException(serviceName);
}
return serviceManager;
} catch (InterruptedException e) {
throw new KernelOperationInterruptedException(e, serviceName, "getServiceManager");
} catch (ExecutionException e) {
// registration threw an exception which means it didn't register
throw new ServiceNotFoundException(serviceName);
}
}
/**
* Gets the first registered service manager that creates an instance of the specified type, or null if no service
* managers create an instance of the specified type.
*
* @param type the of the desired service
* @return the first registered service manager that creates an instance of the specified type, or null if none found
*/
public ServiceManager getServiceManager(Class type) {
SortedSet serviceManagerFutures = getServiceManagerFutures(type);
for (Iterator iterator = serviceManagerFutures.iterator(); iterator.hasNext();) {
RegistryFutureTask registryFutureTask = (RegistryFutureTask) iterator.next();
try {
ServiceManager serviceManager = (ServiceManager) registryFutureTask.get();
if (serviceManager != null) {
return serviceManager;
}
} catch (InterruptedException e) {
throw new KernelOperationInterruptedException(e, registryFutureTask.getServiceName(), "getServiceManagers(java.lang.Class)");
} catch (ExecutionException ignored) {
// registration threw an exception which means it didn't register
}
}
return null;
}
/**
* Gets all service managers that create an instances of the specified type, or an empty list if no service
* managers create an instance of the specified type.
*
* @param type the of the desired service managers
* @return all service managers that create an instances of the specified type, or an empty list if none found
*/
public List getServiceManagers(Class type) {
SortedSet serviceManagerFutures = getServiceManagerFutures(type);
List serviceManagers = new ArrayList(serviceManagerFutures.size());
for (Iterator iterator = serviceManagerFutures.iterator(); iterator.hasNext();) {
RegistryFutureTask registryFutureTask = (RegistryFutureTask) iterator.next();
try {
ServiceManager serviceManager = (ServiceManager) registryFutureTask.get();
if (serviceManager != null) {
serviceManagers.add(serviceManager);
}
} catch (InterruptedException e) {
throw new KernelOperationInterruptedException(e, registryFutureTask.getServiceName(), "getServiceManagers(java.lang.Class)");
} catch (ExecutionException ignored) {
// registration threw an exception which means it didn't register
}
}
return serviceManagers;
}
/**
* Gets the first registed and running service that is an instance of the specified type, or null if no instances
* of the specified type are running.
*
* @param type the of the desired service
* @return the first registed and running service that is an instance of the specified type or null if none found
*/
public synchronized Object getService(Class type) {
SortedSet serviceManagerFutures = getServiceManagerFutures(type);
for (Iterator iterator = serviceManagerFutures.iterator(); iterator.hasNext();) {
RegistryFutureTask registryFutureTask = (RegistryFutureTask) iterator.next();
try {
ServiceManager serviceManager = (ServiceManager) registryFutureTask.get();
if (serviceManager != null) {
Object service = serviceManager.getService();
if (service != null) {
return service;
}
}
} catch (InterruptedException e) {
throw new KernelOperationInterruptedException(e, registryFutureTask.getServiceName(), "getService(java.lang.Class)");
} catch (ExecutionException ignored) {
// registration threw an exception which means it didn't register
}
}
return null;
}
/**
* Gets the all of running service that are an instances of the specified type, or an empty list if no instances
* of the specified type are running.
*
* @param type the of the desired service
* @return the all of running service that are an instances of the specified type, or an empty list if none found
*/
public synchronized List getServices(Class type) {
List serviceManagers = getServiceManagers(type);
List services = new ArrayList(serviceManagers.size());
for (Iterator iterator = serviceManagers.iterator(); iterator.hasNext();) {
ServiceManager serviceManager = (ServiceManager) iterator.next();
if (serviceManager != null) {
Object service = serviceManager.getService();
if (service != null) {
services.add(service);
}
}
}
return services;
}
private SortedSet getServiceManagerFutures(Class type) {
SortedSet serviceManagerFutures;
synchronized (serviceManagers) {
serviceManagerFutures = (SortedSet) serviceManagersByType.get(type);
if (serviceManagerFutures != null) {
serviceManagerFutures = new TreeSet(serviceManagerFutures);
} else {
serviceManagerFutures = new TreeSet();
}
}
return serviceManagerFutures;
}
/**
* Creates a ServiceManager and registers it under the specified name. If the service is restartable, it will
* enter the server in the STOPPED state. If a service is not restartable, the service manager will assure that all
* dependencies are satisfied and service will immediately enter in the RUNNING state. If a
* dependency for a non-restartable service is not immediately satisfiable, this method will throw a
* ServiceRegistrationException.
*
* @param serviceName the unique name of the service
* @param serviceFactory the factory used to create the service
* @param classLoader the class loader to use for this service
* @throws ServiceAlreadyExistsException if service is already registered with the specified name
* @throws ServiceRegistrationException if the service is not restartable and an error occured while starting the service
*/
public void registerService(ServiceName serviceName, ServiceFactory serviceFactory, ClassLoader classLoader) throws ServiceAlreadyExistsException, ServiceRegistrationException {
if (serviceName == null) throw new NullPointerException("serviceName is null");
if (serviceFactory == null) throw new NullPointerException("serviceFactory is null");
if (classLoader == null) throw new NullPointerException("classLoader is null");
if (!serviceFactory.isEnabled()) {
throw new ServiceRegistrationException(serviceName,
new IllegalServiceStateException("A disabled non-restartable service factory can not be registered", serviceName));
}
RegistryFutureTask registrationTask = null;
//
// This loop will continue until we put our registrationTask in the serviceManagers map. If at any point,
// we discover that there is already a service registered under the specified service name, we will throw
// a ServiceAlreadyExistsException exiting this method.
//
while (registrationTask == null) {
RegistryFutureTask existingRegistration;
synchronized (serviceManagers) {
existingRegistration = (RegistryFutureTask) serviceManagers.get(serviceName);
// if we do not have an existing registration or the existing registration task is complete
// we can create the new registration task; otherwise we need to wait for the existing registration to
// finish out side of the synchronized lock on serviceManagers.
if (existingRegistration == null || existingRegistration.isDone()) {
// if we have a valid existing registration, throw a ServiceAlreadyExistsException
if (existingRegistration != null) {
try {
boolean alreadyRegistered = (existingRegistration.get() != null);
if (alreadyRegistered) {
throw new ServiceAlreadyExistsException(serviceName);
}
} catch (InterruptedException e) {
throw new KernelOperationInterruptedException(e, serviceName, "registerService");
} catch (ExecutionException e) {
// the previous registration threw an exception.. we can continure as normal
}
}
// we are ready to register our serviceManager
existingRegistration = null;
ServiceManager serviceManager = serviceManagerFactory.createServiceManager(serviceId.getAndIncrement(),
serviceName,
serviceFactory,
classLoader);
registrationTask = RegistryFutureTask.createRegisterTask(serviceManager);
serviceManagers.put(serviceName, registrationTask);
addTypeIndex(serviceManager, registrationTask);
}
}
// If there is an unfinished exiting registration task, wait until it is done executing
if (existingRegistration != null) {
try {
existingRegistration.get();
// we don't throw an error here because we want to check in the synchronized block that this
// future is still registered in the serviceManagers map
} catch (InterruptedException e) {
throw new KernelOperationInterruptedException(e, serviceName, "registerService");
} catch (ExecutionException e) {
// good
}
}
}
// run our registration task and check the results
registrationTask.run();
try {
// if initialization completed successfully, this method will not throw an exception
registrationTask.get();
} catch (InterruptedException e) {
throw new KernelOperationInterruptedException(e, serviceName, "registerService");
} catch (ExecutionException e) {
// registration failed, remove our task
synchronized (serviceManagers) {
// make sure our task is still the registered one
if (serviceManagers.get(serviceName) == registrationTask) {
serviceManagers.remove(serviceName);
removeTypeIndex(registrationTask);
}
}
throw new ServiceRegistrationException(serviceName, e.getCause());
}
}
/**
* Stops and destorys the ServiceManager and then unregisters it. The ServiceManagerRegistry will attempt to stop
* the service using the specified stop strategy, but if the service can not be stopped a
* ServiceRegistrationException will be thrown containing either an UnsatisfiedConditionsException or an
* IllegalServiceStateException.
*
* @param serviceName the unique name of the service
* @param stopStrategy the strategy that determines how unsatisfied conditions are handled
* @throws ServiceNotFoundException if there is no service registered under the specified name
* @throws ServiceRegistrationException if the service could not be stopped
*/
public void unregisterService(ServiceName serviceName, StopStrategy stopStrategy) throws ServiceNotFoundException, ServiceRegistrationException {
if (serviceName == null) throw new NullPointerException("serviceName is null");
if (stopStrategy == null) throw new NullPointerException("stopStrategy is null");
RegistryFutureTask unregistrationTask = null;
//
// This loop will continue until we put our unregistrationTask in the serviceManagers map. If at any point,
// we discover that there actually is not a service registered under the specified service name, we will throw
// a ServiceNotFoundException exiting this method.
//
while (unregistrationTask == null) {
RegistryFutureTask existingRegistration;
synchronized (serviceManagers) {
existingRegistration = (RegistryFutureTask) serviceManagers.get(serviceName);
if (existingRegistration == null) {
throw new ServiceNotFoundException(serviceName);
}
// if existing registration is done running, we can destroy it
if (existingRegistration.isDone()) {
ServiceManager serviceManager = null;
try {
serviceManager = (ServiceManager) existingRegistration.get();
} catch (InterruptedException e) {
throw new KernelOperationInterruptedException(e, serviceName, "unregisterService");
} catch (ExecutionException e) {
// good
}
// if there isn't a registered manager that is an exception
if (serviceManager == null) {
throw new ServiceNotFoundException(serviceName);
}
// we are ready to register our serviceManager
existingRegistration = null;
unregistrationTask = RegistryFutureTask.createUnregisterTask(serviceManager, stopStrategy);
serviceManagers.put(serviceName, unregistrationTask);
addTypeIndex(serviceManager, unregistrationTask);
}
}
// If there is an unfinished exiting registration task, wait until it is done executing
if (existingRegistration != null) {
try {
existingRegistration.get();
// we don't throw an error here because we want to check in the synchronized block that this
// future is still registered in the serviceManagers map
} catch (InterruptedException e) {
throw new KernelOperationInterruptedException(e, serviceName, "unregisterService");
} catch (ExecutionException e) {
// good
}
}
}
unregistrationTask.run();
try {
// if get returns any value other then null, the unregistration failed
if (unregistrationTask.get() == null) {
// unregistration was successful, remove the furuture object
synchronized (serviceManagers) {
// make sure our task is still the registered one
if (serviceManagers.get(serviceName) == unregistrationTask) {
serviceManagers.remove(serviceName);
removeTypeIndex(unregistrationTask);
}
}
} else {
synchronized (unregistrationTask) {
// the root exception is contained in the exception handle
throw new ServiceRegistrationException(serviceName, unregistrationTask.getThrowable());
}
}
} catch (InterruptedException e) {
throw new KernelOperationInterruptedException(e, serviceName, "unregisterService");
} catch (ExecutionException e) {
// this won't happen
throw new AssertionError(e);
}
}
private void addTypeIndex(ServiceManager serviceManager, RegistryFutureTask registryFutureTask) {
if (serviceManager == null) throw new NullPointerException("serviceManager is null");
if (registryFutureTask == null) throw new NullPointerException("serviceManagerFuture is null");
Set allTypes = new LinkedHashSet();
for (Iterator iterator = serviceManager.getServiceTypes().iterator(); iterator.hasNext();) {
Class serviceType = (Class) iterator.next();
if (serviceType.isArray()) {
throw new IllegalArgumentException("Service is an array: serviceName=" + serviceManager.getServiceName() +
", serviceType=" + serviceManager.getServiceTypes());
}
allTypes.add(serviceType);
allTypes.addAll(getAllSuperClasses(serviceType));
allTypes.addAll(getAllInterfaces(serviceType));
}
synchronized (serviceManagers) {
for (Iterator iterator = allTypes.iterator(); iterator.hasNext();) {
Class type = (Class) iterator.next();
Set futureServiceManagers = (Set) serviceManagersByType.get(type);
if (futureServiceManagers == null) {
futureServiceManagers = new TreeSet();
serviceManagersByType.put(type, futureServiceManagers);
}
futureServiceManagers.add(registryFutureTask);
}
}
}
private void removeTypeIndex(RegistryFutureTask registryFutureTask) {
if (registryFutureTask == null) throw new NullPointerException("serviceManagerFuture is null");
synchronized (serviceManagers) {
for (Iterator iterator = serviceManagersByType.entrySet().iterator(); iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
Set serviceManagers = (Set) entry.getValue();
serviceManagers.remove(registryFutureTask);
if (serviceManagers.isEmpty()) {
iterator.remove();
}
}
}
}
private static Set getAllSuperClasses(Class clazz) {
Set allSuperClasses = new LinkedHashSet();
for (Class superClass = clazz.getSuperclass(); superClass != null; superClass = superClass.getSuperclass()) {
allSuperClasses.add(superClass);
}
return allSuperClasses;
}
private static Set getAllInterfaces(Class clazz) {
Set allInterfaces = new LinkedHashSet();
LinkedList stack = new LinkedList();
stack.addAll(Arrays.asList(clazz.getInterfaces()));
while (!stack.isEmpty()) {
Class intf = (Class) stack.removeFirst();
if (!allInterfaces.contains(intf)) {
allInterfaces.add(intf);
stack.addAll(Arrays.asList(intf.getInterfaces()));
}
}
return allInterfaces;
}
}