1 /** 2 * Copyright (C) 2006 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 com.google.common.collect.ImmutableList; 20 import com.google.common.collect.Iterables; 21 import com.google.inject.Binding; 22 import com.google.inject.Injector; 23 import com.google.inject.Key; 24 import com.google.inject.MembersInjector; 25 import com.google.inject.Module; 26 import com.google.inject.Provider; 27 import com.google.inject.Scope; 28 import com.google.inject.Stage; 29 import com.google.inject.TypeLiteral; 30 import com.google.inject.internal.util.Stopwatch; 31 import com.google.inject.spi.Dependency; 32 import com.google.inject.spi.TypeConverterBinding; 33 34 import java.lang.annotation.Annotation; 35 import java.util.Collection; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Set; 39 40 /** 41 * Builds a tree of injectors. This is a primary injector, plus child injectors needed for each 42 * {@code Binder.newPrivateBinder() private environment}. The primary injector is not necessarily a 43 * top-level injector. 44 * 45 * <p>Injector construction happens in two phases. 46 * <ol> 47 * <li>Static building. In this phase, we interpret commands, create bindings, and inspect 48 * dependencies. During this phase, we hold a lock to ensure consistency with parent injectors. 49 * No user code is executed in this phase.</li> 50 * <li>Dynamic injection. In this phase, we call user code. We inject members that requested 51 * injection. This may require user's objects be created and their providers be called. And we 52 * create eager singletons. In this phase, user code may have started other threads. This phase 53 * is not executed for injectors created using {@link Stage#TOOL the tool stage}</li> 54 * </ol> 55 * 56 * @author crazybob (at) google.com (Bob Lee) 57 * @author jessewilson (at) google.com (Jesse Wilson) 58 */ 59 public final class InternalInjectorCreator { 60 61 private final Stopwatch stopwatch = new Stopwatch(); 62 private final Errors errors = new Errors(); 63 64 private final Initializer initializer = new Initializer(); 65 private final ProcessedBindingData bindingData; 66 private final InjectionRequestProcessor injectionRequestProcessor; 67 68 private final InjectorShell.Builder shellBuilder = new InjectorShell.Builder(); 69 private List<InjectorShell> shells; 70 71 public InternalInjectorCreator() { 72 injectionRequestProcessor = new InjectionRequestProcessor(errors, initializer); 73 bindingData = new ProcessedBindingData(); 74 } 75 76 public InternalInjectorCreator stage(Stage stage) { 77 shellBuilder.stage(stage); 78 return this; 79 } 80 81 /** 82 * Sets the parent of the injector to-be-constructed. As a side effect, this sets this injector's 83 * stage to the stage of {@code parent} and sets {@link #requireExplicitBindings()} if the parent 84 * injector also required them. 85 */ 86 public InternalInjectorCreator parentInjector(InjectorImpl parent) { 87 shellBuilder.parent(parent); 88 return this; 89 } 90 91 public InternalInjectorCreator addModules(Iterable<? extends Module> modules) { 92 shellBuilder.addModules(modules); 93 return this; 94 } 95 96 public Injector build() { 97 if (shellBuilder == null) { 98 throw new AssertionError("Already built, builders are not reusable."); 99 } 100 101 // Synchronize while we're building up the bindings and other injector state. This ensures that 102 // the JIT bindings in the parent injector don't change while we're being built 103 synchronized (shellBuilder.lock()) { 104 shells = shellBuilder.build(initializer, bindingData, stopwatch, errors); 105 stopwatch.resetAndLog("Injector construction"); 106 107 initializeStatically(); 108 } 109 110 injectDynamically(); 111 112 if (shellBuilder.getStage() == Stage.TOOL) { 113 // wrap the primaryInjector in a ToolStageInjector 114 // to prevent non-tool-friendy methods from being called. 115 return new ToolStageInjector(primaryInjector()); 116 } else { 117 return primaryInjector(); 118 } 119 } 120 121 /** Initialize and validate everything. */ 122 private void initializeStatically() { 123 bindingData.initializeBindings(); 124 stopwatch.resetAndLog("Binding initialization"); 125 126 for (InjectorShell shell : shells) { 127 shell.getInjector().index(); 128 } 129 stopwatch.resetAndLog("Binding indexing"); 130 131 injectionRequestProcessor.process(shells); 132 stopwatch.resetAndLog("Collecting injection requests"); 133 134 bindingData.runCreationListeners(errors); 135 stopwatch.resetAndLog("Binding validation"); 136 137 injectionRequestProcessor.validate(); 138 stopwatch.resetAndLog("Static validation"); 139 140 initializer.validateOustandingInjections(errors); 141 stopwatch.resetAndLog("Instance member validation"); 142 143 new LookupProcessor(errors).process(shells); 144 for (InjectorShell shell : shells) { 145 ((DeferredLookups) shell.getInjector().lookups).initialize(errors); 146 } 147 stopwatch.resetAndLog("Provider verification"); 148 149 for (InjectorShell shell : shells) { 150 if (!shell.getElements().isEmpty()) { 151 throw new AssertionError("Failed to execute " + shell.getElements()); 152 } 153 } 154 155 errors.throwCreationExceptionIfErrorsExist(); 156 } 157 158 /** 159 * Returns the injector being constructed. This is not necessarily the root injector. 160 */ 161 private Injector primaryInjector() { 162 return shells.get(0).getInjector(); 163 } 164 165 /** 166 * Inject everything that can be injected. This method is intentionally not synchronized. If we 167 * locked while injecting members (ie. running user code), things would deadlock should the user 168 * code build a just-in-time binding from another thread. 169 */ 170 private void injectDynamically() { 171 injectionRequestProcessor.injectMembers(); 172 stopwatch.resetAndLog("Static member injection"); 173 174 initializer.injectAll(errors); 175 stopwatch.resetAndLog("Instance injection"); 176 errors.throwCreationExceptionIfErrorsExist(); 177 178 if(shellBuilder.getStage() != Stage.TOOL) { 179 for (InjectorShell shell : shells) { 180 loadEagerSingletons(shell.getInjector(), shellBuilder.getStage(), errors); 181 } 182 stopwatch.resetAndLog("Preloading singletons"); 183 } 184 errors.throwCreationExceptionIfErrorsExist(); 185 } 186 187 /** 188 * Loads eager singletons, or all singletons if we're in Stage.PRODUCTION. Bindings discovered 189 * while we're binding these singletons are not be eager. 190 */ 191 void loadEagerSingletons(InjectorImpl injector, Stage stage, final Errors errors) { 192 @SuppressWarnings("unchecked") // casting Collection<Binding> to Collection<BindingImpl> is safe 193 Iterable<BindingImpl<?>> candidateBindings = ImmutableList.copyOf(Iterables.concat( 194 (Collection) injector.state.getExplicitBindingsThisLevel().values(), 195 injector.jitBindings.values())); 196 for (final BindingImpl<?> binding : candidateBindings) { 197 if (isEagerSingleton(injector, binding, stage)) { 198 try { 199 injector.callInContext(new ContextualCallable<Void>() { 200 Dependency<?> dependency = Dependency.get(binding.getKey()); 201 public Void call(InternalContext context) { 202 Dependency previous = context.pushDependency(dependency, binding.getSource()); 203 Errors errorsForBinding = errors.withSource(dependency); 204 try { 205 binding.getInternalFactory().get(errorsForBinding, context, dependency, false); 206 } catch (ErrorsException e) { 207 errorsForBinding.merge(e.getErrors()); 208 } finally { 209 context.popStateAndSetDependency(previous); 210 } 211 212 return null; 213 } 214 }); 215 } catch (ErrorsException e) { 216 throw new AssertionError(); 217 } 218 } 219 } 220 } 221 222 private boolean isEagerSingleton(InjectorImpl injector, BindingImpl<?> binding, Stage stage) { 223 if (binding.getScoping().isEagerSingleton(stage)) { 224 return true; 225 } 226 227 // handle a corner case where a child injector links to a binding in a parent injector, and 228 // that binding is singleton. We won't catch this otherwise because we only iterate the child's 229 // bindings. 230 if (binding instanceof LinkedBindingImpl) { 231 Key<?> linkedBinding = ((LinkedBindingImpl<?>) binding).getLinkedKey(); 232 return isEagerSingleton(injector, injector.getBinding(linkedBinding), stage); 233 } 234 235 return false; 236 } 237 238 /** {@link Injector} exposed to users in {@link Stage#TOOL}. */ 239 static class ToolStageInjector implements Injector { 240 private final Injector delegateInjector; 241 242 ToolStageInjector(Injector delegateInjector) { 243 this.delegateInjector = delegateInjector; 244 } 245 public void injectMembers(Object o) { 246 throw new UnsupportedOperationException( 247 "Injector.injectMembers(Object) is not supported in Stage.TOOL"); 248 } 249 public Map<Key<?>, Binding<?>> getBindings() { 250 return this.delegateInjector.getBindings(); 251 } 252 public Map<Key<?>, Binding<?>> getAllBindings() { 253 return this.delegateInjector.getAllBindings(); 254 } 255 public <T> Binding<T> getBinding(Key<T> key) { 256 return this.delegateInjector.getBinding(key); 257 } 258 public <T> Binding<T> getBinding(Class<T> type) { 259 return this.delegateInjector.getBinding(type); 260 } 261 public <T> Binding<T> getExistingBinding(Key<T> key) { 262 return this.delegateInjector.getExistingBinding(key); 263 } 264 public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) { 265 return this.delegateInjector.findBindingsByType(type); 266 } 267 public Injector getParent() { 268 return delegateInjector.getParent(); 269 } 270 public Injector createChildInjector(Iterable<? extends Module> modules) { 271 return delegateInjector.createChildInjector(modules); 272 } 273 public Injector createChildInjector(Module... modules) { 274 return delegateInjector.createChildInjector(modules); 275 } 276 public Map<Class<? extends Annotation>, Scope> getScopeBindings() { 277 return delegateInjector.getScopeBindings(); 278 } 279 public Set<TypeConverterBinding> getTypeConverterBindings() { 280 return delegateInjector.getTypeConverterBindings(); 281 } 282 public <T> Provider<T> getProvider(Key<T> key) { 283 throw new UnsupportedOperationException( 284 "Injector.getProvider(Key<T>) is not supported in Stage.TOOL"); 285 } 286 public <T> Provider<T> getProvider(Class<T> type) { 287 throw new UnsupportedOperationException( 288 "Injector.getProvider(Class<T>) is not supported in Stage.TOOL"); 289 } 290 public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) { 291 throw new UnsupportedOperationException( 292 "Injector.getMembersInjector(TypeLiteral<T>) is not supported in Stage.TOOL"); 293 } 294 public <T> MembersInjector<T> getMembersInjector(Class<T> type) { 295 throw new UnsupportedOperationException( 296 "Injector.getMembersInjector(Class<T>) is not supported in Stage.TOOL"); 297 } 298 public <T> T getInstance(Key<T> key) { 299 throw new UnsupportedOperationException( 300 "Injector.getInstance(Key<T>) is not supported in Stage.TOOL"); 301 } 302 public <T> T getInstance(Class<T> type) { 303 throw new UnsupportedOperationException( 304 "Injector.getInstance(Class<T>) is not supported in Stage.TOOL"); 305 } 306 } 307 } 308