Home | History | Annotate | Download | only in internal
      1 /**
      2  * Copyright (C) 2008 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.inject.internal;
     18 
     19 import static com.google.common.base.Preconditions.checkNotNull;
     20 
     21 import com.google.common.base.Preconditions;
     22 import com.google.common.collect.Lists;
     23 import com.google.common.collect.Maps;
     24 import com.google.inject.Binding;
     25 import com.google.inject.Key;
     26 import com.google.inject.Stage;
     27 import com.google.inject.TypeLiteral;
     28 import com.google.inject.spi.InjectionPoint;
     29 
     30 import java.util.Map;
     31 import java.util.Set;
     32 import java.util.concurrent.CountDownLatch;
     33 
     34 /**
     35  * Manages and injects instances at injector-creation time. This is made more complicated by
     36  * instances that request other instances while they're being injected. We overcome this by using
     37  * {@link Initializable}, which attempts to perform injection before use.
     38  *
     39  * @author jessewilson (at) google.com (Jesse Wilson)
     40  */
     41 final class Initializer {
     42 
     43   /** the only thread that we'll use to inject members. */
     44   private final Thread creatingThread = Thread.currentThread();
     45 
     46   /** zero means everything is injected. */
     47   private final CountDownLatch ready = new CountDownLatch(1);
     48 
     49   /** Maps from instances that need injection to the MembersInjector that will inject them. */
     50   private final Map<Object, MembersInjectorImpl<?>> pendingMembersInjectors =
     51       Maps.newIdentityHashMap();
     52 
     53   /** Maps instances that need injection to a source that registered them */
     54   private final Map<Object, InjectableReference<?>> pendingInjection = Maps.newIdentityHashMap();
     55 
     56   /**
     57    * Registers an instance for member injection when that step is performed.
     58    *
     59    * @param instance an instance that optionally has members to be injected (each annotated with
     60    *      @Inject).
     61    * @param binding the binding that caused this initializable to be created, if it exists.
     62    * @param source the source location that this injection was requested
     63    */
     64   <T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Binding<T> binding,
     65       Object source, Set<InjectionPoint> injectionPoints) {
     66     checkNotNull(source);
     67 
     68     ProvisionListenerStackCallback<T> provisionCallback =
     69         binding == null ? null : injector.provisionListenerStore.get(binding);
     70 
     71     // short circuit if the object has no injections or listeners.
     72     if (instance == null || (injectionPoints.isEmpty()
     73         && !injector.membersInjectorStore.hasTypeListeners()
     74         && (provisionCallback == null || !provisionCallback.hasListeners()))) {
     75       return Initializables.of(instance);
     76     }
     77 
     78     InjectableReference<T> initializable = new InjectableReference<T>(
     79         injector, instance, binding == null ? null : binding.getKey(), provisionCallback, source);
     80     pendingInjection.put(instance, initializable);
     81     return initializable;
     82   }
     83 
     84   /**
     85    * Prepares member injectors for all injected instances. This prompts Guice to do static analysis
     86    * on the injected instances.
     87    */
     88   void validateOustandingInjections(Errors errors) {
     89     for (InjectableReference<?> reference : pendingInjection.values()) {
     90       try {
     91         pendingMembersInjectors.put(reference.instance, reference.validate(errors));
     92       } catch (ErrorsException e) {
     93         errors.merge(e.getErrors());
     94       }
     95     }
     96   }
     97 
     98   /**
     99    * Performs creation-time injections on all objects that require it. Whenever fulfilling an
    100    * injection depends on another object that requires injection, we inject it first. If the two
    101    * instances are codependent (directly or transitively), ordering of injection is arbitrary.
    102    */
    103   void injectAll(final Errors errors) {
    104     // loop over a defensive copy since ensureInjected() mutates the set. Unfortunately, that copy
    105     // is made complicated by a bug in IBM's JDK, wherein entrySet().toArray(Object[]) doesn't work
    106     for (InjectableReference<?> reference : Lists.newArrayList(pendingInjection.values())) {
    107       try {
    108         reference.get(errors);
    109       } catch (ErrorsException e) {
    110         errors.merge(e.getErrors());
    111       }
    112     }
    113 
    114     if (!pendingInjection.isEmpty()) {
    115       throw new AssertionError("Failed to satisfy " + pendingInjection);
    116     }
    117 
    118     ready.countDown();
    119   }
    120 
    121   private class InjectableReference<T> implements Initializable<T> {
    122     private final InjectorImpl injector;
    123     private final T instance;
    124     private final Object source;
    125     private final Key<T> key;
    126     private final ProvisionListenerStackCallback<T> provisionCallback;
    127 
    128     public InjectableReference(InjectorImpl injector, T instance, Key<T> key,
    129         ProvisionListenerStackCallback<T> provisionCallback, Object source) {
    130       this.injector = injector;
    131       this.key = key; // possibly null!
    132       this.provisionCallback = provisionCallback; // possibly null!
    133       this.instance = checkNotNull(instance, "instance");
    134       this.source = checkNotNull(source, "source");
    135     }
    136 
    137     public MembersInjectorImpl<T> validate(Errors errors) throws ErrorsException {
    138       @SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral<T>
    139           TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
    140       return injector.membersInjectorStore.get(type, errors.withSource(source));
    141     }
    142 
    143     /**
    144      * Reentrant. If {@code instance} was registered for injection at injector-creation time, this
    145      * method will ensure that all its members have been injected before returning.
    146      */
    147     public T get(Errors errors) throws ErrorsException {
    148       if (ready.getCount() == 0) {
    149         return instance;
    150       }
    151 
    152       // just wait for everything to be injected by another thread
    153       if (Thread.currentThread() != creatingThread) {
    154         try {
    155           ready.await();
    156           return instance;
    157         } catch (InterruptedException e) {
    158           // Give up, since we don't know if our injection is ready
    159           throw new RuntimeException(e);
    160         }
    161       }
    162 
    163       // toInject needs injection, do it right away. we only do this once, even if it fails
    164       if (pendingInjection.remove(instance) != null) {
    165         // safe because we only insert a members injector for the appropriate instance
    166         @SuppressWarnings("unchecked")
    167         MembersInjectorImpl<T> membersInjector =
    168             (MembersInjectorImpl<T>)pendingMembersInjectors.remove(instance);
    169         Preconditions.checkState(membersInjector != null,
    170             "No membersInjector available for instance: %s, from key: %s", instance, key);
    171 
    172         // if in Stage.TOOL, we only want to inject & notify toolable injection points.
    173         // (otherwise we'll inject all of them)
    174         membersInjector.injectAndNotify(instance,
    175             errors.withSource(source),
    176             key,
    177             provisionCallback,
    178             source,
    179             injector.options.stage == Stage.TOOL);
    180       }
    181 
    182       return instance;
    183     }
    184 
    185     @Override public String toString() {
    186       return instance.toString();
    187     }
    188   }
    189 }
    190