blob: 6bd907aa76615c3dd56bff6ca1b815bac9788e87 [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
19import static com.google.inject.Scopes.SINGLETON;
20import com.google.inject.commands.Command;
21import com.google.inject.commands.CommandRecorder;
22import com.google.inject.commands.FutureInjector;
limpbizkit2b931682008-04-21 00:59:34 +000023import com.google.inject.internal.Objects;
24import com.google.inject.internal.Stopwatch;
limpbizkit3d58d6b2008-03-08 16:11:47 +000025import com.google.inject.spi.SourceProviders;
26
27import java.lang.reflect.Member;
28import java.util.ArrayList;
limpbizkit3d58d6b2008-03-08 16:11:47 +000029import java.util.LinkedList;
limpbizkitf44e9cc2008-03-26 06:33:29 +000030import java.util.List;
limpbizkit3d58d6b2008-03-08 16:11:47 +000031import java.util.logging.Logger;
32
33/**
34 * Builds a dependency injection {@link Injector}.
35 *
36 * @author crazybob@google.com (Bob Lee)
37 * @author jessewilson@google.com (Jesse Wilson)
38 */
39class InjectorBuilder {
40
41 private final Stopwatch stopwatch = new Stopwatch();
42
43 private Injector parent;
44 private Stage stage;
limpbizkit916f5482008-04-16 20:51:14 +000045 private Reflection.Factory reflectionFactory = new RuntimeReflectionFactory();
limpbizkit3d58d6b2008-03-08 16:11:47 +000046 private final List<Module> modules = new LinkedList<Module>();
47
limpbizkit3d58d6b2008-03-08 16:11:47 +000048 private InjectorImpl injector;
limpbizkitb1d8ab42008-04-27 08:11:37 +000049 private DefaultErrorHandler errorHandler = new DefaultErrorHandler();
limpbizkit3d58d6b2008-03-08 16:11:47 +000050
51 private final FutureInjector futureInjector = new FutureInjector();
52 private final List<Command> commands = new ArrayList<Command>();
53
54 private BindCommandProcessor bindCommandProcesor;
55 private RequestStaticInjectionCommandProcessor requestStaticInjectionCommandProcessor;
56
57 /**
58 * @param stage we're running in. If the stage is {@link Stage#PRODUCTION},
59 * we will eagerly load singletons.
60 */
61 InjectorBuilder stage(Stage stage) {
62 this.stage = stage;
63 return this;
64 }
65
limpbizkit916f5482008-04-16 20:51:14 +000066 InjectorBuilder usingReflectionFactory(Reflection.Factory reflectionFactory) {
67 this.reflectionFactory = reflectionFactory;
68 return this;
69 }
70
limpbizkit3d58d6b2008-03-08 16:11:47 +000071 InjectorBuilder parentInjector(Injector parent) {
72 this.parent = parent;
73 return this;
74 }
75
76 InjectorBuilder addModules(Iterable<? extends Module> modules) {
77 for (Module module : modules) {
78 this.modules.add(module);
79 }
80 return this;
81 }
82
83 Injector build() {
84 if (injector != null) {
85 throw new AssertionError("Already built, builders are not reusable.");
86 }
87
limpbizkitb1d8ab42008-04-27 08:11:37 +000088 injector = new InjectorImpl(parent, errorHandler);
limpbizkit7f8eda02008-03-26 01:11:16 +000089
limpbizkit3d58d6b2008-03-08 16:11:47 +000090 modules.add(0, new BuiltInModule(injector, stage));
91
92 CommandRecorder commandRecorder = new CommandRecorder(futureInjector);
93 commandRecorder.setCurrentStage(stage);
94 commands.addAll(commandRecorder.recordCommands(modules));
95
limpbizkit3d58d6b2008-03-08 16:11:47 +000096 buildCoreInjector();
97
98 validate();
99
limpbizkitb1d8ab42008-04-27 08:11:37 +0000100 errorHandler.switchToRuntime();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000101
102 // If we're in the tool stage, stop here. Don't eagerly inject or load
103 // anything.
104 if (stage == Stage.TOOL) {
105 // TODO: Wrap this and prevent usage of anything besides getBindings().
106 return injector;
107 }
108
109 fulfillInjectionRequests();
110
111 if (!commands.isEmpty()) {
112 throw new AssertionError("Failed to execute " + commands);
113 }
114
115 return injector;
116 }
117
118 /**
119 * Builds the injector.
120 */
121 private void buildCoreInjector() {
limpbizkitad94bcb2008-04-28 00:22:43 +0000122 new ErrorsCommandProcessor(errorHandler)
123 .processCommands(commands);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000124
125 BindInterceptorCommandProcessor bindInterceptorCommandProcessor
limpbizkitb1d8ab42008-04-27 08:11:37 +0000126 = new BindInterceptorCommandProcessor(errorHandler);
limpbizkitad94bcb2008-04-28 00:22:43 +0000127 bindInterceptorCommandProcessor.processCommands(commands);
limpbizkit916f5482008-04-16 20:51:14 +0000128 ConstructionProxyFactory proxyFactory = bindInterceptorCommandProcessor.createProxyFactory();
limpbizkitb1d8ab42008-04-27 08:11:37 +0000129 injector.reflection = reflectionFactory.create(errorHandler, proxyFactory);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000130 stopwatch.resetAndLog("Interceptors creation");
131
limpbizkitad94bcb2008-04-28 00:22:43 +0000132 new ScopesCommandProcessor(errorHandler, injector.scopes)
133 .processCommands(commands);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000134 stopwatch.resetAndLog("Scopes creation");
135
limpbizkitad94bcb2008-04-28 00:22:43 +0000136 new ConvertToTypesCommandProcessor(errorHandler, injector.converters)
137 .processCommands(commands);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000138 stopwatch.resetAndLog("Converters creation");
139
limpbizkit150d6772008-04-25 17:34:25 +0000140 bindLogger();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000141 bindCommandProcesor = new BindCommandProcessor(
limpbizkit51515b52008-03-11 03:46:25 +0000142 injector, injector.scopes, stage, injector.explicitBindings,
143 injector.outstandingInjections);
limpbizkitad94bcb2008-04-28 00:22:43 +0000144 bindCommandProcesor.processCommands(commands);
limpbizkitf44e9cc2008-03-26 06:33:29 +0000145 bindCommandProcesor.createUntargettedBindings();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000146 stopwatch.resetAndLog("Binding creation");
147
148 injector.index();
149 stopwatch.resetAndLog("Binding indexing");
150
limpbizkit3d58d6b2008-03-08 16:11:47 +0000151 requestStaticInjectionCommandProcessor
limpbizkitad94bcb2008-04-28 00:22:43 +0000152 = new RequestStaticInjectionCommandProcessor(errorHandler);
153 requestStaticInjectionCommandProcessor
154 .processCommands(commands);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000155 stopwatch.resetAndLog("Static injection");
156 }
157
158 /**
159 * Validate everything that we can validate now that the injector is ready
160 * for use.
161 */
162 private void validate() {
163 bindCommandProcesor.runCreationListeners(injector);
164 stopwatch.resetAndLog("Validation");
165
166 requestStaticInjectionCommandProcessor.validate(injector);
167 stopwatch.resetAndLog("Static validation");
168
limpbizkit51515b52008-03-11 03:46:25 +0000169 injector.validateOustandingInjections();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000170 stopwatch.resetAndLog("Instance member validation");
171
172 new GetProviderProcessor(injector)
limpbizkitad94bcb2008-04-28 00:22:43 +0000173 .processCommands(commands);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000174 stopwatch.resetAndLog("Provider verification");
175
limpbizkitb1d8ab42008-04-27 08:11:37 +0000176 errorHandler.blowUpIfErrorsExist();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000177 }
178
179 /**
180 * Inject everything that can be injected. This uses runtime error handling.
181 */
182 private void fulfillInjectionRequests() {
183 futureInjector.initialize(injector);
184
limpbizkit3d58d6b2008-03-08 16:11:47 +0000185 requestStaticInjectionCommandProcessor.injectMembers(injector);
186 stopwatch.resetAndLog("Static member injection");
limpbizkit51515b52008-03-11 03:46:25 +0000187 injector.fulfillOutstandingInjections();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000188 stopwatch.resetAndLog("Instance injection");
189
limpbizkit51515b52008-03-11 03:46:25 +0000190 bindCommandProcesor.createEagerSingletons(injector);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000191 stopwatch.resetAndLog("Preloading");
192 }
193
194 private static class BuiltInModule extends AbstractModule {
195 final Injector injector;
196 final Stage stage;
197
198 private BuiltInModule(Injector injector, Stage stage) {
limpbizkit7f8eda02008-03-26 01:11:16 +0000199 this.injector = Objects.nonNull(injector, "injector");
200 this.stage = Objects.nonNull(stage, "stage");
limpbizkit3d58d6b2008-03-08 16:11:47 +0000201 }
202
203 protected void configure() {
204 SourceProviders.withDefault(SourceProviders.UNKNOWN_SOURCE, new Runnable() {
205 public void run() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000206 bind(Stage.class).toInstance(stage);
207 bindScope(Singleton.class, SINGLETON);
208 // Create default bindings.
209 // We use toProvider() instead of toInstance() to avoid infinite recursion
210 // in toString().
211 bind(Injector.class).toProvider(new InjectorProvider(injector));
212
213 }
214 });
215 }
216
217 class InjectorProvider implements Provider<Injector> {
218 final Injector injector;
219
220 InjectorProvider(Injector injector) {
221 this.injector = injector;
222 }
223
224 public Injector get() {
225 return injector;
226 }
227
228 public String toString() {
229 return "Provider<Injector>";
230 }
231 }
limpbizkit2b931682008-04-21 00:59:34 +0000232 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000233
limpbizkit2b931682008-04-21 00:59:34 +0000234 /**
235 * The Logger is a special case because it knows the injection point of the
236 * injected member. It's the only binding that does this.
237 */
238 private void bindLogger() {
239 Key<Logger> key = Key.get(Logger.class);
240 LoggerFactory loggerFactory = new LoggerFactory();
241 injector.explicitBindings.put(key,
242 new ProviderInstanceBindingImpl<Logger>(injector, key,
243 SourceProviders.UNKNOWN_SOURCE, loggerFactory, Scopes.NO_SCOPE,
244 loggerFactory));
245 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000246
limpbizkit2b931682008-04-21 00:59:34 +0000247 static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
248 public Logger get(InternalContext context, InjectionPoint<?> injectionPoint) {
249 Member member = injectionPoint.getMember();
250 return member == null
251 ? Logger.getAnonymousLogger()
252 : Logger.getLogger(member.getDeclaringClass().getName());
253 }
254
255 public Logger get() {
256 return Logger.getAnonymousLogger();
257 }
258
259 public String toString() {
260 return "Provider<Logger>";
limpbizkit3d58d6b2008-03-08 16:11:47 +0000261 }
262 }
263
limpbizkit3d58d6b2008-03-08 16:11:47 +0000264}