Package com.netflix.governator.guice.concurrent

Source Code of com.netflix.governator.guice.concurrent.ConcurrentProviders

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<DependencyC> 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<DependencyC> 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
     * <b>FineGrainedLazySingleton</b> 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 <T> Provider<T> of(final Class<? extends T> type) {
        return new ProviderWithExtensionVisitor<T>() {
            private volatile T instance;
            private Injector injector;
            private Set<LifecycleListener> 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<Dependency<?>> 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<Supplier<?>> 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<Object>() {
                                        @Override
                                        public Object call() throws Exception {
                                            return getCreator(dep.getKey()).get();
                                        }
                                    });
                                    suppliers.add(new Supplier() {
                                        @Override
                                        public Object get() {
                                            try {
                                                return future.get();
                                            } catch (InterruptedException e) {
                                                Thread.currentThread().interrupt();
                                                throw new ProvisionException("interrupted during provision");
                                            } catch (ExecutionException e) {
                                                throw new RuntimeException(e.getCause());
                                            }
                                        }
                                    });
                                }
                            }
                            // All dependencies are now being instantiated in parallel
                           
                            // Fetch the arguments from the futures and put in an array to pass to newInstance
                            List<Object> params = Lists.newArrayListWithCapacity(deps.size());
                            for (Supplier<?> supplier: suppliers) {
                                params.add(supplier.get());
                            }
                           
                            // All dependencies have been initialized
                           
                            // Look for the @Inject constructor and invoke it.
                            try {
                                T obj = (T)constructor.newInstance(params.toArray());
                                long duration = System.nanoTime() - startTime;
                                for (LifecycleListener listener : listeners) {
                                    listener.objectInjected((TypeLiteral<T>)TypeLiteral.get(type), obj, duration, TimeUnit.NANOSECONDS);
                                }
                                return obj;
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        }
                        finally {
                            executor.shutdown();
                        }
                    }
                }
               
                try {
                    T obj = type.newInstance();
                    long duration = System.nanoTime() - startTime;
                    for (LifecycleListener listener : listeners) {
                        listener.objectInjected((TypeLiteral<T>)TypeLiteral.get(type), obj, duration, TimeUnit.NANOSECONDS);
                    }
                    return obj;
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new ProvisionException("Error constructing object of type " + type.getName(), e);
                }
            }
           
            private boolean isConcurrent(Constructor<?> constructor, int parameterIndex) {
                Annotation[] annots = constructor.getParameterAnnotations()[parameterIndex];
                if (annots != null) {
                    for (Annotation annot : annots) {
                        if (annot.annotationType().equals(NonConcurrent.class)) {
                            return false;
                        }
                    }
                }
                return true;
            }

            /**
             * Required to get the Injector in {@link initialize()}
             */
            @Override
            public <B, V> V acceptExtensionVisitor(
                    BindingTargetVisitor<B, V> visitor,
                    ProviderInstanceBinding<? extends B> binding) {
                return visitor.visit(binding);
            }
           
            @Inject
            @Toolable
            void initialize(Injector injector) {
                this.injector = injector;
            }
           
            @Inject(optional = true)
            void setListeners(Set<LifecycleListener> listeners) {
                this.listeners = listeners;
            }
           
            public <S> Supplier<S> getCreator(final Key<S> key) {
                return new Supplier<S>() {
                    @Override
                    public S get() {
                        final long startTime = System.nanoTime();
                        for (LifecycleListener listener : listeners) {
                            listener.objectInjecting(key.getTypeLiteral());
                        }
                        S obj = injector.getInstance(key);
                        final long duration = System.nanoTime() - startTime;
                        for (LifecycleListener listener : listeners) {
                            listener.objectInjected(key.getTypeLiteral(), obj, System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
                        }
                        return obj;
                    }
                };
            }

        };
    }
}
TOP

Related Classes of com.netflix.governator.guice.concurrent.ConcurrentProviders

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.