org.gradle.internal.service.DefaultServiceRegistry Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* 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 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.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicReference;
/**
* A hierarchical {@link ServiceRegistry} implementation.
*
* Subclasses can register services by:
*
* Calling {@link #add(Class, 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 close() or 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 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;
} else {
parentServices = setupParentServices(parents);
allServices = new CompositeServiceProvider(ownServices, parentServices);
}
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 to this registry. The given object is closed when this registry is closed.
*/
public DefaultServiceRegistry add(Class serviceType, final T serviceInstance) {
assertMutable();
ownServices.add(new FixedInstanceService(this, serviceType, serviceInstance));
return this;
}
/**
* 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() {
if (state.compareAndSet(State.INIT, State.STARTED)) {
ownServices.noLongerMutable();
}
}
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 ProviderAnalyser analyser = new ProviderAnalyser();
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(ServiceProvider serviceProvider) {
assertMutable();
if (!(serviceProvider instanceof SingletonService)) {
throw new UnsupportedOperationException("Unsupported service provider type: " + serviceProvider);
}
stoppable.add(serviceProvider);
analyser.addProviderForClassHierarchy(((SingletonService) serviceProvider).serviceClass, serviceProvider);
}
public void noLongerMutable() {
analyser = null;
}
private class ProviderAnalyser {
private final Set> seen = new HashSet>(4, 0.5f);
public void addProviderForClassHierarchy(Class> serviceType, ServiceProvider serviceProvider) {
analyseType(serviceType, serviceProvider);
seen.clear();
}
private void analyseType(Class> type, ServiceProvider serviceProvider) {
if (type == null || type == Object.class) {
return;
}
if (seen.add(type)) {
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);
analyseType(type.getSuperclass(), serviceProvider);
for (Class> iface : type.getInterfaces()) {
analyseType(iface, 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);
}
}
}
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 {
protected final DefaultServiceRegistry owner;
private final Queue dependents = new ConcurrentLinkedQueue();
private volatile T instance;
protected ManagedObjectServiceProvider(DefaultServiceRegistry owner) {
this.owner = owner;
}
protected final void setInstance(T instance) {
this.instance = instance;
}
public final T getInstance() {
T result = instance;
if (result == null) {
synchronized (this) {
result = instance;
if (result == null) {
result = instance = create();
assert instance != null : String.format("create() of %s returned null", toString());
}
}
}
return result;
}
/**
* Subclasses implement this method to create the service instance. It is never called concurrently and may not return null.
*/
protected abstract T create();
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
© 2015 - 2025 Weber Informatics LLC | Privacy Policy