blob: 8e92beb68a9f678f53a92282c2b22594ec0040a6 [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;
limpbizkitf44e9cc2008-03-26 06:33:29 +000033import java.util.List;
limpbizkit8eef9982008-08-03 00:45:52 +000034import java.util.Map;
limpbizkit3d58d6b2008-03-08 16:11:47 +000035import java.util.logging.Logger;
36
37/**
38 * Builds a dependency injection {@link Injector}.
39 *
40 * @author crazybob@google.com (Bob Lee)
41 * @author jessewilson@google.com (Jesse Wilson)
42 */
43class InjectorBuilder {
44
45 private final Stopwatch stopwatch = new Stopwatch();
46
47 private Injector parent;
48 private Stage stage;
limpbizkit9dc32d42008-06-15 11:29:10 +000049 private Factory reflectionFactory = new RuntimeReflectionFactory();
50 private final List<Module> modules = Lists.newLinkedList();
limpbizkit3d58d6b2008-03-08 16:11:47 +000051
limpbizkit3d58d6b2008-03-08 16:11:47 +000052 private InjectorImpl injector;
limpbizkit9dc32d42008-06-15 11:29:10 +000053 private Errors errors = new Errors();
limpbizkit3d58d6b2008-03-08 16:11:47 +000054
limpbizkit477f9f92008-07-28 07:05:14 +000055 private final List<Element> elements = Lists.newArrayList();
limpbizkit3d58d6b2008-03-08 16:11:47 +000056
limpbizkit00ca9f72008-08-02 17:56:17 +000057 private BindingProcessor bindCommandProcesor;
58 private InjectionRequestProcessor injectionCommandProcessor;
limpbizkit3d58d6b2008-03-08 16:11:47 +000059
60 /**
limpbizkit9dc32d42008-06-15 11:29:10 +000061 * @param stage we're running in. If the stage is {@link Stage#PRODUCTION}, we will eagerly load
62 * singletons.
limpbizkit3d58d6b2008-03-08 16:11:47 +000063 */
64 InjectorBuilder stage(Stage stage) {
65 this.stage = stage;
66 return this;
67 }
68
limpbizkit9dc32d42008-06-15 11:29:10 +000069 InjectorBuilder usingReflectionFactory(Factory reflectionFactory) {
limpbizkit916f5482008-04-16 20:51:14 +000070 this.reflectionFactory = reflectionFactory;
71 return this;
72 }
73
limpbizkit3d58d6b2008-03-08 16:11:47 +000074 InjectorBuilder parentInjector(Injector parent) {
75 this.parent = parent;
76 return this;
77 }
78
79 InjectorBuilder addModules(Iterable<? extends Module> modules) {
80 for (Module module : modules) {
81 this.modules.add(module);
82 }
83 return this;
84 }
85
86 Injector build() {
87 if (injector != null) {
88 throw new AssertionError("Already built, builders are not reusable.");
89 }
90
limpbizkit9dc32d42008-06-15 11:29:10 +000091 injector = new InjectorImpl(parent);
limpbizkit7f8eda02008-03-26 01:11:16 +000092
limpbizkit3d58d6b2008-03-08 16:11:47 +000093 modules.add(0, new BuiltInModule(injector, stage));
94
limpbizkit477f9f92008-07-28 07:05:14 +000095 elements.addAll(Elements.getElements(stage, modules));
limpbizkit3d58d6b2008-03-08 16:11:47 +000096
limpbizkit3d58d6b2008-03-08 16:11:47 +000097 buildCoreInjector();
98
99 validate();
100
limpbizkit9dc32d42008-06-15 11:29:10 +0000101 errors.throwCreationExceptionIfErrorsExist();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000102
103 // If we're in the tool stage, stop here. Don't eagerly inject or load
104 // anything.
105 if (stage == Stage.TOOL) {
limpbizkit8eef9982008-08-03 00:45:52 +0000106 return new ToolStageInjector(injector);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000107 }
108
109 fulfillInjectionRequests();
110
limpbizkit477f9f92008-07-28 07:05:14 +0000111 if (!elements.isEmpty()) {
112 throw new AssertionError("Failed to execute " + elements);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000113 }
114
115 return injector;
116 }
117
limpbizkit9dc32d42008-06-15 11:29:10 +0000118 /** Builds the injector. */
limpbizkit3d58d6b2008-03-08 16:11:47 +0000119 private void buildCoreInjector() {
limpbizkit00ca9f72008-08-02 17:56:17 +0000120 new MessageProcessor(errors)
limpbizkit477f9f92008-07-28 07:05:14 +0000121 .processCommands(elements);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000122
limpbizkit00ca9f72008-08-02 17:56:17 +0000123 InterceptorBindingProcessor interceptorCommandProcessor
124 = new InterceptorBindingProcessor(errors);
125 interceptorCommandProcessor.processCommands(elements);
126 ConstructionProxyFactory proxyFactory = interceptorCommandProcessor.createProxyFactory();
limpbizkit9dc32d42008-06-15 11:29:10 +0000127 injector.reflection = reflectionFactory.create(proxyFactory);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000128 stopwatch.resetAndLog("Interceptors creation");
129
limpbizkit00ca9f72008-08-02 17:56:17 +0000130 new ScopeBindingProcessor(errors, injector.scopes).processCommands(elements);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000131 stopwatch.resetAndLog("Scopes creation");
132
limpbizkit00ca9f72008-08-02 17:56:17 +0000133 new TypeConverterBindingProcessor(errors, injector.converters).processCommands(elements);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000134 stopwatch.resetAndLog("Converters creation");
135
limpbizkit150d6772008-04-25 17:34:25 +0000136 bindLogger();
limpbizkit00ca9f72008-08-02 17:56:17 +0000137 bindCommandProcesor = new BindingProcessor(errors,
limpbizkitecbb0802008-06-02 07:02:11 +0000138 injector, injector.scopes, injector.explicitBindings,
limpbizkit7a2341b2008-07-08 05:49:55 +0000139 injector.memberInjector);
limpbizkit477f9f92008-07-28 07:05:14 +0000140 bindCommandProcesor.processCommands(elements);
limpbizkitf44e9cc2008-03-26 06:33:29 +0000141 bindCommandProcesor.createUntargettedBindings();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000142 stopwatch.resetAndLog("Binding creation");
143
144 injector.index();
145 stopwatch.resetAndLog("Binding indexing");
146
limpbizkit00ca9f72008-08-02 17:56:17 +0000147 injectionCommandProcessor
148 = new InjectionRequestProcessor(errors, injector.memberInjector);
149 injectionCommandProcessor.processCommands(elements);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000150 stopwatch.resetAndLog("Static injection");
151 }
152
limpbizkit9dc32d42008-06-15 11:29:10 +0000153 /** Validate everything that we can validate now that the injector is ready for use. */
limpbizkit3d58d6b2008-03-08 16:11:47 +0000154 private void validate() {
155 bindCommandProcesor.runCreationListeners(injector);
156 stopwatch.resetAndLog("Validation");
157
limpbizkit00ca9f72008-08-02 17:56:17 +0000158 injectionCommandProcessor.validate(injector);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000159 stopwatch.resetAndLog("Static validation");
160
limpbizkit7a2341b2008-07-08 05:49:55 +0000161 injector.memberInjector.validateOustandingInjections(errors);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000162 stopwatch.resetAndLog("Instance member validation");
163
limpbizkit00ca9f72008-08-02 17:56:17 +0000164 new ProviderLookupProcessor(errors, injector).processCommands(elements);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000165 stopwatch.resetAndLog("Provider verification");
166
limpbizkit9dc32d42008-06-15 11:29:10 +0000167 errors.throwCreationExceptionIfErrorsExist();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000168 }
169
limpbizkit9532e622008-06-18 08:20:54 +0000170 /** Inject everything that can be injected. */
limpbizkit3d58d6b2008-03-08 16:11:47 +0000171 private void fulfillInjectionRequests() {
limpbizkit00ca9f72008-08-02 17:56:17 +0000172 injectionCommandProcessor.injectMembers(injector);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000173 stopwatch.resetAndLog("Static member injection");
limpbizkit9532e622008-06-18 08:20:54 +0000174
limpbizkit7a2341b2008-07-08 05:49:55 +0000175 injector.memberInjector.injectAll(errors);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000176 stopwatch.resetAndLog("Instance injection");
limpbizkit9532e622008-06-18 08:20:54 +0000177 errors.throwCreationExceptionIfErrorsExist();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000178
limpbizkitecbb0802008-06-02 07:02:11 +0000179 loadEagerSingletons();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000180 stopwatch.resetAndLog("Preloading");
limpbizkit9532e622008-06-18 08:20:54 +0000181 errors.throwCreationExceptionIfErrorsExist();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000182 }
183
limpbizkitecbb0802008-06-02 07:02:11 +0000184 public void loadEagerSingletons() {
185 // load eager singletons, or all singletons if we're in Stage.PRODUCTION.
limpbizkite81cf1b2008-06-02 16:41:22 +0000186 for (final BindingImpl<?> binding
kevinb9n1601ae52008-06-03 22:21:04 +0000187 : Iterables.concat(injector.explicitBindings.values(), injector.jitBindings.values())) {
limpbizkit5789ef42008-08-04 06:44:30 +0000188 if ((stage == Stage.PRODUCTION && binding.getScope() == SINGLETON)
limpbizkite81cf1b2008-06-02 16:41:22 +0000189 || binding.getLoadStrategy() == LoadStrategy.EAGER) {
limpbizkit9dc32d42008-06-15 11:29:10 +0000190 try {
191 injector.callInContext(new ContextualCallable<Void>() {
192 public Void call(InternalContext context) {
limpbizkita98bc7a2008-08-29 16:52:44 +0000193 Dependency<?> dependency = Dependency.get(binding.key);
194 context.setDependency(dependency);
limpbizkita6e0e782008-09-03 06:19:56 +0000195 errors.pushSource(dependency);
limpbizkit9dc32d42008-06-15 11:29:10 +0000196 try {
limpbizkita98bc7a2008-08-29 16:52:44 +0000197 binding.internalFactory.get(errors, context, dependency);
limpbizkit163c48a2008-06-16 02:58:08 +0000198 } catch (ErrorsException e) {
limpbizkit9dc32d42008-06-15 11:29:10 +0000199 errors.merge(e.getErrors());
200 } finally {
limpbizkita98bc7a2008-08-29 16:52:44 +0000201 context.setDependency(null);
limpbizkita6e0e782008-09-03 06:19:56 +0000202 errors.popSource(dependency);
limpbizkit9dc32d42008-06-15 11:29:10 +0000203 }
204
limpbizkitecbb0802008-06-02 07:02:11 +0000205 return null;
limpbizkitecbb0802008-06-02 07:02:11 +0000206 }
limpbizkit9dc32d42008-06-15 11:29:10 +0000207 });
limpbizkit163c48a2008-06-16 02:58:08 +0000208 } catch (ErrorsException e) {
limpbizkit9dc32d42008-06-15 11:29:10 +0000209 throw new AssertionError();
210 }
limpbizkitecbb0802008-06-02 07:02:11 +0000211 }
212 }
213 }
214
limpbizkit7d9991e2008-06-17 02:49:18 +0000215 private static class BuiltInModule implements Module {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000216 final Injector injector;
217 final Stage stage;
218
219 private BuiltInModule(Injector injector, Stage stage) {
kevinb9n1601ae52008-06-03 22:21:04 +0000220 this.injector = checkNotNull(injector, "injector");
221 this.stage = checkNotNull(stage, "stage");
limpbizkit3d58d6b2008-03-08 16:11:47 +0000222 }
223
limpbizkit7d9991e2008-06-17 02:49:18 +0000224 public void configure(Binder binder) {
limpbizkit9532e622008-06-18 08:20:54 +0000225 binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000226
limpbizkit7d9991e2008-06-17 02:49:18 +0000227 binder.bind(Stage.class).toInstance(stage);
228 binder.bindScope(Singleton.class, SINGLETON);
229 // Create default bindings.
230 // We use toProvider() instead of toInstance() to avoid infinite recursion
231 // in toString().
232 binder.bind(Injector.class).toProvider(new InjectorProvider(injector));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000233 }
234
235 class InjectorProvider implements Provider<Injector> {
236 final Injector injector;
237
238 InjectorProvider(Injector injector) {
239 this.injector = injector;
240 }
241
242 public Injector get() {
243 return injector;
244 }
245
246 public String toString() {
247 return "Provider<Injector>";
248 }
249 }
limpbizkit2b931682008-04-21 00:59:34 +0000250 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000251
limpbizkit2b931682008-04-21 00:59:34 +0000252 /**
limpbizkit9dc32d42008-06-15 11:29:10 +0000253 * The Logger is a special case because it knows the injection point of the injected member. It's
254 * the only binding that does this.
limpbizkit2b931682008-04-21 00:59:34 +0000255 */
256 private void bindLogger() {
257 Key<Logger> key = Key.get(Logger.class);
258 LoggerFactory loggerFactory = new LoggerFactory();
259 injector.explicitBindings.put(key,
260 new ProviderInstanceBindingImpl<Logger>(injector, key,
limpbizkit9532e622008-06-18 08:20:54 +0000261 SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scopes.NO_SCOPE,
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000262 loggerFactory, LoadStrategy.LAZY, ImmutableSet.<InjectionPoint>of()));
limpbizkit2b931682008-04-21 00:59:34 +0000263 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000264
limpbizkit2b931682008-04-21 00:59:34 +0000265 static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
limpbizkita98bc7a2008-08-29 16:52:44 +0000266 public Logger get(Errors errors, InternalContext context, Dependency<?> dependency) {
267 InjectionPoint injectionPoint = dependency.getInjectionPoint();
268 return injectionPoint == null
limpbizkit2b931682008-04-21 00:59:34 +0000269 ? Logger.getAnonymousLogger()
limpbizkita98bc7a2008-08-29 16:52:44 +0000270 : Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
limpbizkit2b931682008-04-21 00:59:34 +0000271 }
272
273 public Logger get() {
274 return Logger.getAnonymousLogger();
275 }
276
277 public String toString() {
278 return "Provider<Logger>";
limpbizkit3d58d6b2008-03-08 16:11:47 +0000279 }
280 }
281
limpbizkit8eef9982008-08-03 00:45:52 +0000282 /** {@link Injector} exposed to users in {@link Stage#TOOL}. */
283 static class ToolStageInjector implements Injector {
284 private final Injector delegateInjector;
285
286 ToolStageInjector(Injector delegateInjector) {
287 this.delegateInjector = delegateInjector;
288 }
289 public void injectMembers(Object o) {
290 throw new UnsupportedOperationException(
291 "Injector.injectMembers(Object) is not supported in Stage.TOOL");
292 }
293 public Map<Key<?>, Binding<?>> getBindings() {
294 return this.delegateInjector.getBindings();
295 }
296 public <T> Binding<T> getBinding(Key<T> key) {
297 return this.delegateInjector.getBinding(key);
298 }
299 public <T> Binding<T> getBinding(Class<T> type) {
300 return this.delegateInjector.getBinding(type);
301 }
302 public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
303 return this.delegateInjector.findBindingsByType(type);
304 }
305 public <T> Provider<T> getProvider(Key<T> key) {
306 throw new UnsupportedOperationException(
307 "Injector.getProvider(Key<T>) is not supported in Stage.TOOL");
308 }
309 public <T> Provider<T> getProvider(Class<T> type) {
310 throw new UnsupportedOperationException(
311 "Injector.getProvider(Class<T>) is not supported in Stage.TOOL");
312 }
313 public <T> T getInstance(Key<T> key) {
314 throw new UnsupportedOperationException(
315 "Injector.getInstance(Key<T>) is not supported in Stage.TOOL");
316 }
317 public <T> T getInstance(Class<T> type) {
318 throw new UnsupportedOperationException(
319 "Injector.getInstance(Class<T>) is not supported in Stage.TOOL");
320 }
321 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000322}