package tc.oc.commons.core.inject; import java.util.List; import com.google.inject.Binding; import com.google.inject.Provider; import com.google.inject.ProvisionException; import com.google.inject.spi.Dependency; import com.google.inject.spi.DependencyAndSource; import com.google.inject.spi.InjectionPoint; import com.google.inject.spi.ProvisionListener; /** * A {@link Provider} that knows the binding and injection point for each provision. * In other words, it knows what it is providing *to*, more or less. * * In order for this to work, the {@link ContextualProviderModule} must be installed. * That module installs a {@link ProvisionListener} which passes the details of each * provisioning to this class via a {@link ThreadLocal}. This is pretty hacky, but * Guice does not provide any straightforward way to do it. * * If this is a violation of some fundamental principle, I haven't been able to figure * out what that is. So, until the sky falls, we'll keep doing this, because it is * very, very useful. */ public abstract class ContextualProvider<T> implements Provider<T> { static final ThreadLocal<ProvisionListener.ProvisionInvocation<?>> provisionInvocation = new ThreadLocal<>(); /** * Provides an instance of {@link T} for a given binding and dependency. * The {@link InjectionPoint}s are available through the {@link DependencyAndSource} * objects. The last dependency in the list is the one being directly provided. * * Note carefully the cases in which {@link DependencyAndSource#getDependency} and * {@link Dependency#getInjectionPoint} can return null. */ protected abstract T getFor(Binding<T> binding, List<DependencyAndSource> dependencyChain); /** * Provided an instance of {@link T} when no provisioning context is available * i.e. when the {@link ProvisionListener} is not called. I don't know if or when * it is possible for this to happen. */ protected T getWithoutContext() { throw new ProvisionException("No context available"); } @Override final public T get() { final ProvisionListener.ProvisionInvocation<T> pi = (ProvisionListener.ProvisionInvocation<T>) provisionInvocation.get(); if(pi == null) { return getWithoutContext(); } else { return getFor(pi.getBinding(), pi.getDependencyChain()); } } }