blob: dd941aa7c27fe36e7317b23f2ce3794b90d19a9f [file] [log] [blame]
package com.google.inject.internal;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.ProviderWithExtensionVisitor;
/**
* A {@link ProviderInstanceBindingImpl} for implementing 'native' guice extensions.
*
* <p>Beyond the normal binding contract that is mostly handled by our baseclass, this also
* implements {@link DelayedInitialize} in order to initialize factory state.
*/
final class InternalProviderInstanceBindingImpl<T> extends ProviderInstanceBindingImpl<T>
implements DelayedInitialize {
enum InitializationTiming {
/** This factory can be initialized eagerly. This should be the case for most things. */
EAGER,
/**
* Initialization of this factory should be delayed until after all other static initialization
* completes. This will be useful for factories that need to call {@link
* InjectorImpl#getExistingBinding(Key)} to not create jit bindings, but also want to be able to
* conditionally consume jit bindings created by other other bindings.
*/
DELAYED;
}
private final Factory<T> originalFactory;
InternalProviderInstanceBindingImpl(
InjectorImpl injector,
Key<T> key,
Object source,
Factory<T> originalFactory,
InternalFactory<? extends T> scopedFactory,
Scoping scoping) {
super(
injector,
key,
source,
scopedFactory,
scoping,
originalFactory,
ImmutableSet.<InjectionPoint>of());
this.originalFactory = originalFactory;
}
InitializationTiming getInitializationTiming() {
return originalFactory.initializationTiming;
}
@Override
public void initialize(final InjectorImpl injector, final Errors errors) throws ErrorsException {
originalFactory.source = getSource();
originalFactory.provisionCallback = injector.provisionListenerStore.get(this);
// For these kinds of providers, the 'user supplied provider' is really 'guice supplied'
// So make our user supplied provider just delegate to the guice supplied one.
originalFactory.delegateProvider = getProvider();
originalFactory.initialize(injector, errors);
}
/**
* A base factory implementation. Any Factories that delegate to other bindings should use the
* {@code CyclicFactory} subclass, but trivial factories can use this one.
*/
abstract static class Factory<T> implements InternalFactory<T>, Provider<T>, HasDependencies {
private final InitializationTiming initializationTiming;
private Object source;
private Provider<T> delegateProvider;
ProvisionListenerStackCallback<T> provisionCallback;
Factory(InitializationTiming initializationTiming) {
this.initializationTiming = initializationTiming;
}
/**
* The binding source.
*
* <p>May be useful for augmenting runtime error messages.
*
* <p>Note: this will return {#code null} until {@link #initialize(InjectorImpl, Errors)} has
* already been called.
*/
final Object getSource() {
return source;
}
/**
* A callback that allows for implementations to fetch dependencies on other bindings.
*
* <p>Will be called exactly once, prior to any call to {@link #doProvision}.
*/
abstract void initialize(InjectorImpl injector, Errors errors) throws ErrorsException;
@Override
public final T get() {
Provider<T> local = delegateProvider;
if (local == null) {
throw new IllegalStateException(
"This Provider cannot be used until the Injector has been created.");
}
return local.get();
}
@Override
public T get(final InternalContext context, final Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
if (provisionCallback == null) {
return doProvision(context, dependency);
} else {
return provisionCallback.provision(
context,
new ProvisionCallback<T>() {
@Override
public T call() throws InternalProvisionException {
return doProvision(context, dependency);
}
});
}
}
/**
* Creates an object to be injected.
*
* @throws com.google.inject.internal.InternalProvisionException if a value cannot be provided
* @return instance to be injected
*/
protected abstract T doProvision(InternalContext context, Dependency<?> dependency)
throws InternalProvisionException;
}
/**
* An base factory implementation that can be extended to provide a specialized implementation of
* a {@link ProviderWithExtensionVisitor} and also implements {@link InternalFactory}
*/
abstract static class CyclicFactory<T> extends Factory<T> {
CyclicFactory(InitializationTiming initializationTiming) {
super(initializationTiming);
}
@Override
public final T get(
final InternalContext context, final Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
// We have a circular reference between bindings. Return a proxy.
if (constructionContext.isConstructing()) {
Class<?> expectedType = dependency.getKey().getTypeLiteral().getRawType();
@SuppressWarnings("unchecked")
T proxyType =
(T) constructionContext.createProxy(context.getInjectorOptions(), expectedType);
return proxyType;
}
// Optimization: Don't go through the callback stack if no one's listening.
constructionContext.startConstruction();
try {
if (provisionCallback == null) {
return provision(dependency, context, constructionContext);
} else {
return provisionCallback.provision(
context,
new ProvisionCallback<T>() {
@Override
public T call() throws InternalProvisionException {
return provision(dependency, context, constructionContext);
}
});
}
} finally {
constructionContext.removeCurrentReference();
constructionContext.finishConstruction();
}
}
private T provision(
Dependency<?> dependency,
InternalContext context,
ConstructionContext<T> constructionContext)
throws InternalProvisionException {
try {
T t = doProvision(context, dependency);
constructionContext.setProxyDelegates(t);
return t;
} catch (InternalProvisionException ipe) {
throw ipe.addSource(getSource());
} catch (Throwable t) {
throw InternalProvisionException.errorInProvider(t).addSource(getSource());
}
}
}
}