All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.google.inject.internal.Initializer Maven / Gradle / Ivy
package com.google.inject.internal;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.inject.Binding;
import com.google.inject.Key;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.InjectionPoint;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Manages and injects instances at injector-creation time. This is made more complicated by
* instances that request other instances while they're being injected. We overcome this by using
* {@link Initializable}, which attempts to perform injection before use.
*
*/
final class Initializer {
/** Is set to true once {@link #validateOustandingInjections} is called. */
private volatile boolean validationStarted = false;
/**
* Allows us to detect circular dependencies. It's only used during injectable reference
* initialization. After initialization direct access through volatile field is used.
*/
private final CycleDetectingLock.CycleDetectingLockFactory> cycleDetectingLockFactory =
new CycleDetectingLock.CycleDetectingLockFactory<>();
/**
* Instances that need injection during injector creation to a source that registered them. New
* references added before {@link #validateOustandingInjections}. Cleared up in {@link
* #injectAll}.
*/
private final List> pendingInjections = Lists.newArrayList();
/**
* Map that guarantees that no instance would get two references. New references added before
* {@link #validateOustandingInjections}. Cleared up in {@link #validateOustandingInjections}.
*/
private final IdentityHashMap> initializablesCache =
Maps.newIdentityHashMap();
/**
* Registers an instance for member injection when that step is performed.
*
* @param instance an instance that optionally has members to be injected (each annotated with
* @param binding the binding that caused this initializable to be created, if it exists.
* @param source the source location that this injection was requested
* @Inject).
*/
Initializable requestInjection(InjectorImpl injector, T instance, Binding binding,
Object source, Set injectionPoints) {
checkNotNull(source);
Preconditions.checkState(
!validationStarted, "Member injection could not be requested after validation is started");
ProvisionListenerStackCallback provisionCallback =
binding == null ? null : injector.provisionListenerStore.get(binding);
// short circuit if the object has no injections or listeners.
if (instance == null || (injectionPoints.isEmpty()
&& !injector.membersInjectorStore.hasTypeListeners()
&& (provisionCallback == null))) {
return Initializables.of(instance);
}
if (initializablesCache.containsKey(instance)) {
@SuppressWarnings("unchecked") // Map from T to InjectableReference
Initializable cached = (Initializable) initializablesCache.get(instance);
return cached;
}
InjectableReference injectableReference =
new InjectableReference(
injector,
instance,
binding == null ? null : binding.getKey(),
provisionCallback,
source,
cycleDetectingLockFactory.create(instance.getClass()));
initializablesCache.put(instance, injectableReference);
pendingInjections.add(injectableReference);
return injectableReference;
}
/**
* Prepares member injectors for all injected instances. This prompts Guice to do static analysis
* on the injected instances.
*/
void validateOustandingInjections(Errors errors) {
validationStarted = true;
initializablesCache.clear();
for (InjectableReference> reference : pendingInjections) {
try {
reference.validate(errors);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
}
}
/**
* Performs creation-time injections on all objects that require it. Whenever fulfilling an
* injection depends on another object that requires injection, we inject it first. If the two
* instances are codependent (directly or transitively), ordering of injection is arbitrary.
*/
void injectAll(final Errors errors) {
Preconditions.checkState(validationStarted, "Validation should be done before injection");
for (InjectableReference> reference : pendingInjections) {
try {
reference.get();
} catch (InternalProvisionException ipe) {
errors.merge(ipe);
}
}
pendingInjections.clear();
}
private enum InjectableReferenceState {
NEW,
VALIDATED,
INJECTING,
READY
}
private static class InjectableReference implements Initializable {
private volatile InjectableReferenceState state = InjectableReferenceState.NEW;
private volatile MembersInjectorImpl membersInjector = null;
private final InjectorImpl injector;
private final T instance;
private final Object source;
private final Key key;
private final ProvisionListenerStackCallback provisionCallback;
private final CycleDetectingLock> lock;
public InjectableReference(InjectorImpl injector, T instance, Key key,
ProvisionListenerStackCallback provisionCallback,
Object source,
CycleDetectingLock> lock) {
this.injector = injector;
this.key = key; // possibly null!
this.provisionCallback = provisionCallback; // possibly null!
this.instance = checkNotNull(instance, "instance");
this.source = checkNotNull(source, "source");
this.lock = checkNotNull(lock, "lock");
}
public void validate(Errors errors) throws ErrorsException {
@SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral
TypeLiteral type = TypeLiteral.get((Class) instance.getClass());
membersInjector = injector.membersInjectorStore.get(type, errors.withSource(source));
Preconditions.checkNotNull(
membersInjector,
"No membersInjector available for instance: %s, from key: %s",
instance,
key);
state = InjectableReferenceState.VALIDATED;
}
/**
* Reentrant. If {@code instance} was registered for injection at injector-creation time, this
* method will ensure that all its members have been injected before returning.
*/
@Override
public T get() throws InternalProvisionException {
// skipping acquiring lock if initialization is already finished
if (state == InjectableReferenceState.READY) {
return instance;
}
// acquire lock for current binding to initialize an instance
Multimap, ?> lockCycle = lock.lockOrDetectPotentialLocksCycle();
if (!lockCycle.isEmpty()) {
// Potential deadlock detected and creation lock is not taken.
// According to injectAll()'s contract return non-initialized instance.
// This condition should not be possible under the current Guice implementation.
// This clause exists for defensive programming purposes.
// Reasoning:
// get() is called either directly from injectAll(), holds no locks and can not create
// a cycle, or it is called through a singleton scope, which resolves deadlocks by itself.
// Before calling get() object has to be requested for injection.
// Initializer.requestInjection() is called either for constant object bindings, which wrap
// creation into a Singleton scope, or from Binder.requestInjection(), which
// has to use Singleton scope to reuse the same InjectableReference to potentially
// create a lock cycle.
return instance;
}
try {
// lock acquired, current thread owns this instance initialization
switch (state) {
case READY:
return instance;
// When instance depends on itself in the same thread potential dead lock
// is not detected. We have to prevent a stack overflow and we use
// an "injecting" stage to short-circuit a call.
case INJECTING:
return instance;
case VALIDATED:
state = InjectableReferenceState.INJECTING;
break;
case NEW:
throw new IllegalStateException("InjectableReference is not validated yet");
default:
throw new IllegalStateException("Unknown state: " + state);
}
// if in Stage.TOOL, we only want to inject & notify toolable injection points.
// (otherwise we'll inject all of them)
try {
membersInjector.injectAndNotify(
instance, key, provisionCallback, source, injector.options.stage == Stage.TOOL);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(source);
}
// mark instance as ready to skip a lock on subsequent calls
state = InjectableReferenceState.READY;
return instance;
} finally {
// always release our creation lock, even on failures
lock.unlock();
}
}
@Override
public String toString() {
return instance.toString();
}
}
}