blob: 7c67b2f864d91c4625595410228eb2b958c6e950 [file] [log] [blame]
limpbizkit3d58d6b2008-03-08 16:11:47 +00001/**
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
17package com.google.inject;
18
kevinb9n1601ae52008-06-03 22:21:04 +000019import static com.google.common.base.Preconditions.checkNotNull;
limpbizkitb3a8f0b2008-09-05 22:30:40 +000020import com.google.common.collect.ImmutableSet;
limpbizkit9dc32d42008-06-15 11:29:10 +000021import com.google.common.collect.Iterables;
22import com.google.common.collect.Lists;
23import com.google.inject.Reflection.Factory;
limpbizkit3d58d6b2008-03-08 16:11:47 +000024import static com.google.inject.Scopes.SINGLETON;
limpbizkit9dc32d42008-06-15 11:29:10 +000025import com.google.inject.internal.Errors;
limpbizkit163c48a2008-06-16 02:58:08 +000026import com.google.inject.internal.ErrorsException;
limpbizkit6663d022008-06-19 07:57:55 +000027import com.google.inject.internal.SourceProvider;
limpbizkit2b931682008-04-21 00:59:34 +000028import com.google.inject.internal.Stopwatch;
limpbizkita98bc7a2008-08-29 16:52:44 +000029import com.google.inject.spi.Dependency;
limpbizkit477f9f92008-07-28 07:05:14 +000030import com.google.inject.spi.Element;
31import com.google.inject.spi.Elements;
limpbizkit163c48a2008-06-16 02:58:08 +000032import com.google.inject.spi.InjectionPoint;
limpbizkit5fb9d922008-10-14 23:35:56 +000033import java.util.Collection;
limpbizkitf44e9cc2008-03-26 06:33:29 +000034import java.util.List;
limpbizkit8eef9982008-08-03 00:45:52 +000035import java.util.Map;
limpbizkit51273652008-09-29 18:21:20 +000036import java.util.Set;
limpbizkit3d58d6b2008-03-08 16:11:47 +000037import java.util.logging.Logger;
38
39/**
40 * Builds a dependency injection {@link Injector}.
41 *
42 * @author crazybob@google.com (Bob Lee)
43 * @author jessewilson@google.com (Jesse Wilson)
44 */
45class InjectorBuilder {
46
47 private final Stopwatch stopwatch = new Stopwatch();
48
limpbizkit5fb9d922008-10-14 23:35:56 +000049 private InjectorImpl parent = null;
limpbizkit3d58d6b2008-03-08 16:11:47 +000050 private Stage stage;
limpbizkit9dc32d42008-06-15 11:29:10 +000051 private Factory reflectionFactory = new RuntimeReflectionFactory();
52 private final List<Module> modules = Lists.newLinkedList();
limpbizkit3d58d6b2008-03-08 16:11:47 +000053
limpbizkit3d58d6b2008-03-08 16:11:47 +000054 private InjectorImpl injector;
limpbizkit9dc32d42008-06-15 11:29:10 +000055 private Errors errors = new Errors();
limpbizkit3d58d6b2008-03-08 16:11:47 +000056
limpbizkit477f9f92008-07-28 07:05:14 +000057 private final List<Element> elements = Lists.newArrayList();
limpbizkit3d58d6b2008-03-08 16:11:47 +000058
limpbizkit00ca9f72008-08-02 17:56:17 +000059 private BindingProcessor bindCommandProcesor;
60 private InjectionRequestProcessor injectionCommandProcessor;
limpbizkit3d58d6b2008-03-08 16:11:47 +000061
62 /**
limpbizkit9dc32d42008-06-15 11:29:10 +000063 * @param stage we're running in. If the stage is {@link Stage#PRODUCTION}, we will eagerly load
64 * singletons.
limpbizkit3d58d6b2008-03-08 16:11:47 +000065 */
66 InjectorBuilder stage(Stage stage) {
67 this.stage = stage;
68 return this;
69 }
70
limpbizkit9dc32d42008-06-15 11:29:10 +000071 InjectorBuilder usingReflectionFactory(Factory reflectionFactory) {
limpbizkit916f5482008-04-16 20:51:14 +000072 this.reflectionFactory = reflectionFactory;
73 return this;
74 }
75
limpbizkit5fb9d922008-10-14 23:35:56 +000076 InjectorBuilder parentInjector(InjectorImpl parent) {
limpbizkit3d58d6b2008-03-08 16:11:47 +000077 this.parent = parent;
78 return this;
79 }
80
81 InjectorBuilder addModules(Iterable<? extends Module> modules) {
82 for (Module module : modules) {
83 this.modules.add(module);
84 }
85 return this;
86 }
87
88 Injector build() {
89 if (injector != null) {
90 throw new AssertionError("Already built, builders are not reusable.");
91 }
92
limpbizkit9dc32d42008-06-15 11:29:10 +000093 injector = new InjectorImpl(parent);
limpbizkit7f8eda02008-03-26 01:11:16 +000094
limpbizkit5fb9d922008-10-14 23:35:56 +000095 // bind Stage and Singleton if this is a top-level injector
96 if (parent == null) {
97 modules.add(0, new RootModule(stage));
98 }
limpbizkit3d58d6b2008-03-08 16:11:47 +000099
limpbizkit477f9f92008-07-28 07:05:14 +0000100 elements.addAll(Elements.getElements(stage, modules));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000101
limpbizkit3d58d6b2008-03-08 16:11:47 +0000102 buildCoreInjector();
103
104 validate();
105
limpbizkit9dc32d42008-06-15 11:29:10 +0000106 errors.throwCreationExceptionIfErrorsExist();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000107
108 // If we're in the tool stage, stop here. Don't eagerly inject or load
109 // anything.
110 if (stage == Stage.TOOL) {
limpbizkit8eef9982008-08-03 00:45:52 +0000111 return new ToolStageInjector(injector);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000112 }
113
114 fulfillInjectionRequests();
115
limpbizkit477f9f92008-07-28 07:05:14 +0000116 if (!elements.isEmpty()) {
117 throw new AssertionError("Failed to execute " + elements);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000118 }
119
120 return injector;
121 }
122
limpbizkit9dc32d42008-06-15 11:29:10 +0000123 /** Builds the injector. */
limpbizkit3d58d6b2008-03-08 16:11:47 +0000124 private void buildCoreInjector() {
limpbizkit00ca9f72008-08-02 17:56:17 +0000125 new MessageProcessor(errors)
limpbizkit477f9f92008-07-28 07:05:14 +0000126 .processCommands(elements);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000127
limpbizkit00ca9f72008-08-02 17:56:17 +0000128 InterceptorBindingProcessor interceptorCommandProcessor
limpbizkit5fb9d922008-10-14 23:35:56 +0000129 = new InterceptorBindingProcessor(errors, injector.state);
limpbizkit00ca9f72008-08-02 17:56:17 +0000130 interceptorCommandProcessor.processCommands(elements);
131 ConstructionProxyFactory proxyFactory = interceptorCommandProcessor.createProxyFactory();
limpbizkit9dc32d42008-06-15 11:29:10 +0000132 injector.reflection = reflectionFactory.create(proxyFactory);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000133 stopwatch.resetAndLog("Interceptors creation");
134
limpbizkit5fb9d922008-10-14 23:35:56 +0000135 new ScopeBindingProcessor(errors, injector.state).processCommands(elements);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000136 stopwatch.resetAndLog("Scopes creation");
137
limpbizkit5fb9d922008-10-14 23:35:56 +0000138 new TypeConverterBindingProcessor(errors, injector.state).processCommands(elements);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000139 stopwatch.resetAndLog("Converters creation");
140
limpbizkit5fb9d922008-10-14 23:35:56 +0000141 bindInjector();
limpbizkit150d6772008-04-25 17:34:25 +0000142 bindLogger();
limpbizkit00ca9f72008-08-02 17:56:17 +0000143 bindCommandProcesor = new BindingProcessor(errors,
limpbizkit5fb9d922008-10-14 23:35:56 +0000144 injector, injector.state, injector.initializer);
limpbizkit477f9f92008-07-28 07:05:14 +0000145 bindCommandProcesor.processCommands(elements);
limpbizkitf44e9cc2008-03-26 06:33:29 +0000146 bindCommandProcesor.createUntargettedBindings();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000147 stopwatch.resetAndLog("Binding creation");
148
149 injector.index();
150 stopwatch.resetAndLog("Binding indexing");
151
limpbizkit5fb9d922008-10-14 23:35:56 +0000152 injectionCommandProcessor = new InjectionRequestProcessor(errors, injector.initializer);
limpbizkit00ca9f72008-08-02 17:56:17 +0000153 injectionCommandProcessor.processCommands(elements);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000154 stopwatch.resetAndLog("Static injection");
155 }
156
limpbizkit9dc32d42008-06-15 11:29:10 +0000157 /** Validate everything that we can validate now that the injector is ready for use. */
limpbizkit3d58d6b2008-03-08 16:11:47 +0000158 private void validate() {
159 bindCommandProcesor.runCreationListeners(injector);
160 stopwatch.resetAndLog("Validation");
161
limpbizkit00ca9f72008-08-02 17:56:17 +0000162 injectionCommandProcessor.validate(injector);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000163 stopwatch.resetAndLog("Static validation");
164
limpbizkit5fb9d922008-10-14 23:35:56 +0000165 injector.initializer.validateOustandingInjections(errors);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000166 stopwatch.resetAndLog("Instance member validation");
167
limpbizkit00ca9f72008-08-02 17:56:17 +0000168 new ProviderLookupProcessor(errors, injector).processCommands(elements);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000169 stopwatch.resetAndLog("Provider verification");
170
limpbizkit9dc32d42008-06-15 11:29:10 +0000171 errors.throwCreationExceptionIfErrorsExist();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000172 }
173
limpbizkit9532e622008-06-18 08:20:54 +0000174 /** Inject everything that can be injected. */
limpbizkit3d58d6b2008-03-08 16:11:47 +0000175 private void fulfillInjectionRequests() {
limpbizkit00ca9f72008-08-02 17:56:17 +0000176 injectionCommandProcessor.injectMembers(injector);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000177 stopwatch.resetAndLog("Static member injection");
limpbizkit9532e622008-06-18 08:20:54 +0000178
limpbizkit5fb9d922008-10-14 23:35:56 +0000179 injector.initializer.injectAll(errors);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000180 stopwatch.resetAndLog("Instance injection");
limpbizkit9532e622008-06-18 08:20:54 +0000181 errors.throwCreationExceptionIfErrorsExist();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000182
limpbizkitecbb0802008-06-02 07:02:11 +0000183 loadEagerSingletons();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000184 stopwatch.resetAndLog("Preloading");
limpbizkit9532e622008-06-18 08:20:54 +0000185 errors.throwCreationExceptionIfErrorsExist();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000186 }
187
limpbizkitecbb0802008-06-02 07:02:11 +0000188 public void loadEagerSingletons() {
189 // load eager singletons, or all singletons if we're in Stage.PRODUCTION.
limpbizkit51273652008-09-29 18:21:20 +0000190 // Bindings discovered while we're binding these singletons are not be eager.
limpbizkit5fb9d922008-10-14 23:35:56 +0000191 @SuppressWarnings("unchecked") // casting Collection<Binding> to Collection<BindingImpl> is safe
192 Set<BindingImpl<?>> candidateBindings = ImmutableSet.copyOf(Iterables.concat(
193 (Collection) injector.state.getExplicitBindingsThisLevel().values(),
194 injector.jitBindings.values()));
limpbizkit51273652008-09-29 18:21:20 +0000195 for (final BindingImpl<?> binding : candidateBindings) {
limpbizkit5789ef42008-08-04 06:44:30 +0000196 if ((stage == Stage.PRODUCTION && binding.getScope() == SINGLETON)
limpbizkite81cf1b2008-06-02 16:41:22 +0000197 || binding.getLoadStrategy() == LoadStrategy.EAGER) {
limpbizkit9dc32d42008-06-15 11:29:10 +0000198 try {
199 injector.callInContext(new ContextualCallable<Void>() {
200 public Void call(InternalContext context) {
limpbizkita98bc7a2008-08-29 16:52:44 +0000201 Dependency<?> dependency = Dependency.get(binding.key);
202 context.setDependency(dependency);
limpbizkita6e0e782008-09-03 06:19:56 +0000203 errors.pushSource(dependency);
limpbizkit9dc32d42008-06-15 11:29:10 +0000204 try {
limpbizkita98bc7a2008-08-29 16:52:44 +0000205 binding.internalFactory.get(errors, context, dependency);
limpbizkit163c48a2008-06-16 02:58:08 +0000206 } catch (ErrorsException e) {
limpbizkit9dc32d42008-06-15 11:29:10 +0000207 errors.merge(e.getErrors());
208 } finally {
limpbizkita98bc7a2008-08-29 16:52:44 +0000209 context.setDependency(null);
limpbizkita6e0e782008-09-03 06:19:56 +0000210 errors.popSource(dependency);
limpbizkit9dc32d42008-06-15 11:29:10 +0000211 }
212
limpbizkitecbb0802008-06-02 07:02:11 +0000213 return null;
limpbizkitecbb0802008-06-02 07:02:11 +0000214 }
limpbizkit9dc32d42008-06-15 11:29:10 +0000215 });
limpbizkit163c48a2008-06-16 02:58:08 +0000216 } catch (ErrorsException e) {
limpbizkit9dc32d42008-06-15 11:29:10 +0000217 throw new AssertionError();
218 }
limpbizkitecbb0802008-06-02 07:02:11 +0000219 }
220 }
221 }
222
limpbizkit5fb9d922008-10-14 23:35:56 +0000223 private static class RootModule implements Module {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000224 final Stage stage;
225
limpbizkit5fb9d922008-10-14 23:35:56 +0000226 private RootModule(Stage stage) {
kevinb9n1601ae52008-06-03 22:21:04 +0000227 this.stage = checkNotNull(stage, "stage");
limpbizkit3d58d6b2008-03-08 16:11:47 +0000228 }
229
limpbizkit7d9991e2008-06-17 02:49:18 +0000230 public void configure(Binder binder) {
limpbizkit9532e622008-06-18 08:20:54 +0000231 binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
limpbizkit7d9991e2008-06-17 02:49:18 +0000232 binder.bind(Stage.class).toInstance(stage);
233 binder.bindScope(Singleton.class, SINGLETON);
limpbizkit5fb9d922008-10-14 23:35:56 +0000234 }
235 }
236
237 /**
238 * The Injector is a special case because we allow both parent and child injectors to both have
239 * a binding for that key.
240 */
241 private void bindInjector() {
242 Key<Injector> key = Key.get(Injector.class);
243 InjectorFactory injectorFactory = new InjectorFactory(injector);
244 injector.state.putBinding(key,
245 new ProviderInstanceBindingImpl<Injector>(injector, key, SourceProvider.UNKNOWN_SOURCE,
246 injectorFactory, Scopes.NO_SCOPE, injectorFactory, LoadStrategy.LAZY,
247 ImmutableSet.<InjectionPoint>of()));
248 }
249
250 static class InjectorFactory implements InternalFactory<Injector>, Provider<Injector> {
251 private final Injector injector;
252
253 private InjectorFactory(Injector injector) {
254 this.injector = injector;
limpbizkit3d58d6b2008-03-08 16:11:47 +0000255 }
256
limpbizkit5fb9d922008-10-14 23:35:56 +0000257 public Injector get(Errors errors, InternalContext context, Dependency<?> dependency)
258 throws ErrorsException {
259 return injector;
260 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000261
limpbizkit5fb9d922008-10-14 23:35:56 +0000262 public Injector get() {
263 return injector;
264 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000265
limpbizkit5fb9d922008-10-14 23:35:56 +0000266 public String toString() {
267 return "Provider<Injector>";
limpbizkit3d58d6b2008-03-08 16:11:47 +0000268 }
limpbizkit2b931682008-04-21 00:59:34 +0000269 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000270
limpbizkit2b931682008-04-21 00:59:34 +0000271 /**
limpbizkit9dc32d42008-06-15 11:29:10 +0000272 * The Logger is a special case because it knows the injection point of the injected member. It's
273 * the only binding that does this.
limpbizkit2b931682008-04-21 00:59:34 +0000274 */
275 private void bindLogger() {
276 Key<Logger> key = Key.get(Logger.class);
277 LoggerFactory loggerFactory = new LoggerFactory();
limpbizkit5fb9d922008-10-14 23:35:56 +0000278 injector.state.putBinding(key,
limpbizkit2b931682008-04-21 00:59:34 +0000279 new ProviderInstanceBindingImpl<Logger>(injector, key,
limpbizkit9532e622008-06-18 08:20:54 +0000280 SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scopes.NO_SCOPE,
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000281 loggerFactory, LoadStrategy.LAZY, ImmutableSet.<InjectionPoint>of()));
limpbizkit2b931682008-04-21 00:59:34 +0000282 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000283
limpbizkit2b931682008-04-21 00:59:34 +0000284 static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
limpbizkita98bc7a2008-08-29 16:52:44 +0000285 public Logger get(Errors errors, InternalContext context, Dependency<?> dependency) {
286 InjectionPoint injectionPoint = dependency.getInjectionPoint();
287 return injectionPoint == null
limpbizkit2b931682008-04-21 00:59:34 +0000288 ? Logger.getAnonymousLogger()
limpbizkita98bc7a2008-08-29 16:52:44 +0000289 : Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
limpbizkit2b931682008-04-21 00:59:34 +0000290 }
291
292 public Logger get() {
293 return Logger.getAnonymousLogger();
294 }
295
296 public String toString() {
297 return "Provider<Logger>";
limpbizkit3d58d6b2008-03-08 16:11:47 +0000298 }
299 }
300
limpbizkit8eef9982008-08-03 00:45:52 +0000301 /** {@link Injector} exposed to users in {@link Stage#TOOL}. */
302 static class ToolStageInjector implements Injector {
303 private final Injector delegateInjector;
304
305 ToolStageInjector(Injector delegateInjector) {
306 this.delegateInjector = delegateInjector;
307 }
308 public void injectMembers(Object o) {
309 throw new UnsupportedOperationException(
310 "Injector.injectMembers(Object) is not supported in Stage.TOOL");
311 }
312 public Map<Key<?>, Binding<?>> getBindings() {
313 return this.delegateInjector.getBindings();
314 }
315 public <T> Binding<T> getBinding(Key<T> key) {
316 return this.delegateInjector.getBinding(key);
317 }
318 public <T> Binding<T> getBinding(Class<T> type) {
319 return this.delegateInjector.getBinding(type);
320 }
321 public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
322 return this.delegateInjector.findBindingsByType(type);
323 }
limpbizkitedd8d642008-10-18 19:50:49 +0000324 public Injector getParent() {
325 return delegateInjector.getParent();
326 }
limpbizkit5fb9d922008-10-14 23:35:56 +0000327 public Injector createChildInjector(Iterable<? extends Module> modules) {
328 return delegateInjector.createChildInjector(modules);
329 }
330 public Injector createChildInjector(Module... modules) {
331 return delegateInjector.createChildInjector(modules);
332 }
limpbizkit8eef9982008-08-03 00:45:52 +0000333 public <T> Provider<T> getProvider(Key<T> key) {
334 throw new UnsupportedOperationException(
335 "Injector.getProvider(Key<T>) is not supported in Stage.TOOL");
336 }
337 public <T> Provider<T> getProvider(Class<T> type) {
338 throw new UnsupportedOperationException(
339 "Injector.getProvider(Class<T>) is not supported in Stage.TOOL");
340 }
341 public <T> T getInstance(Key<T> key) {
342 throw new UnsupportedOperationException(
343 "Injector.getInstance(Key<T>) is not supported in Stage.TOOL");
344 }
345 public <T> T getInstance(Class<T> type) {
346 throw new UnsupportedOperationException(
347 "Injector.getInstance(Class<T>) is not supported in Stage.TOOL");
348 }
349 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000350}