com.netflix.governator.guice.concurrent.ConcurrentProviders Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of governator Show documentation
Show all versions of governator Show documentation
governator developed by Netflix
package com.netflix.governator.guice.concurrent;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.google.common.base.Supplier;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.spi.Toolable;
import com.netflix.governator.annotations.NonConcurrent;
import com.netflix.governator.lifecycle.LifecycleListener;
/**
* Utility class for creating Providers that allow for concurrent instantiation
* of dependencies to a type.
*
* @author elandau
*
*/
public class ConcurrentProviders {
/**
* Create a Provider that will construct all constructor arguments in parallel and wait
* for all dependencies to be constructed before invoking the constructor of the type.
*
* For example, consider the following class that has 4 dependencies
*
* {@code
* @Singleton
* public class Foo {
* @Inject
* public Foo(@NonConcurrent NonConcurrentSingleton, DependencyA a, DependencyB b, Provider c, NonSingletonD d) {
* }
* }
* }
*
* and the following Guice binding to enable the concurrent behavior,
*
* {@code
* public configure() {
* bind(Foo.class).toProvider(ConcurrentProviders.of(Foo.class)).asEagerSingleton();
* }
* }
*
* When Foo is created eagerly (by Guice) the provider will spawn 4 threads each creating
* one of the above dependencies. Note that for Provider the provider will
* be created and not an instance of DependencyC. Also, note that NonConcurrentSingleton
* will not be constructed in a separate thread.
*
* Note that a dedicated pool of N threads (where N is the number of dependencies) is created
* when Foo is first constructed. Upon instantiation of Foo the pool is shut down and the
* resulting instance of Foo cached for future retrieval.
*
* It's also important to note that ALL transitive dependencies of Foo MUST be in the
* FineGrainedLazySingleton scope, otherwise there is a high risk of hitting the global Guice
* Singleton scope deadlock issue. Any parameter that causes this deadlock can be annotated
* with @NonConcurrent to force it to be created within the same thread as the injectee.
*
* @param type
* @return
*/
public static Provider of(final Class extends T> type) {
return new ProviderWithExtensionVisitor() {
private volatile T instance;
private Injector injector;
private Set listeners = Collections.emptySet();
public T get() {
if ( instance == null ) {
synchronized (this) {
if ( instance == null ) {
instance = createAndInjectMember();
}
}
}
return instance;
}
private T createAndInjectMember() {
T instance = create();
injector.injectMembers(instance);
return instance;
}
private T create() {
// Look for an @Inject constructor or just create a new instance if not found
InjectionPoint injectionPoint = InjectionPoint.forConstructorOf(type);
final long startTime = System.nanoTime();
for (LifecycleListener listener : listeners) {
listener.objectInjecting(TypeLiteral.get(type));
}
if (injectionPoint != null) {
List> deps = injectionPoint.getDependencies();
if (deps.size() > 0) {
Constructor> constructor = (Constructor>)injectionPoint.getMember();
// One thread for each dependency
ExecutorService executor = Executors.newCachedThreadPool(
new ThreadFactoryBuilder()
.setDaemon(true)
.setNameFormat("ConcurrentProviders-" + type.getSimpleName() + "-%d")
.build());
try {
List> suppliers = Lists.newArrayListWithCapacity(deps.size());
// Iterate all constructor dependencies and get and instance from the Injector
for (final Dependency> dep : deps) {
if (!isConcurrent(constructor, dep.getParameterIndex())) {
suppliers.add(getCreator(dep.getKey()));
}
else {
final Future> future = executor.submit(new Callable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy