blob: cea61dc036ab8f6bcf7e79a155a04282815ed2a4 [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
limpbizkit5ae41eb2009-06-06 17:51:27 +000017package com.google.inject.internal;
limpbizkit3d58d6b2008-03-08 16:11:47 +000018
limpbizkit5ae41eb2009-06-06 17:51:27 +000019import com.google.inject.Binding;
20import com.google.inject.Injector;
21import com.google.inject.Key;
22import com.google.inject.MembersInjector;
23import com.google.inject.Module;
24import com.google.inject.Provider;
25import com.google.inject.Stage;
26import com.google.inject.TypeLiteral;
limpbizkita98bc7a2008-08-29 16:52:44 +000027import com.google.inject.spi.Dependency;
limpbizkit5fb9d922008-10-14 23:35:56 +000028import java.util.Collection;
limpbizkitf44e9cc2008-03-26 06:33:29 +000029import java.util.List;
limpbizkit8eef9982008-08-03 00:45:52 +000030import java.util.Map;
limpbizkit51273652008-09-29 18:21:20 +000031import java.util.Set;
limpbizkit3d58d6b2008-03-08 16:11:47 +000032
33/**
limpbizkitfcbdf992008-11-26 02:37:35 +000034 * Builds a tree of injectors. This is a primary injector, plus child injectors needed for each
limpbizkit5ae41eb2009-06-06 17:51:27 +000035 * {@code Binder.newPrivateBinder() private environment}. The primary injector is not necessarily a
limpbizkitfcbdf992008-11-26 02:37:35 +000036 * top-level injector.
37 *
38 * <p>Injector construction happens in two phases.
limpbizkita8dccb32008-11-11 22:41:56 +000039 * <ol>
40 * <li>Static building. In this phase, we interpret commands, create bindings, and inspect
41 * dependencies. During this phase, we hold a lock to ensure consistency with parent injectors.
42 * No user code is executed in this phase.</li>
43 * <li>Dynamic injection. In this phase, we call user code. We inject members that requested
44 * injection. This may require user's objects be created and their providers be called. And we
45 * create eager singletons. In this phase, user code may have started other threads. This phase
46 * is not executed for injectors created using {@link Stage#TOOL the tool stage}</li>
47 * </ol>
limpbizkit3d58d6b2008-03-08 16:11:47 +000048 *
49 * @author crazybob@google.com (Bob Lee)
50 * @author jessewilson@google.com (Jesse Wilson)
51 */
limpbizkit5ae41eb2009-06-06 17:51:27 +000052public final class InjectorBuilder {
limpbizkit3d58d6b2008-03-08 16:11:47 +000053
54 private final Stopwatch stopwatch = new Stopwatch();
limpbizkitfcbdf992008-11-26 02:37:35 +000055 private final Errors errors = new Errors();
limpbizkit3d58d6b2008-03-08 16:11:47 +000056
limpbizkit3d58d6b2008-03-08 16:11:47 +000057 private Stage stage;
limpbizkit3d58d6b2008-03-08 16:11:47 +000058
limpbizkitfcbdf992008-11-26 02:37:35 +000059 private final Initializer initializer = new Initializer();
60 private final BindingProcessor bindingProcesor;
61 private final InjectionRequestProcessor injectionRequestProcessor;
limpbizkit3d58d6b2008-03-08 16:11:47 +000062
limpbizkitfcbdf992008-11-26 02:37:35 +000063 private final InjectorShell.Builder shellBuilder = new InjectorShell.Builder();
64 private List<InjectorShell> shells;
limpbizkit3d58d6b2008-03-08 16:11:47 +000065
limpbizkit5ae41eb2009-06-06 17:51:27 +000066 public InjectorBuilder() {
limpbizkitfcbdf992008-11-26 02:37:35 +000067 injectionRequestProcessor = new InjectionRequestProcessor(errors, initializer);
limpbizkitc3f92842008-12-30 19:43:47 +000068 bindingProcesor = new BindingProcessor(errors, initializer);
limpbizkitfcbdf992008-11-26 02:37:35 +000069 }
limpbizkit3d58d6b2008-03-08 16:11:47 +000070
71 /**
limpbizkitfcbdf992008-11-26 02:37:35 +000072 * Sets the stage for the created injector. If the stage is {@link Stage#PRODUCTION}, this class
73 * will eagerly load singletons.
limpbizkit3d58d6b2008-03-08 16:11:47 +000074 */
limpbizkit5ae41eb2009-06-06 17:51:27 +000075 public InjectorBuilder stage(Stage stage) {
limpbizkitfcbdf992008-11-26 02:37:35 +000076 shellBuilder.stage(stage);
limpbizkit3d58d6b2008-03-08 16:11:47 +000077 this.stage = stage;
78 return this;
79 }
80
limpbizkitfcbdf992008-11-26 02:37:35 +000081 /**
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}.
84 */
limpbizkit5ae41eb2009-06-06 17:51:27 +000085 public InjectorBuilder parentInjector(InjectorImpl parent) {
limpbizkitfcbdf992008-11-26 02:37:35 +000086 shellBuilder.parent(parent);
87 return stage(parent.getInstance(Stage.class));
limpbizkit3d58d6b2008-03-08 16:11:47 +000088 }
89
limpbizkit5ae41eb2009-06-06 17:51:27 +000090 public InjectorBuilder addModules(Iterable<? extends Module> modules) {
limpbizkitfcbdf992008-11-26 02:37:35 +000091 shellBuilder.addModules(modules);
limpbizkit3d58d6b2008-03-08 16:11:47 +000092 return this;
93 }
94
limpbizkit5ae41eb2009-06-06 17:51:27 +000095 public Injector build() {
limpbizkitfcbdf992008-11-26 02:37:35 +000096 if (shellBuilder == null) {
limpbizkit3d58d6b2008-03-08 16:11:47 +000097 throw new AssertionError("Already built, builders are not reusable.");
98 }
99
limpbizkitfcbdf992008-11-26 02:37:35 +0000100 // Synchronize while we're building up the bindings and other injector state. This ensures that
101 // the JIT bindings in the parent injector don't change while we're being built
102 synchronized (shellBuilder.lock()) {
limpbizkitc3f92842008-12-30 19:43:47 +0000103 shells = shellBuilder.build(initializer, bindingProcesor, stopwatch, errors);
limpbizkitfcbdf992008-11-26 02:37:35 +0000104 stopwatch.resetAndLog("Injector construction");
limpbizkit7f8eda02008-03-26 01:11:16 +0000105
limpbizkitfcbdf992008-11-26 02:37:35 +0000106 initializeStatically();
107 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000108
limpbizkita8dccb32008-11-11 22:41:56 +0000109 // If we're in the tool stage, stop here. Don't eagerly inject or load anything.
limpbizkit3d58d6b2008-03-08 16:11:47 +0000110 if (stage == Stage.TOOL) {
limpbizkitfcbdf992008-11-26 02:37:35 +0000111 return new ToolStageInjector(primaryInjector());
limpbizkit3d58d6b2008-03-08 16:11:47 +0000112 }
113
limpbizkita8dccb32008-11-11 22:41:56 +0000114 injectDynamically();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000115
limpbizkitfcbdf992008-11-26 02:37:35 +0000116 return primaryInjector();
117 }
118
119 /** Initialize and validate everything. */
120 private void initializeStatically() {
121 bindingProcesor.initializeBindings();
122 stopwatch.resetAndLog("Binding initialization");
123
124 for (InjectorShell shell : shells) {
125 shell.getInjector().index();
126 }
127 stopwatch.resetAndLog("Binding indexing");
128
129 injectionRequestProcessor.process(shells);
130 stopwatch.resetAndLog("Collecting injection requests");
131
limpbizkitc3f92842008-12-30 19:43:47 +0000132 bindingProcesor.runCreationListeners();
limpbizkitfcbdf992008-11-26 02:37:35 +0000133 stopwatch.resetAndLog("Binding validation");
134
135 injectionRequestProcessor.validate();
136 stopwatch.resetAndLog("Static validation");
137
138 initializer.validateOustandingInjections(errors);
139 stopwatch.resetAndLog("Instance member validation");
140
limpbizkit97eac0f2009-03-28 18:25:35 +0000141 new LookupProcessor(errors).process(shells);
limpbizkit8d620752009-03-31 22:37:26 +0000142 for (InjectorShell shell : shells) {
143 ((DeferredLookups) shell.getInjector().lookups).initialize(errors);
144 }
limpbizkitfcbdf992008-11-26 02:37:35 +0000145 stopwatch.resetAndLog("Provider verification");
146
147 for (InjectorShell shell : shells) {
148 if (!shell.getElements().isEmpty()) {
149 throw new AssertionError("Failed to execute " + shell.getElements());
150 }
151 }
152
153 errors.throwCreationExceptionIfErrorsExist();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000154 }
155
limpbizkita8dccb32008-11-11 22:41:56 +0000156 /**
limpbizkitfcbdf992008-11-26 02:37:35 +0000157 * Returns the injector being constructed. This is not necessarily the root injector.
limpbizkita8dccb32008-11-11 22:41:56 +0000158 */
limpbizkitfcbdf992008-11-26 02:37:35 +0000159 private Injector primaryInjector() {
160 return shells.get(0).getInjector();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000161 }
162
limpbizkita8dccb32008-11-11 22:41:56 +0000163 /**
164 * Inject everything that can be injected. This method is intentionally not synchronized. If we
165 * locked while injecting members (ie. running user code), things would deadlock should the user
166 * code build a just-in-time binding from another thread.
167 */
168 private void injectDynamically() {
limpbizkitfcbdf992008-11-26 02:37:35 +0000169 injectionRequestProcessor.injectMembers();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000170 stopwatch.resetAndLog("Static member injection");
limpbizkit9532e622008-06-18 08:20:54 +0000171
limpbizkitfcbdf992008-11-26 02:37:35 +0000172 initializer.injectAll(errors);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000173 stopwatch.resetAndLog("Instance injection");
limpbizkit9532e622008-06-18 08:20:54 +0000174 errors.throwCreationExceptionIfErrorsExist();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000175
limpbizkitfcbdf992008-11-26 02:37:35 +0000176 for (InjectorShell shell : shells) {
177 loadEagerSingletons(shell.getInjector(), stage, errors);
178 }
179 stopwatch.resetAndLog("Preloading singletons");
limpbizkit9532e622008-06-18 08:20:54 +0000180 errors.throwCreationExceptionIfErrorsExist();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000181 }
182
limpbizkitfcbdf992008-11-26 02:37:35 +0000183 /**
184 * Loads eager singletons, or all singletons if we're in Stage.PRODUCTION. Bindings discovered
185 * while we're binding these singletons are not be eager.
186 */
limpbizkit5ae41eb2009-06-06 17:51:27 +0000187 void loadEagerSingletons(InjectorImpl injector, Stage stage, final Errors errors) {
limpbizkit5fb9d922008-10-14 23:35:56 +0000188 @SuppressWarnings("unchecked") // casting Collection<Binding> to Collection<BindingImpl> is safe
189 Set<BindingImpl<?>> candidateBindings = ImmutableSet.copyOf(Iterables.concat(
190 (Collection) injector.state.getExplicitBindingsThisLevel().values(),
191 injector.jitBindings.values()));
limpbizkit51273652008-09-29 18:21:20 +0000192 for (final BindingImpl<?> binding : candidateBindings) {
limpbizkite37805a2009-06-04 19:33:42 +0000193 if (isEagerSingleton(injector, binding, stage)) {
limpbizkit9dc32d42008-06-15 11:29:10 +0000194 try {
195 injector.callInContext(new ContextualCallable<Void>() {
limpbizkit76c24b12008-12-25 04:32:41 +0000196 Dependency<?> dependency = Dependency.get(binding.getKey());
limpbizkit9dc32d42008-06-15 11:29:10 +0000197 public Void call(InternalContext context) {
limpbizkita98bc7a2008-08-29 16:52:44 +0000198 context.setDependency(dependency);
limpbizkit06898062008-11-02 05:14:55 +0000199 Errors errorsForBinding = errors.withSource(dependency);
limpbizkit9dc32d42008-06-15 11:29:10 +0000200 try {
limpbizkit76c24b12008-12-25 04:32:41 +0000201 binding.getInternalFactory().get(errorsForBinding, context, dependency);
limpbizkit163c48a2008-06-16 02:58:08 +0000202 } catch (ErrorsException e) {
limpbizkit06898062008-11-02 05:14:55 +0000203 errorsForBinding.merge(e.getErrors());
limpbizkit9dc32d42008-06-15 11:29:10 +0000204 } finally {
limpbizkita98bc7a2008-08-29 16:52:44 +0000205 context.setDependency(null);
limpbizkit9dc32d42008-06-15 11:29:10 +0000206 }
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
limpbizkite37805a2009-06-04 19:33:42 +0000218 private boolean isEagerSingleton(InjectorImpl injector, BindingImpl<?> binding, Stage stage) {
219 if (binding.getScoping().isEagerSingleton(stage)) {
220 return true;
221 }
222
223 // handle a corner case where a child injector links to a binding in a parent injector, and
224 // that binding is singleton. We won't catch this otherwise because we only iterate the child's
225 // bindings.
226 if (binding instanceof LinkedBindingImpl) {
227 Key<?> linkedBinding = ((LinkedBindingImpl<?>) binding).getLinkedKey();
228 return isEagerSingleton(injector, injector.getBinding(linkedBinding), stage);
229 }
230
231 return false;
232 }
233
limpbizkit8eef9982008-08-03 00:45:52 +0000234 /** {@link Injector} exposed to users in {@link Stage#TOOL}. */
235 static class ToolStageInjector implements Injector {
236 private final Injector delegateInjector;
237
238 ToolStageInjector(Injector delegateInjector) {
239 this.delegateInjector = delegateInjector;
240 }
241 public void injectMembers(Object o) {
242 throw new UnsupportedOperationException(
243 "Injector.injectMembers(Object) is not supported in Stage.TOOL");
244 }
245 public Map<Key<?>, Binding<?>> getBindings() {
246 return this.delegateInjector.getBindings();
247 }
248 public <T> Binding<T> getBinding(Key<T> key) {
249 return this.delegateInjector.getBinding(key);
250 }
251 public <T> Binding<T> getBinding(Class<T> type) {
252 return this.delegateInjector.getBinding(type);
253 }
254 public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
255 return this.delegateInjector.findBindingsByType(type);
256 }
limpbizkitedd8d642008-10-18 19:50:49 +0000257 public Injector getParent() {
258 return delegateInjector.getParent();
259 }
limpbizkit5fb9d922008-10-14 23:35:56 +0000260 public Injector createChildInjector(Iterable<? extends Module> modules) {
261 return delegateInjector.createChildInjector(modules);
262 }
263 public Injector createChildInjector(Module... modules) {
264 return delegateInjector.createChildInjector(modules);
265 }
limpbizkit8eef9982008-08-03 00:45:52 +0000266 public <T> Provider<T> getProvider(Key<T> key) {
267 throw new UnsupportedOperationException(
268 "Injector.getProvider(Key<T>) is not supported in Stage.TOOL");
269 }
270 public <T> Provider<T> getProvider(Class<T> type) {
271 throw new UnsupportedOperationException(
272 "Injector.getProvider(Class<T>) is not supported in Stage.TOOL");
273 }
limpbizkit03b81a62009-03-18 05:34:39 +0000274 public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
275 throw new UnsupportedOperationException(
276 "Injector.getMembersInjector(TypeLiteral<T>) is not supported in Stage.TOOL");
277 }
278 public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
279 throw new UnsupportedOperationException(
280 "Injector.getMembersInjector(Class<T>) is not supported in Stage.TOOL");
281 }
limpbizkit8eef9982008-08-03 00:45:52 +0000282 public <T> T getInstance(Key<T> key) {
283 throw new UnsupportedOperationException(
284 "Injector.getInstance(Key<T>) is not supported in Stage.TOOL");
285 }
286 public <T> T getInstance(Class<T> type) {
287 throw new UnsupportedOperationException(
288 "Injector.getInstance(Class<T>) is not supported in Stage.TOOL");
289 }
290 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000291}