blob: d08e66cf178973ec96b9da9f5fd1a50430168647 [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;
limpbizkit9dc32d42008-06-15 11:29:10 +000020import com.google.common.collect.Iterables;
21import com.google.common.collect.Lists;
22import com.google.inject.Reflection.Factory;
limpbizkit3d58d6b2008-03-08 16:11:47 +000023import static com.google.inject.Scopes.SINGLETON;
24import com.google.inject.commands.Command;
25import com.google.inject.commands.CommandRecorder;
26import com.google.inject.commands.FutureInjector;
limpbizkit9dc32d42008-06-15 11:29:10 +000027import com.google.inject.internal.Errors;
limpbizkit163c48a2008-06-16 02:58:08 +000028import com.google.inject.internal.ErrorsException;
limpbizkit2b931682008-04-21 00:59:34 +000029import com.google.inject.internal.Stopwatch;
limpbizkit163c48a2008-06-16 02:58:08 +000030import com.google.inject.spi.InjectionPoint;
limpbizkit3d58d6b2008-03-08 16:11:47 +000031import com.google.inject.spi.SourceProviders;
limpbizkit3d58d6b2008-03-08 16:11:47 +000032import java.lang.reflect.Member;
limpbizkitf44e9cc2008-03-26 06:33:29 +000033import java.util.List;
limpbizkit3d58d6b2008-03-08 16:11:47 +000034import java.util.logging.Logger;
35
36/**
37 * Builds a dependency injection {@link Injector}.
38 *
39 * @author crazybob@google.com (Bob Lee)
40 * @author jessewilson@google.com (Jesse Wilson)
41 */
42class InjectorBuilder {
43
44 private final Stopwatch stopwatch = new Stopwatch();
45
46 private Injector parent;
47 private Stage stage;
limpbizkit9dc32d42008-06-15 11:29:10 +000048 private Factory reflectionFactory = new RuntimeReflectionFactory();
49 private final List<Module> modules = Lists.newLinkedList();
limpbizkit3d58d6b2008-03-08 16:11:47 +000050
limpbizkit3d58d6b2008-03-08 16:11:47 +000051 private InjectorImpl injector;
limpbizkit9dc32d42008-06-15 11:29:10 +000052 private Errors errors = new Errors();
limpbizkit3d58d6b2008-03-08 16:11:47 +000053
54 private final FutureInjector futureInjector = new FutureInjector();
limpbizkit9dc32d42008-06-15 11:29:10 +000055 private final List<Command> commands = Lists.newArrayList();
limpbizkit3d58d6b2008-03-08 16:11:47 +000056
57 private BindCommandProcessor bindCommandProcesor;
58 private RequestStaticInjectionCommandProcessor requestStaticInjectionCommandProcessor;
59
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
95 CommandRecorder commandRecorder = new CommandRecorder(futureInjector);
96 commandRecorder.setCurrentStage(stage);
97 commands.addAll(commandRecorder.recordCommands(modules));
98
limpbizkit3d58d6b2008-03-08 16:11:47 +000099 buildCoreInjector();
100
101 validate();
102
limpbizkit9dc32d42008-06-15 11:29:10 +0000103 errors.throwCreationExceptionIfErrorsExist();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000104
105 // If we're in the tool stage, stop here. Don't eagerly inject or load
106 // anything.
107 if (stage == Stage.TOOL) {
108 // TODO: Wrap this and prevent usage of anything besides getBindings().
109 return injector;
110 }
111
112 fulfillInjectionRequests();
113
114 if (!commands.isEmpty()) {
115 throw new AssertionError("Failed to execute " + commands);
116 }
117
118 return injector;
119 }
120
limpbizkit9dc32d42008-06-15 11:29:10 +0000121 /** Builds the injector. */
limpbizkit3d58d6b2008-03-08 16:11:47 +0000122 private void buildCoreInjector() {
limpbizkit9dc32d42008-06-15 11:29:10 +0000123 new ErrorsCommandProcessor(errors)
limpbizkitad94bcb2008-04-28 00:22:43 +0000124 .processCommands(commands);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000125
126 BindInterceptorCommandProcessor bindInterceptorCommandProcessor
limpbizkit9dc32d42008-06-15 11:29:10 +0000127 = new BindInterceptorCommandProcessor(errors);
limpbizkitad94bcb2008-04-28 00:22:43 +0000128 bindInterceptorCommandProcessor.processCommands(commands);
limpbizkit916f5482008-04-16 20:51:14 +0000129 ConstructionProxyFactory proxyFactory = bindInterceptorCommandProcessor.createProxyFactory();
limpbizkit9dc32d42008-06-15 11:29:10 +0000130 injector.reflection = reflectionFactory.create(proxyFactory);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000131 stopwatch.resetAndLog("Interceptors creation");
132
limpbizkit9dc32d42008-06-15 11:29:10 +0000133 new ScopesCommandProcessor(errors, injector.scopes).processCommands(commands);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000134 stopwatch.resetAndLog("Scopes creation");
135
limpbizkit9dc32d42008-06-15 11:29:10 +0000136 new ConvertToTypesCommandProcessor(errors, injector.converters).processCommands(commands);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000137 stopwatch.resetAndLog("Converters creation");
138
limpbizkit150d6772008-04-25 17:34:25 +0000139 bindLogger();
limpbizkit9dc32d42008-06-15 11:29:10 +0000140 bindCommandProcesor = new BindCommandProcessor(errors,
limpbizkitecbb0802008-06-02 07:02:11 +0000141 injector, injector.scopes, injector.explicitBindings,
limpbizkit51515b52008-03-11 03:46:25 +0000142 injector.outstandingInjections);
limpbizkitad94bcb2008-04-28 00:22:43 +0000143 bindCommandProcesor.processCommands(commands);
limpbizkitf44e9cc2008-03-26 06:33:29 +0000144 bindCommandProcesor.createUntargettedBindings();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000145 stopwatch.resetAndLog("Binding creation");
146
147 injector.index();
148 stopwatch.resetAndLog("Binding indexing");
149
limpbizkit3d58d6b2008-03-08 16:11:47 +0000150 requestStaticInjectionCommandProcessor
limpbizkit9dc32d42008-06-15 11:29:10 +0000151 = new RequestStaticInjectionCommandProcessor(errors);
limpbizkitad94bcb2008-04-28 00:22:43 +0000152 requestStaticInjectionCommandProcessor
153 .processCommands(commands);
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
162 requestStaticInjectionCommandProcessor.validate(injector);
163 stopwatch.resetAndLog("Static validation");
164
limpbizkit9dc32d42008-06-15 11:29:10 +0000165 injector.validateOustandingInjections(errors);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000166 stopwatch.resetAndLog("Instance member validation");
167
limpbizkit9dc32d42008-06-15 11:29:10 +0000168 new GetProviderProcessor(errors, injector).processCommands(commands);
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
limpbizkit9dc32d42008-06-15 11:29:10 +0000174 /** Inject everything that can be injected. This uses runtime error handling. */
limpbizkit3d58d6b2008-03-08 16:11:47 +0000175 private void fulfillInjectionRequests() {
176 futureInjector.initialize(injector);
177
limpbizkit3d58d6b2008-03-08 16:11:47 +0000178 requestStaticInjectionCommandProcessor.injectMembers(injector);
179 stopwatch.resetAndLog("Static member injection");
limpbizkit9dc32d42008-06-15 11:29:10 +0000180 injector.fulfillOutstandingInjections(errors);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000181 stopwatch.resetAndLog("Instance injection");
182
limpbizkitecbb0802008-06-02 07:02:11 +0000183 loadEagerSingletons();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000184 stopwatch.resetAndLog("Preloading");
185 }
186
limpbizkitecbb0802008-06-02 07:02:11 +0000187 public void loadEagerSingletons() {
188 // load eager singletons, or all singletons if we're in Stage.PRODUCTION.
limpbizkite81cf1b2008-06-02 16:41:22 +0000189 for (final BindingImpl<?> binding
kevinb9n1601ae52008-06-03 22:21:04 +0000190 : Iterables.concat(injector.explicitBindings.values(), injector.jitBindings.values())) {
limpbizkite81cf1b2008-06-02 16:41:22 +0000191 if ((stage == Stage.PRODUCTION && binding.getScope() == Scopes.SINGLETON)
192 || binding.getLoadStrategy() == LoadStrategy.EAGER) {
limpbizkit9dc32d42008-06-15 11:29:10 +0000193 try {
194 injector.callInContext(new ContextualCallable<Void>() {
195 public Void call(InternalContext context) {
196 InjectionPoint<?> injectionPoint = InjectionPoint.newInstance(binding.key);
197 context.setInjectionPoint(injectionPoint);
198 errors.pushInjectionPoint(injectionPoint);
199 try {
200 binding.internalFactory.get(errors, context, injectionPoint);
limpbizkit163c48a2008-06-16 02:58:08 +0000201 } catch (ErrorsException e) {
limpbizkit9dc32d42008-06-15 11:29:10 +0000202 errors.merge(e.getErrors());
203 } finally {
204 context.setInjectionPoint(null);
205 errors.popInjectionPoint(injectionPoint);
206 }
207
limpbizkitecbb0802008-06-02 07:02:11 +0000208 return null;
limpbizkitecbb0802008-06-02 07:02:11 +0000209 }
limpbizkit9dc32d42008-06-15 11:29:10 +0000210 });
limpbizkit163c48a2008-06-16 02:58:08 +0000211 } catch (ErrorsException e) {
limpbizkit9dc32d42008-06-15 11:29:10 +0000212 throw new AssertionError();
213 }
limpbizkitecbb0802008-06-02 07:02:11 +0000214 }
215 }
216 }
217
limpbizkit7d9991e2008-06-17 02:49:18 +0000218 private static class BuiltInModule implements Module {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000219 final Injector injector;
220 final Stage stage;
221
222 private BuiltInModule(Injector injector, Stage stage) {
kevinb9n1601ae52008-06-03 22:21:04 +0000223 this.injector = checkNotNull(injector, "injector");
224 this.stage = checkNotNull(stage, "stage");
limpbizkit3d58d6b2008-03-08 16:11:47 +0000225 }
226
limpbizkit7d9991e2008-06-17 02:49:18 +0000227 public void configure(Binder binder) {
228 binder = binder.withSource(SourceProviders.UNKNOWN_SOURCE);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000229
limpbizkit7d9991e2008-06-17 02:49:18 +0000230 binder.bind(Stage.class).toInstance(stage);
231 binder.bindScope(Singleton.class, SINGLETON);
232 // Create default bindings.
233 // We use toProvider() instead of toInstance() to avoid infinite recursion
234 // in toString().
235 binder.bind(Injector.class).toProvider(new InjectorProvider(injector));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000236 }
237
238 class InjectorProvider implements Provider<Injector> {
239 final Injector injector;
240
241 InjectorProvider(Injector injector) {
242 this.injector = injector;
243 }
244
245 public Injector get() {
246 return injector;
247 }
248
249 public String toString() {
250 return "Provider<Injector>";
251 }
252 }
limpbizkit2b931682008-04-21 00:59:34 +0000253 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000254
limpbizkit2b931682008-04-21 00:59:34 +0000255 /**
limpbizkit9dc32d42008-06-15 11:29:10 +0000256 * The Logger is a special case because it knows the injection point of the injected member. It's
257 * the only binding that does this.
limpbizkit2b931682008-04-21 00:59:34 +0000258 */
259 private void bindLogger() {
260 Key<Logger> key = Key.get(Logger.class);
261 LoggerFactory loggerFactory = new LoggerFactory();
262 injector.explicitBindings.put(key,
263 new ProviderInstanceBindingImpl<Logger>(injector, key,
264 SourceProviders.UNKNOWN_SOURCE, loggerFactory, Scopes.NO_SCOPE,
limpbizkit05757142008-06-02 06:58:47 +0000265 loggerFactory, LoadStrategy.LAZY));
limpbizkit2b931682008-04-21 00:59:34 +0000266 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000267
limpbizkit2b931682008-04-21 00:59:34 +0000268 static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
limpbizkit9dc32d42008-06-15 11:29:10 +0000269 public Logger get(Errors errors, InternalContext context, InjectionPoint<?> injectionPoint) {
limpbizkit2b931682008-04-21 00:59:34 +0000270 Member member = injectionPoint.getMember();
271 return member == null
272 ? Logger.getAnonymousLogger()
273 : Logger.getLogger(member.getDeclaringClass().getName());
274 }
275
276 public Logger get() {
277 return Logger.getAnonymousLogger();
278 }
279
280 public String toString() {
281 return "Provider<Logger>";
limpbizkit3d58d6b2008-03-08 16:11:47 +0000282 }
283 }
284
limpbizkit3d58d6b2008-03-08 16:11:47 +0000285}