org.gradle.internal.service.DefaultServiceRegistry Maven / Gradle / Ivy
Show all versions of gradle-tooling-api Show documentation
/*
* Copyright 2010 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.gradle.internal.service;
import org.gradle.api.Action;
import org.gradle.internal.Cast;
import org.gradle.internal.Factory;
import org.gradle.internal.concurrent.CompositeStoppable;
import org.gradle.internal.concurrent.Stoppable;
import java.io.Closeable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Formatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;
/**
* A hierarchical {@link ServiceRegistry} implementation.
*
* Subclasses can register services by:
*
* Calling {@link #add(Class, Object)} or {@link #add(Object)} to register a service instance.
*
* Calling {@link #addProvider(Object)} to register a service provider bean. A provider bean may have factory, decorator and configuration methods as described below.
*
* Adding a factory method. A factory method should have a name that starts with 'create', and have a non-void return type. For example, protected SomeService createSomeService() { ....
* }
. Parameters are injected using services from this registry or its parents. Parameter of type {@link ServiceRegistry} will receive the service registry that owns the service. Parameter of
* type {@code List} will receive all services of type T, if any. If a parameter has the same type as the return type of the factory method, then that parameter will be located in the parent registry.
* This allows decorating services.
*
* Adding a configure method. A configure method should be called 'configure', take a {@link ServiceRegistration} parameter, and a have a void return type. Additional parameters are injected using
* services from this registry or its parents.
*
*
*
* Service instances are created on demand. {@link #getFactory(Class)} looks for a service instance which implements {@code Factory} where {@code T} is the expected type.
*
* Service instances and factories are closed when the registry that created them is closed using {@link #close()}. If a service instance or factory implements {@link java.io.Closeable} or {@link
* org.gradle.internal.concurrent.Stoppable} then the appropriate {@link Closeable#close()} or {@link Stoppable#stop()} method is called. Instances are closed in reverse dependency order.
*
* Service registries are arranged in a hierarchy. If a service of a given type cannot be located, the registry uses its parent registry, if any, to locate the service.
*/
public class DefaultServiceRegistry implements ServiceRegistry, Closeable, ContainsServices {
private enum State {INIT, STARTED, CLOSED}
private final static ServiceRegistry[] NO_PARENTS = new ServiceRegistry[0];
private final static Service[] NO_DEPENDENTS = new Service[0];
private final static Object[] NO_PARAMS = new Object[0];
private final ClassInspector inspector;
private final OwnServices ownServices;
private final ServiceProvider allServices;
private final ServiceProvider parentServices;
private final String displayName;
private final ServiceProvider thisAsServiceProvider;
private final AtomicReference state = new AtomicReference(State.INIT);
public DefaultServiceRegistry() {
this(null, NO_PARENTS);
}
public DefaultServiceRegistry(String displayName) {
this(displayName, NO_PARENTS);
}
public DefaultServiceRegistry(ServiceRegistry... parents) {
this(null, parents);
}
public DefaultServiceRegistry(String displayName, ServiceRegistry... parents) {
this.displayName = displayName;
this.ownServices = new OwnServices();
if (parents.length == 0) {
this.parentServices = null;
this.allServices = ownServices;
this.inspector = new ClassInspector();
} else {
this.parentServices = setupParentServices(parents);
this.allServices = new CompositeServiceProvider(ownServices, parentServices);
this.inspector = parents[0] instanceof DefaultServiceRegistry ? ((DefaultServiceRegistry) parents[0]).inspector : new ClassInspector();
}
this.thisAsServiceProvider = allServices;
findProviderMethods(this);
}
private static ServiceProvider setupParentServices(ServiceRegistry[] parents) {
ServiceProvider parentServices;
if (parents.length == 1) {
parentServices = toParentServices(parents[0]);
} else {
ServiceProvider[] parentServiceProviders = new ServiceProvider[parents.length];
for (int i = 0; i < parents.length; i++) {
parentServiceProviders[i] = toParentServices(parents[i]);
}
parentServices = new CompositeServiceProvider(parentServiceProviders);
}
return parentServices;
}
@Override
public ServiceProvider asProvider() {
return thisAsServiceProvider;
}
private static ServiceProvider toParentServices(ServiceRegistry serviceRegistry) {
if (serviceRegistry instanceof ContainsServices) {
return new ParentServices(((ContainsServices) serviceRegistry).asProvider());
}
throw new IllegalArgumentException(String.format("Service registry %s cannot be used as a parent for another service registry.", serviceRegistry));
}
/**
* Creates a service registry that uses the given providers.
*/
public static ServiceRegistry create(Object... providers) {
DefaultServiceRegistry registry = new DefaultServiceRegistry();
for (Object provider : providers) {
registry.addProvider(provider);
}
return registry;
}
private String getDisplayName() {
return displayName == null ? getClass().getSimpleName() : displayName;
}
@Override
public String toString() {
return getDisplayName();
}
private void findProviderMethods(Object target) {
Class> type = target.getClass();
RelevantMethods methods = RelevantMethods.getMethods(type);
for (ServiceMethod method : methods.decorators) {
if (parentServices == null) {
throw new ServiceLookupException(String.format("Cannot use decorator method %s.%s() when no parent registry is provided.", type.getSimpleName(), method.getName()));
}
ownServices.add(new FactoryMethodService(this, target, method));
}
for (ServiceMethod method : methods.factories) {
ownServices.add(new FactoryMethodService(this, target, method));
}
for (ServiceMethod method : methods.configurers) {
applyConfigureMethod(method, target);
}
}
private void applyConfigureMethod(ServiceMethod method, Object target) {
Object[] params = new Object[method.getParameterTypes().length];
for (int i = 0; i < method.getParameterTypes().length; i++) {
Type paramType = method.getParameterTypes()[i];
if (paramType.equals(ServiceRegistration.class)) {
params[i] = newRegistration();
} else {
Service paramProvider = find(paramType, allServices);
if (paramProvider == null) {
throw new ServiceLookupException(String.format("Cannot configure services using %s.%s() as required service of type %s is not available.",
method.getOwner().getSimpleName(),
method.getName(),
format(paramType)));
}
params[i] = paramProvider.get();
}
}
try {
method.invoke(target, params);
} catch (Exception e) {
throw new ServiceLookupException(String.format("Could not configure services using %s.%s().",
method.getOwner().getSimpleName(),
method.getName()), e);
}
}
/**
* Adds services to this container using the given action.
*/
public void register(Action super ServiceRegistration> action) {
assertMutable();
action.execute(newRegistration());
}
private void assertMutable() {
if (state.get() != State.INIT) {
throw new IllegalStateException("Cannot add services to service registry " + this + " as it is no longer mutable");
}
}
private ServiceRegistration newRegistration() {
return new ServiceRegistration() {
@Override
public void add(Class serviceType, T serviceInstance) {
DefaultServiceRegistry.this.add(serviceType, serviceInstance);
}
@Override
public void add(Class> serviceType) {
ownServices.add(new ConstructorService(DefaultServiceRegistry.this, serviceType));
}
@Override
public void addProvider(Object provider) {
DefaultServiceRegistry.this.addProvider(provider);
}
};
}
/**
* Adds a service instance to this registry with the given public type. The given object is closed when this registry is closed.
*/
public DefaultServiceRegistry add(Class extends T> serviceType, T serviceInstance) {
assertMutable();
ownServices.add(new FixedInstanceService(this, serviceType, serviceInstance));
return this;
}
/**
* Adds a service instance to this registry. The given object is closed when this registry is closed.
*/
public DefaultServiceRegistry add(Object serviceInstance) {
return add(serviceInstance.getClass(), serviceInstance);
}
/**
* Adds a service provider bean to this registry. This provider may define factory and decorator methods.
*/
public DefaultServiceRegistry addProvider(Object provider) {
assertMutable();
findProviderMethods(provider);
return this;
}
/**
* Closes all services for this registry. For each service, if the service has a public void close() or stop() method, that method is called to close the service.
*/
@Override
public void close() {
noLongerMutable();
if (state.compareAndSet(State.STARTED, State.CLOSED)) {
CompositeStoppable.stoppable(allServices).stop();
}
}
private void serviceRequested() {
noLongerMutable();
if (state.get() == State.CLOSED) {
throw new IllegalStateException(String.format("%s has been closed.", getDisplayName()));
}
}
private void noLongerMutable() {
state.compareAndSet(State.INIT, State.STARTED);
}
public boolean isClosed() {
return state.get() == State.CLOSED;
}
@Override
public T get(Class serviceType) throws UnknownServiceException, ServiceLookupException {
return serviceType.cast(get((Type) serviceType));
}
@Override
public Object get(Type serviceType) throws UnknownServiceException, ServiceLookupException {
Object instance = find(serviceType);
if (instance == null) {
throw new UnknownServiceException(serviceType, String.format("No service of type %s available in %s.", format(serviceType), getDisplayName()));
}
return instance;
}
@Override
public Object get(Type serviceType, Class extends Annotation> annotatedWith) throws UnknownServiceException, ServiceLookupException {
throw new UnknownServiceException(serviceType, String.format("No service of type %s annotated with @%s available in %s.", format(serviceType), annotatedWith.getSimpleName(), getDisplayName()));
}
@Override
public Object find(Type serviceType) throws ServiceLookupException {
assertValidServiceType(unwrap(serviceType));
Service provider = getService(serviceType);
return provider == null ? null : provider.get();
}
private Service getService(Type serviceType) {
serviceRequested();
return find(serviceType, allServices);
}
@Override
public Factory getFactory(Class type) {
assertValidServiceType(type);
Service provider = getFactoryService(type);
Factory factory = provider == null ? null : Cast.>uncheckedCast(provider.get());
if (factory == null) {
throw new UnknownServiceException(type, String.format("No factory for objects of type %s available in %s.", format(type), getDisplayName()));
}
return factory;
}
private Service getFactoryService(Class> serviceType) {
serviceRequested();
return allServices.getFactory(serviceType);
}
@Override
public List getAll(Class serviceType) throws ServiceLookupException {
assertValidServiceType(serviceType);
List services = new ArrayList();
serviceRequested();
allServices.getAll(serviceType, new InstanceUnpackingVisitor(serviceType, services));
return services;
}
private static class InstanceUnpackingVisitor implements ServiceProvider.Visitor {
private final Class serviceType;
private final List delegate;
private InstanceUnpackingVisitor(Class serviceType, List delegate) {
this.serviceType = serviceType;
this.delegate = delegate;
}
@Override
public void visit(Service service) {
T instance = serviceType.cast(service.get());
if (!delegate.contains(instance)) {
delegate.add(instance);
}
}
}
private static class CollectingVisitor implements ServiceProvider.Visitor {
private final List delegate;
private CollectingVisitor(List delegate) {
this.delegate = delegate;
}
@Override
public void visit(Service service) {
if (!delegate.contains(service)) {
delegate.add(service);
}
}
}
@Override
public T newInstance(Class type) {
return getFactory(type).create();
}
private class OwnServices implements ServiceProvider {
private final Map, List> providersByType = new HashMap, List>(16, 0.5f);
private final CompositeStoppable stoppable = CompositeStoppable.stoppable();
private final List services = new ArrayList();
private final List lifecycleHandlers = new ArrayList();
public OwnServices() {
providersByType.put(ServiceRegistry.class, Collections.singletonList(new ThisAsService()));
}
@Override
public Service getFactory(Class> type) {
List serviceProviders = getProviders(Factory.class);
if (serviceProviders.isEmpty()) {
return null;
}
if (serviceProviders.size() == 1) {
return serviceProviders.get(0).getFactory(type);
}
List services = new ArrayList(serviceProviders.size());
for (ServiceProvider serviceProvider : serviceProviders) {
Service service = serviceProvider.getFactory(type);
if (service != null) {
services.add(service);
}
}
if (services.isEmpty()) {
return null;
}
if (services.size() == 1) {
return services.get(0);
}
Set descriptions = new TreeSet();
for (Service candidate : services) {
descriptions.add(candidate.getDisplayName());
}
Formatter formatter = new Formatter();
formatter.format("Multiple factories for objects of type %s available in %s:", format(type), getDisplayName());
for (String description : descriptions) {
formatter.format("%n - %s", description);
}
throw new ServiceLookupException(formatter.toString());
}
@Override
public Service getService(Type type) {
List serviceProviders = getProviders(unwrap(type));
if (serviceProviders.isEmpty()) {
return null;
}
if (serviceProviders.size() == 1) {
return serviceProviders.get(0).getService(type);
}
List services = new ArrayList(serviceProviders.size());
for (ServiceProvider serviceProvider : serviceProviders) {
Service service = serviceProvider.getService(type);
if (service != null) {
services.add(service);
}
}
if (services.isEmpty()) {
return null;
}
if (services.size() == 1) {
return services.get(0);
}
Set descriptions = new TreeSet();
for (Service candidate : services) {
descriptions.add(candidate.getDisplayName());
}
Formatter formatter = new Formatter();
formatter.format("Multiple services of type %s available in %s:", format(type), getDisplayName());
for (String description : descriptions) {
formatter.format("%n - %s", description);
}
throw new ServiceLookupException(formatter.toString());
}
private List getProviders(Class> type) {
List providers = providersByType.get(type);
return providers == null ? Collections.emptyList() : providers;
}
@Override
public Visitor getAll(Class> serviceType, ServiceProvider.Visitor visitor) {
for (ServiceProvider serviceProvider : getProviders(serviceType)) {
visitor = serviceProvider.getAll(serviceType, visitor);
}
return visitor;
}
@Override
public void stop() {
stoppable.stop();
}
public void add(SingletonService serviceProvider) {
assertMutable();
stoppable.add(serviceProvider);
collectProvidersForClassHierarchy(inspector, serviceProvider.serviceClass, serviceProvider);
services.add(serviceProvider);
for (AnnotatedServiceLifecycleHandler annotationHandler : lifecycleHandlers) {
if (inspector.hasAnnotation(serviceProvider.serviceClass, annotationHandler.getAnnotation())) {
annotationHandler.whenRegistered(new RegistrationWrapper(serviceProvider));
}
}
}
public void collectProvidersForClassHierarchy(ClassInspector inspector, Class> serviceType, ServiceProvider serviceProvider) {
for (Class> type : inspector.getHierarchy(serviceType)) {
if (type.equals(ServiceRegistry.class)) {
// Disallow custom services of type ServiceRegistry, as these are automatically provided
throw new IllegalArgumentException("Cannot define a service of type ServiceRegistry: " + serviceProvider);
}
putServiceType(type, serviceProvider);
}
}
private void putServiceType(Class> type, ServiceProvider serviceProvider) {
List serviceProviders = providersByType.get(type);
if (serviceProviders == null) {
serviceProviders = new ArrayList(2);
providersByType.put(type, serviceProviders);
}
serviceProviders.add(serviceProvider);
}
public void instanceRealized(ManagedObjectServiceProvider serviceProvider, Object instance) {
Class> serviceType = serviceProvider.getServiceType();
if (instance instanceof AnnotatedServiceLifecycleHandler && !AnnotatedServiceLifecycleHandler.class.isAssignableFrom(serviceType)) {
throw new IllegalStateException(String.format("%s implements %s but is not declared as a service of this type. This service is declared as having type %s.", serviceProvider.getDisplayName(), AnnotatedServiceLifecycleHandler.class.getSimpleName(), format(serviceType)));
}
if (instance instanceof AnnotatedServiceLifecycleHandler) {
annotationHandlerCreated((AnnotatedServiceLifecycleHandler) instance);
}
for (AnnotatedServiceLifecycleHandler lifecycleHandler : lifecycleHandlers) {
boolean implementationHasAnnotation = inspector.hasAnnotation(instance.getClass(), lifecycleHandler.getAnnotation());
boolean declaredWithAnnotation = inspector.hasAnnotation(serviceType, lifecycleHandler.getAnnotation());
if (implementationHasAnnotation && !declaredWithAnnotation) {
throw new IllegalStateException(String.format("%s is annotated with @%s but is not declared as a service with this annotation. This service is declared as having type %s.", serviceProvider.getDisplayName(), format(lifecycleHandler.getAnnotation()), format(serviceType)));
}
}
}
void annotationHandlerCreated(AnnotatedServiceLifecycleHandler annotationHandler) {
lifecycleHandlers.add(annotationHandler);
for (SingletonService candidate : services) {
if (inspector.hasAnnotation(candidate.serviceClass, annotationHandler.getAnnotation())) {
annotationHandler.whenRegistered(new RegistrationWrapper(candidate));
}
}
}
}
private class RegistrationWrapper implements AnnotatedServiceLifecycleHandler.Registration {
private final SingletonService serviceProvider;
public RegistrationWrapper(SingletonService serviceProvider) {
this.serviceProvider = serviceProvider;
}
@Override
public Class> getDeclaredType() {
return serviceProvider.serviceClass;
}
@Override
public Object getInstance() {
serviceRequested();
return serviceProvider.getService(serviceProvider.serviceClass).get();
}
}
private static Class> unwrap(Type type) {
if (type instanceof Class) {
return (Class) type;
} else {
if (type instanceof WildcardType) {
final WildcardType wildcardType = (WildcardType) type;
if (wildcardType.getUpperBounds()[0] instanceof Class && wildcardType.getLowerBounds().length == 0) {
return (Class>) wildcardType.getUpperBounds()[0];
}
}
ParameterizedType parameterizedType = (ParameterizedType) type;
return (Class) parameterizedType.getRawType();
}
}
private static abstract class ManagedObjectServiceProvider implements ServiceProvider, Service {
protected final DefaultServiceRegistry owner;
private final Queue dependents = new ConcurrentLinkedQueue();
private volatile Object instance;
protected ManagedObjectServiceProvider(DefaultServiceRegistry owner) {
this.owner = owner;
}
abstract Class> getServiceType();
protected void setInstance(Object instance) {
this.instance = instance;
owner.ownServices.instanceRealized(this, instance);
}
public final Object getInstance() {
Object result = instance;
if (result == null) {
synchronized (this) {
result = instance;
if (result == null) {
setInstance(createServiceInstance());
result = instance;
}
}
}
return result;
}
/**
* Subclasses implement this method to create the service instance. It is never called concurrently and may not return null.
*/
protected abstract Object createServiceInstance();
public final void requiredBy(ServiceProvider serviceProvider) {
if (fromSameRegistry(serviceProvider)) {
dependents.add(serviceProvider);
}
}
private boolean fromSameRegistry(ServiceProvider serviceProvider) {
return serviceProvider instanceof ManagedObjectServiceProvider && ((ManagedObjectServiceProvider) serviceProvider).owner == owner;
}
@Override
public final synchronized void stop() {
try {
if (instance != null) {
CompositeStoppable.stoppable(dependents).add(instance).stop();
}
} finally {
dependents.clear();
instance = null;
}
}
}
private static abstract class SingletonService extends ManagedObjectServiceProvider {
private enum BindState {UNBOUND, BINDING, BOUND}
final Type serviceType;
final Class> serviceClass;
BindState state = BindState.UNBOUND;
Class> factoryElementType;
SingletonService(DefaultServiceRegistry owner, Type serviceType) {
super(owner);
this.serviceType = serviceType;
serviceClass = unwrap(serviceType);
}
@Override
Class> getServiceType() {
return serviceClass;
}
@Override
public String toString() {
return getDisplayName();
}
@Override
public Object get() {
return getInstance();
}
private Service prepare() {
if (state == BindState.BOUND) {
return this;
}
synchronized (this) {
if (state == BindState.BINDING) {
throw new ServiceValidationException("Cycle in dependencies of " + getDisplayName() + " detected");
}
if (state == BindState.UNBOUND) {
state = BindState.BINDING;
try {
bind();
state = BindState.BOUND;
} catch (RuntimeException e) {
state = BindState.UNBOUND;
throw e;
}
}
return this;
}
}
/**
* Do any preparation work and validation to ensure that {@link #createServiceInstance()} ()} can be called later.
* This method is never called concurrently.
*/
protected void bind() {
}
@Override
public Service getService(Type serviceType) {
if (!isSatisfiedBy(serviceType, this.serviceType)) {
return null;
}
return prepare();
}
@Override
public Visitor getAll(Class> serviceType, ServiceProvider.Visitor visitor) {
if (serviceType.isAssignableFrom(this.serviceClass)) {
visitor.visit(prepare());
}
return visitor;
}
@Override
public Service getFactory(Class> elementType) {
if (!isFactory(serviceType, elementType)) {
return null;
}
return prepare();
}
private boolean isFactory(Type type, Class> elementType) {
Class> c = unwrap(type);
if (!Factory.class.isAssignableFrom(c)) {
return false;
}
if (factoryElementType != null) {
return elementType.isAssignableFrom(factoryElementType);
}
if (type instanceof ParameterizedType) {
// Check if type is Factory extends ElementType>
ParameterizedType parameterizedType = (ParameterizedType) type;
if (parameterizedType.getRawType().equals(Factory.class)) {
Type actualType = parameterizedType.getActualTypeArguments()[0];
if (actualType instanceof Class) {
factoryElementType = (Class>) actualType;
return elementType.isAssignableFrom((Class>) actualType);
}
}
}
// Check if type extends Factory extends ElementType>
for (Type interfaceType : c.getGenericInterfaces()) {
if (isFactory(interfaceType, elementType)) {
return true;
}
}
return false;
}
}
private static abstract class FactoryService extends SingletonService {
private Service[] paramServices;
private Service decorates;
protected FactoryService(DefaultServiceRegistry owner, Type serviceType) {
super(owner, serviceType);
}
protected abstract Type[] getParameterTypes();
protected abstract Member getFactory();
protected abstract String getFactoryDisplayName();
@Override
protected void bind() {
Type[] parameterTypes = getParameterTypes();
if (parameterTypes.length == 0) {
paramServices = NO_DEPENDENTS;
return;
}
paramServices = new Service[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Type paramType = parameterTypes[i];
if (paramType.equals(serviceType)) {
// A decorating factory
Service paramProvider = find(paramType, owner.parentServices);
if (paramProvider == null) {
throw new ServiceCreationException(String.format("Cannot create service of type %s using %s as required service of type %s for parameter #%s is not available in parent registries.",
format(serviceType),
getFactoryDisplayName(),
format(paramType),
i + 1));
}
paramServices[i] = paramProvider;
decorates = paramProvider;
} else {
Service paramProvider;
try {
paramProvider = find(paramType, owner.allServices);
} catch (ServiceLookupException e) {
throw new ServiceCreationException(String.format("Cannot create service of type %s using %s as there is a problem with parameter #%s of type %s.",
format(serviceType),
getFactoryDisplayName(),
i + 1,
format(paramType)), e);
}
if (paramProvider == null) {
throw new ServiceCreationException(String.format("Cannot create service of type %s using %s as required service of type %s for parameter #%s is not available.",
format(serviceType),
getFactoryDisplayName(),
format(paramType),
i + 1));
}
paramServices[i] = paramProvider;
paramProvider.requiredBy(this);
}
}
}
@Override
protected Object createServiceInstance() {
Object[] params = assembleParameters();
Object result = invokeMethod(params);
// Can discard the state required to create instance
paramServices = null;
return result;
}
private Object[] assembleParameters() {
if (paramServices == NO_DEPENDENTS) {
return NO_PARAMS;
}
Object[] params = new Object[paramServices.length];
for (int i = 0; i < paramServices.length; i++) {
Service paramProvider = paramServices[i];
params[i] = paramProvider.get();
}
return params;
}
@Override
public Visitor getAll(Class> serviceType, final Visitor visitor) {
super.getAll(serviceType, visitor);
if (decorates == null) {
return visitor;
} else {
return new Visitor() {
@Override
public void visit(Service service) {
// Ignore the decorated service
if (service != decorates) {
visitor.visit(service);
}
}
};
}
}
protected abstract Object invokeMethod(Object[] params);
}
private static class FactoryMethodService extends FactoryService {
private final ServiceMethod method;
private Object target;
public FactoryMethodService(DefaultServiceRegistry owner, Object target, ServiceMethod method) {
super(owner, method.getServiceType());
this.target = target;
this.method = method;
}
@Override
public String getDisplayName() {
return "Service " + format(method.getServiceType()) + " at " + format(method.getOwner()) + "." + method.getName() + "()";
}
@Override
protected Type[] getParameterTypes() {
return method.getParameterTypes();
}
@Override
protected Member getFactory() {
return method.getMethod();
}
@Override
protected String getFactoryDisplayName() {
return String.format("method %s.%s()", format(getFactory().getDeclaringClass()), getFactory().getName());
}
@Override
protected Object invokeMethod(Object[] params) {
Object result;
try {
result = method.invoke(target, params);
} catch (Exception e) {
throw new ServiceCreationException(String.format("Could not create service of type %s using %s.%s().",
format(serviceType),
method.getOwner().getSimpleName(),
method.getName()),
e);
}
try {
if (result == null) {
throw new ServiceCreationException(String.format("Could not create service of type %s using %s.%s() as this method returned null.",
format(serviceType),
method.getOwner().getSimpleName(),
method.getName()));
}
return result;
} finally {
// Can discard the state required to create instance
target = null;
}
}
}
private static class FixedInstanceService extends SingletonService {
public FixedInstanceService(DefaultServiceRegistry owner, Class> serviceType, Object serviceInstance) {
super(owner, serviceType);
setInstance(serviceInstance);
}
@Override
public String getDisplayName() {
return "Service " + format(serviceType) + " with implementation " + format(getInstance().getClass());
}
@Override
protected Object createServiceInstance() {
throw new UnsupportedOperationException();
}
}
private static class ConstructorService extends FactoryService {
private final Constructor> constructor;
private ConstructorService(DefaultServiceRegistry owner, Class> serviceType) {
super(owner, serviceType);
if (serviceType.isInterface()) {
throw new ServiceValidationException("Cannot register an interface for construction.");
}
Constructor> match = InjectUtil.selectConstructor(serviceType);
if (InjectUtil.isPackagePrivate(match.getModifiers()) || Modifier.isPrivate(match.getModifiers())) {
match.setAccessible(true);
}
this.constructor = match;
}
@Override
protected Type[] getParameterTypes() {
return constructor.getGenericParameterTypes();
}
@Override
protected Member getFactory() {
return constructor;
}
@Override
protected String getFactoryDisplayName() {
return String.format("%s constructor", format(getFactory().getDeclaringClass()));
}
@Override
protected Object invokeMethod(Object[] params) {
try {
return constructor.newInstance(params);
} catch (InvocationTargetException e) {
throw new ServiceCreationException(String.format("Could not create service of type %s.", format(serviceType)), e.getCause());
} catch (Exception e) {
throw new ServiceCreationException(String.format("Could not create service of type %s.", format(serviceType)), e);
}
}
@Override
public String getDisplayName() {
return "Service " + format(serviceType);
}
}
private static class CompositeServiceProvider implements ServiceProvider {
private final ServiceProvider[] serviceProviders;
private CompositeServiceProvider(ServiceProvider... serviceProviders) {
this.serviceProviders = serviceProviders;
}
@Override
public Service getService(Type serviceType) {
for (ServiceProvider serviceProvider : serviceProviders) {
Service service = serviceProvider.getService(serviceType);
if (service != null) {
return service;
}
}
return null;
}
@Override
public Service getFactory(Class> type) {
for (ServiceProvider serviceProvider : serviceProviders) {
Service factory = serviceProvider.getFactory(type);
if (factory != null) {
return factory;
}
}
return null;
}
@Override
public Visitor getAll(Class> serviceType, Visitor visitor) {
for (ServiceProvider serviceProvider : serviceProviders) {
visitor = serviceProvider.getAll(serviceType, visitor);
}
return visitor;
}
@Override
public void stop() {
try {
CompositeStoppable.stoppable(Arrays.asList(serviceProviders)).stop();
} finally {
Arrays.fill(serviceProviders, null);
}
}
}
/**
* Wraps a parent to ignore stop requests.
*/
private static class ParentServices implements ServiceProvider {
private final ServiceProvider parent;
private ParentServices(ServiceProvider parent) {
this.parent = parent;
}
@Override
public Service getFactory(Class> serviceType) {
return parent.getFactory(serviceType);
}
@Override
public Service getService(Type serviceType) {
return parent.getService(serviceType);
}
@Override
public Visitor getAll(Class> serviceType, Visitor visitor) {
return parent.getAll(serviceType, visitor);
}
@Override
public void stop() {
}
}
private static Service find(Type serviceType, ServiceProvider serviceProvider) {
if (serviceType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) serviceType;
Type rawType = parameterizedType.getRawType();
if (rawType.equals(Factory.class)) {
final Type typeArg = parameterizedType.getActualTypeArguments()[0];
return getFactoryService(typeArg, serviceProvider);
}
if (rawType instanceof Class) {
if (((Class>) rawType).isAssignableFrom(List.class)) {
Type typeArg = parameterizedType.getActualTypeArguments()[0];
return getCollectionService(typeArg, serviceProvider);
}
assertValidServiceType((Class>) rawType);
return serviceProvider.getService(serviceType);
}
}
if (serviceType instanceof Class>) {
assertValidServiceType((Class>) serviceType);
return serviceProvider.getService(serviceType);
}
throw new ServiceValidationException(String.format("Locating services with type %s is not supported.", format(serviceType)));
}
private static Service getFactoryService(Type type, ServiceProvider serviceProvider) {
if (type instanceof Class) {
return serviceProvider.getFactory((Class) type);
}
if (type instanceof WildcardType) {
final WildcardType wildcardType = (WildcardType) type;
if (wildcardType.getLowerBounds().length == 1 && wildcardType.getUpperBounds().length == 1) {
if (wildcardType.getLowerBounds()[0] instanceof Class && wildcardType.getUpperBounds()[0].equals(Object.class)) {
return serviceProvider.getFactory((Class>) wildcardType.getLowerBounds()[0]);
}
}
if (wildcardType.getLowerBounds().length == 0 && wildcardType.getUpperBounds().length == 1) {
if (wildcardType.getUpperBounds()[0] instanceof Class) {
return serviceProvider.getFactory((Class>) wildcardType.getUpperBounds()[0]);
}
}
}
throw new ServiceValidationException(String.format("Locating services with type %s is not supported.", format(type)));
}
private static Service getCollectionService(Type elementType, ServiceProvider serviceProvider) {
if (elementType instanceof Class) {
Class> elementClass = (Class>) elementType;
return getCollectionService(elementClass, serviceProvider);
}
if (elementType instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) elementType;
if (wildcardType.getUpperBounds()[0] instanceof Class && wildcardType.getLowerBounds().length == 0) {
Class> elementClass = (Class>) wildcardType.getUpperBounds()[0];
return getCollectionService(elementClass, serviceProvider);
}
}
throw new ServiceValidationException(String.format("Locating services with type %s is not supported.", format(elementType)));
}
private static Service getCollectionService(Class> elementClass, ServiceProvider serviceProvider) {
assertValidServiceType(elementClass);
List providers = new ArrayList();
serviceProvider.getAll(elementClass, new CollectingVisitor(providers));
List