| /** |
| * Copyright (C) 2008 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.inject.internal; |
| |
| import com.google.inject.TypeLiteral; |
| import static com.google.inject.internal.Preconditions.checkNotNull; |
| import com.google.inject.spi.InjectionPoint; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.CountDownLatch; |
| |
| /** |
| * 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. |
| * |
| * @author [email protected] (Jesse Wilson) |
| */ |
| final class Initializer { |
| /** the only thread that we'll use to inject members. */ |
| private final Thread creatingThread = Thread.currentThread(); |
| |
| /** zero means everything is injected. */ |
| private final CountDownLatch ready = new CountDownLatch(1); |
| |
| /** Maps instances that need injection to a source that registered them */ |
| private final Map<Object, InjectableReference<?>> pendingInjection = 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 |
| * @Inject). |
| * @param source the source location that this injection was requested |
| */ |
| <T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Object source, |
| Set<InjectionPoint> injectionPoints) { |
| checkNotNull(source); |
| |
| // short circuit if the object has no injections |
| if (instance == null |
| || (injectionPoints.isEmpty() && !injector.membersInjectorStore.hasTypeListeners())) { |
| return Initializables.of(instance); |
| } |
| |
| InjectableReference<T> initializable = new InjectableReference<T>(injector, instance, source); |
| pendingInjection.put(instance, initializable); |
| return initializable; |
| } |
| |
| /** |
| * Prepares member injectors for all injected instances. This prompts Guice to do static analysis |
| * on the injected instances. |
| */ |
| void validateOustandingInjections(Errors errors) { |
| for (InjectableReference<?> reference : pendingInjection.values()) { |
| 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) { |
| // loop over a defensive copy since ensureInjected() mutates the set. Unfortunately, that copy |
| // is made complicated by a bug in IBM's JDK, wherein entrySet().toArray(Object[]) doesn't work |
| for (InjectableReference<?> reference : Lists.newArrayList(pendingInjection.values())) { |
| try { |
| reference.get(errors); |
| } catch (ErrorsException e) { |
| errors.merge(e.getErrors()); |
| } |
| } |
| |
| if (!pendingInjection.isEmpty()) { |
| throw new AssertionError("Failed to satisfy " + pendingInjection); |
| } |
| |
| ready.countDown(); |
| } |
| |
| private class InjectableReference<T> implements Initializable<T> { |
| private final InjectorImpl injector; |
| private final T instance; |
| private final Object source; |
| private MembersInjectorImpl<T> membersInjector; |
| |
| public InjectableReference(InjectorImpl injector, T instance, Object source) { |
| this.injector = injector; |
| this.instance = checkNotNull(instance, "instance"); |
| this.source = checkNotNull(source, "source"); |
| } |
| |
| public void validate(Errors errors) throws ErrorsException { |
| @SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral<T> |
| TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass()); |
| membersInjector = injector.membersInjectorStore.get(type, errors.withSource(source)); |
| } |
| |
| /** |
| * 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. |
| */ |
| public T get(Errors errors) throws ErrorsException { |
| if (ready.getCount() == 0) { |
| return instance; |
| } |
| |
| // just wait for everything to be injected by another thread |
| if (Thread.currentThread() != creatingThread) { |
| try { |
| ready.await(); |
| return instance; |
| } catch (InterruptedException e) { |
| // Give up, since we don't know if our injection is ready |
| throw new RuntimeException(e); |
| } |
| } |
| |
| // toInject needs injection, do it right away. we only do this once, even if it fails |
| if (pendingInjection.remove(instance) != null) { |
| membersInjector.injectAndNotify(instance, errors.withSource(source)); |
| } |
| |
| return instance; |
| } |
| |
| @Override public String toString() { |
| return instance.toString(); |
| } |
| } |
| } |