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 com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.gradle.api.Action;
import org.gradle.api.Nullable;
import org.gradle.api.specs.Spec;
import org.gradle.internal.Factory;
import org.gradle.internal.concurrent.CompositeStoppable;
import org.gradle.internal.concurrent.Stoppable;
import org.gradle.internal.reflect.JavaReflectionUtil;
import org.gradle.internal.util.BiFunction;
import java.io.Closeable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
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.Collection;
import java.util.Collections;
import java.util.Formatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 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. Note that factory methods with a single parameter and an return type equal to that parameter type are interpreted
* as decorator methods.
*
* Adding a decorator method. A decorator method should have a name that starts with 'decorate', take a single parameter, and a have return type equal to the parameter type. Before invoking the
* method, the parameter is located in the parent service registry and then passed to the method.
*
* 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 {
private static final ConcurrentMap, RelevantMethods> METHODS_CACHE = new ConcurrentHashMap, RelevantMethods>();
private static final ConcurrentMap> SERVICE_TYPE_PROVIDER_CACHE = new ConcurrentHashMap>();
private final Map providerCache = new HashMap();
private final Object lock = new Object();
private final OwnServices ownServices;
private final Provider allServices;
private final Provider parentServices;
private final String displayName;
private boolean closed;
private boolean mutable = true; // access under lock
public DefaultServiceRegistry() {
this(null, Collections.emptyList());
}
public DefaultServiceRegistry(String displayName) {
this(displayName, Collections.emptyList());
}
public DefaultServiceRegistry(ServiceRegistry... parents) {
this(null, parents);
}
public DefaultServiceRegistry(String displayName, ServiceRegistry... parents) {
this(displayName, Arrays.asList(parents));
}
public DefaultServiceRegistry(String displayName, Collection extends ServiceRegistry> parents) {
this.displayName = displayName;
this.ownServices = new OwnServices();
if (parents.isEmpty()) {
this.parentServices = null;
this.allServices = ownServices;
} else {
if (parents.size() == 1) {
this.parentServices = new ParentServices(parents.iterator().next());
} else {
List providers = new ArrayList(parents.size());
for (ServiceRegistry parent : parents) {
providers.add(new ParentServices(parent));
}
this.parentServices = new CompositeProvider(providers);
}
List allProviders = new ArrayList(2);
allProviders.add(ownServices);
allProviders.add(parentServices);
allServices = new CachingProvider(new CompositeProvider(allProviders));
}
findProviderMethods(this);
}
/**
* 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();
}
static class RelevantMethods {
final List decorators;
final List factories;
final List configurers;
public RelevantMethods(List decorators, List factories, List configurers) {
this.decorators = decorators;
this.factories = factories;
this.configurers = configurers;
}
}
static class RelevantMethodsBuilder {
final List remainingMethods;
final Class> type;
final LinkedList decorators = new LinkedList();
final LinkedList factories = new LinkedList();
final LinkedList configurers = new LinkedList();
final Set seen = new HashSet();
public RelevantMethodsBuilder(Class> type) {
this.type = type;
this.remainingMethods = new LinkedList();
for (Class> clazz = type; clazz != Object.class && clazz != DefaultServiceRegistry.class; clazz = clazz.getSuperclass()) {
remainingMethods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
}
}
void add(Iterator iterator, List builder, Method method) {
if (seen.add(method.getName())) {
builder.add(method);
}
iterator.remove();
}
RelevantMethods build() {
return new RelevantMethods(decorators, factories, configurers);
}
}
private void findProviderMethods(Object target) {
Class> type = target.getClass();
RelevantMethods methods = getMethods(type);
for (Method 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 DecoratorMethodService(target, method));
}
for (Method method : methods.factories) {
ownServices.add(new FactoryMethodService(target, method));
}
for (Method method : methods.configurers) {
applyConfigureMethod(method, target);
}
}
private RelevantMethods getMethods(Class> type) {
RelevantMethods relevantMethods = METHODS_CACHE.get(type);
if (relevantMethods == null) {
relevantMethods = buildRelevantMethods(type);
METHODS_CACHE.putIfAbsent(type, relevantMethods);
}
return relevantMethods;
}
private RelevantMethods buildRelevantMethods(Class> type) {
RelevantMethods relevantMethods;
RelevantMethodsBuilder builder = new RelevantMethodsBuilder(type);
addDecoratorMethods(builder);
addFactoryMethods(builder);
addConfigureMethods(builder);
relevantMethods = builder.build();
return relevantMethods;
}
private void applyConfigureMethod(Method method, Object target) {
Object[] params = new Object[method.getGenericParameterTypes().length];
DefaultLookupContext context = new DefaultLookupContext();
for (int i = 0; i < method.getGenericParameterTypes().length; i++) {
Type paramType = method.getGenericParameterTypes()[i];
if (paramType.equals(ServiceRegistration.class)) {
params[i] = newRegistration();
} else {
ServiceProvider paramProvider = context.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.getDeclaringClass().getSimpleName(),
method.getName(),
format(paramType)));
}
params[i] = paramProvider.get();
}
}
try {
invoke(method, target, params);
} catch (Exception e) {
throw new ServiceLookupException(String.format("Could not configure services using %s.%s().",
method.getDeclaringClass().getSimpleName(),
method.getName()), e);
}
}
private static void addConfigureMethods(RelevantMethodsBuilder builder) {
Class> type = builder.type;
Iterator iterator = builder.remainingMethods.iterator();
while (iterator.hasNext()) {
Method method = iterator.next();
if (method.getName().equals("configure")) {
if (!method.getReturnType().equals(Void.TYPE)) {
throw new ServiceLookupException(String.format("Method %s.%s() must return void.", type.getSimpleName(), method.getName()));
}
builder.add(iterator, builder.configurers, method);
}
}
}
private static void addFactoryMethods(RelevantMethodsBuilder builder) {
Class> type = builder.type;
Iterator iterator = builder.remainingMethods.iterator();
while (iterator.hasNext()) {
Method method = iterator.next();
if (method.getName().startsWith("create") && !Modifier.isStatic(method.getModifiers())) {
if (method.getReturnType().equals(Void.TYPE)) {
throw new ServiceLookupException(String.format("Method %s.%s() must not return void.", type.getSimpleName(), method.getName()));
}
builder.add(iterator, builder.factories, method);
}
}
}
private static void addDecoratorMethods(RelevantMethodsBuilder builder) {
Class> type = builder.type;
Iterator iterator = builder.remainingMethods.iterator();
while (iterator.hasNext()) {
Method method = iterator.next();
if ((method.getName().startsWith("create") || method.getName().startsWith("decorate"))
&& method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(method.getReturnType())) {
if (method.getReturnType().equals(Void.TYPE)) {
throw new ServiceLookupException(String.format("Method %s.%s() must not return void.", type.getSimpleName(), method.getName()));
}
builder.add(iterator, builder.decorators, method);
}
}
}
/**
* Adds services to this container using the given action.
*/
public void register(Action super ServiceRegistration> action) {
assertMutable();
action.execute(newRegistration());
}
private void assertMutable() {
if (!mutable) {
throw new IllegalStateException("Cannot add provide to service registry " + this + " as it is no longer mutable");
}
}
private ServiceRegistration newRegistration() {
return new ServiceRegistration() {
public void add(Class serviceType, T serviceInstance) {
DefaultServiceRegistry.this.add(serviceType, serviceInstance);
}
public void add(Class> serviceType) {
ownServices.add(new ConstructorService(serviceType));
}
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(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.
*/
public void close() {
synchronized (lock) {
try {
CompositeStoppable.stoppable(allServices).stop();
} finally {
closed = true;
}
}
}
public boolean isClosed() {
return closed;
}
private static String format(Type type) {
if (type instanceof Class) {
Class> aClass = (Class) type;
return aClass.getSimpleName();
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
StringBuilder builder = new StringBuilder();
builder.append(format(parameterizedType.getRawType()));
builder.append("<");
for (int i = 0; i < parameterizedType.getActualTypeArguments().length; i++) {
Type typeParam = parameterizedType.getActualTypeArguments()[i];
if (i > 0) {
builder.append(", ");
}
builder.append(format(typeParam));
}
builder.append(">");
return builder.toString();
}
return type.toString();
}
public List getAll(Class serviceType) throws ServiceLookupException {
synchronized (lock) {
mutable = false;
if (closed) {
throw new IllegalStateException(String.format("Cannot locate service of type %s, as %s has been closed.", format(serviceType), getDisplayName()));
}
List result = new ArrayList();
DefaultLookupContext context = new DefaultLookupContext();
allServices.getAll(context, serviceType, result);
return result;
}
}
public T get(Class serviceType) throws UnknownServiceException, ServiceLookupException {
return serviceType.cast(doGet(serviceType));
}
public Object get(Type serviceType) throws UnknownServiceException, ServiceLookupException {
return doGet(serviceType);
}
private Object doGet(Type serviceType) throws IllegalArgumentException {
synchronized (lock) {
mutable = false;
if (closed) {
throw new IllegalStateException(String.format("Cannot locate service of type %s, as %s has been closed.", format(serviceType), getDisplayName()));
}
ServiceProvider provider = providerCache.get(serviceType);
if (provider == null) {
provider = getServiceProvider(serviceType);
providerCache.put(serviceType, provider);
}
return provider.get();
}
}
private ServiceProvider getServiceProvider(Type serviceType) {
ServiceProvider provider = new DefaultLookupContext().find(serviceType, allServices);
if (provider == null) {
throw new UnknownServiceException(serviceType, String.format("No service of type %s available in %s.", format(serviceType), getDisplayName()));
}
return provider;
}
public Factory getFactory(Class type) {
synchronized (lock) {
if (closed) {
throw new IllegalStateException(String.format("Cannot locate factory for objects of type %s, as %s has been closed.", format(type), getDisplayName()));
}
DefaultLookupContext context = new DefaultLookupContext();
ServiceProvider factory = allServices.getFactory(context, type);
if (factory != null) {
return (Factory) factory.get();
}
throw new UnknownServiceException(type, String.format("No factory for objects of type %s available in %s.", format(type), getDisplayName()));
}
}
public T newInstance(Class type) {
return getFactory(type).create();
}
private static Object invoke(Method method, Object target, Object... args) {
return JavaReflectionUtil.method(target, Object.class, method).invoke(target, args);
}
interface ServiceProvider {
String getDisplayName();
Object get();
void requiredBy(Provider provider);
}
interface Provider extends Stoppable {
/**
* Locates a service instance of the given type. Returns null if this provider does not provide a service of this type.
*/
ServiceProvider getService(LookupContext context, TypeSpec serviceType);
/**
* Locates a factory for services of the given type. Returns null if this provider does not provide any services of this type.
*/
ServiceProvider getFactory(LookupContext context, Class> type);
void getAll(LookupContext context, Class serviceType, List result);
}
private class OwnServices implements Provider {
private List providers;
public ServiceProvider getFactory(LookupContext context, Class> type) {
if (providers == null) {
return null;
}
List candidates = new ArrayList();
ServiceProvider unique = null;
for (Provider provider : providers) {
ServiceProvider factory = provider.getFactory(context, type);
if (factory != null) {
unique = factory;
candidates.add(factory);
}
}
if (candidates.size() == 0) {
return null;
}
if (candidates.size() == 1) {
return candidates.get(0);
}
Set descriptions = new TreeSet();
for (ServiceProvider candidate : candidates) {
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());
}
public ServiceProvider getService(LookupContext context, TypeSpec serviceType) {
if (providers == null) {
return null;
}
List candidates = new ArrayList();
for (Provider provider : providers) {
ServiceProvider service = provider.getService(context, serviceType);
if (service != null) {
candidates.add(service);
}
}
if (candidates.size() == 0) {
return null;
}
if (candidates.size() == 1) {
return candidates.get(0);
}
Set descriptions = new TreeSet();
for (ServiceProvider candidate : candidates) {
descriptions.add(candidate.getDisplayName());
}
Formatter formatter = new Formatter();
formatter.format("Multiple services of type %s available in %s:", format(serviceType.getType()), getDisplayName());
for (String description : descriptions) {
formatter.format("%n - %s", description);
}
throw new ServiceLookupException(formatter.toString());
}
public void getAll(LookupContext context, Class serviceType, List result) {
if (providers == null) {
return;
}
for (Provider provider : providers) {
provider.getAll(context, serviceType, result);
}
}
public void stop() {
if (providers == null) {
return;
}
CompositeStoppable.stoppable(providers).stop();
}
public void add(Provider provider) {
if (providers == null) {
providers = Lists.newArrayList();
}
this.providers.add(provider);
}
}
private static abstract class ManagedObjectProvider implements Provider {
private T instance;
private Set dependents;
protected void setInstance(T instance) {
this.instance = instance;
}
public T getInstance() {
if (instance == null) {
instance = create();
assert instance != null : String.format("create() of %s returned null", toString());
}
return instance;
}
protected abstract T create();
public void requiredBy(Provider provider) {
if (dependents == null) {
dependents = Sets.newHashSet();
}
dependents.add(provider);
}
public void stop() {
try {
if (instance != null) {
CompositeStoppable.stoppable(dependents == null ? Collections.emptyList() : dependents).add(instance).stop();
}
} finally {
if (dependents != null) {
dependents.clear();
}
instance = null;
}
}
}
private static abstract class SingletonService extends ManagedObjectProvider
© 2015 - 2025 Weber Informatics LLC | Privacy Policy