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