blob: 25451979bac0721fbd77a3d0fae76cbec3e7c38e [file] [log] [blame]
crazyboblee66b415a2006-08-25 02:01:19 +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
crazyboblee7c5b2c42007-01-20 02:05:20 +000019import com.google.inject.util.Objects;
crazybobleeef83bd22007-02-01 00:52:57 +000020import com.google.inject.util.Stopwatch;
crazybobleee3adfd62007-02-02 21:30:08 +000021import com.google.inject.util.ToStringBuilder;
crazyboblee7c5b2c42007-01-20 02:05:20 +000022import static com.google.inject.util.Objects.nonNull;
crazybobleee3adfd62007-02-02 21:30:08 +000023import com.google.inject.spi.ConstructionProxyFactory;
24import com.google.inject.spi.DefaultConstructionProxyFactory;
crazyboblee7c5b2c42007-01-20 02:05:20 +000025
crazyboblee66b415a2006-08-25 02:01:19 +000026import java.lang.reflect.Member;
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.HashMap;
crazyboblee235d0682007-01-31 02:25:21 +000030import java.util.LinkedHashSet;
crazyboblee66b415a2006-08-25 02:01:19 +000031import java.util.List;
32import java.util.Map;
crazyboblee07e41822006-11-21 01:27:08 +000033import java.util.Properties;
crazyboblee7c5b2c42007-01-20 02:05:20 +000034import java.util.Set;
crazyboblee235d0682007-01-31 02:25:21 +000035import java.util.HashSet;
crazyboblee66b415a2006-08-25 02:01:19 +000036import java.util.logging.Logger;
37
38/**
crazyboblee7c5b2c42007-01-20 02:05:20 +000039 * Builds a dependency injection {@link Container}. Binds {@link Key}s to
crazyboblee235d0682007-01-31 02:25:21 +000040 * implementations. A binding implementation could be anything from a constant
41 * value to an object in the HTTP session.
crazyboblee66b415a2006-08-25 02:01:19 +000042 *
crazyboblee7c5b2c42007-01-20 02:05:20 +000043 * <p>Not safe for concurrent use.
44 *
45 * <p>Default bindings include:
crazyboblee66b415a2006-08-25 02:01:19 +000046 *
47 * <ul>
crazyboblee78e1cc12007-01-26 02:18:06 +000048 * <li>A {@code Factory<T>} for each binding of type {@code T}
crazyboblee7c5b2c42007-01-20 02:05:20 +000049 * <li>The {@link Container} iself
50 * <li>The {@link Logger} for the class being injected
crazyboblee66b415a2006-08-25 02:01:19 +000051 * </ul>
52 *
crazyboblee07e41822006-11-21 01:27:08 +000053 * <p>Converts constants as needed from {@code String} to any primitive type
crazyboblee7c5b2c42007-01-20 02:05:20 +000054 * in addition to {@code enum} and {@code Class<?>}.
crazyboblee07e41822006-11-21 01:27:08 +000055 *
crazyboblee66b415a2006-08-25 02:01:19 +000056 * @author crazybob@google.com (Bob Lee)
57 */
58public final class ContainerBuilder {
59
crazyboblee63b592b2007-01-25 02:45:24 +000060 private static final Logger logger =
61 Logger.getLogger(ContainerBuilder.class.getName());
62
crazyboblee7c5b2c42007-01-20 02:05:20 +000063 final List<BindingBuilder<?>> bindingBuilders =
64 new ArrayList<BindingBuilder<?>>();
65 final List<ConstantBindingBuilder> constantBindingBuilders =
66 new ArrayList<ConstantBindingBuilder>();
67 final List<LinkedBindingBuilder<?>> linkedBindingBuilders =
68 new ArrayList<LinkedBindingBuilder<?>>();
crazyboblee63b592b2007-01-25 02:45:24 +000069 final Map<String, Scope> scopes = new HashMap<String, Scope>();
crazyboblee7c5b2c42007-01-20 02:05:20 +000070
crazybobleeef83bd22007-02-01 00:52:57 +000071 final List<StaticInjection> staticInjections =
72 new ArrayList<StaticInjection>();
crazyboblee7c5b2c42007-01-20 02:05:20 +000073
crazybobleea6e73982007-02-02 00:21:07 +000074 ContainerImpl container;
crazyboblee66b415a2006-08-25 02:01:19 +000075
crazyboblee7c5b2c42007-01-20 02:05:20 +000076 /**
77 * Keeps error messages in order and prevents duplicates.
78 */
79 Set<ErrorMessage> errorMessages = new LinkedHashSet<ErrorMessage>();
80
crazyboblee66b415a2006-08-25 02:01:19 +000081 private static final InternalFactory<Container> CONTAINER_FACTORY =
82 new InternalFactory<Container>() {
crazyboblee7c5b2c42007-01-20 02:05:20 +000083 public Container get(InternalContext context) {
crazyboblee66b415a2006-08-25 02:01:19 +000084 return context.getContainer();
85 }
86 };
87
88 private static final InternalFactory<Logger> LOGGER_FACTORY =
89 new InternalFactory<Logger>() {
crazyboblee7c5b2c42007-01-20 02:05:20 +000090 public Logger get(InternalContext context) {
crazyboblee66b415a2006-08-25 02:01:19 +000091 Member member = context.getExternalContext().getMember();
92 return member == null ? Logger.getAnonymousLogger()
93 : Logger.getLogger(member.getDeclaringClass().getName());
94 }
95 };
96
crazyboblee7c5b2c42007-01-20 02:05:20 +000097 static final String UNKNOWN_SOURCE = "[unknown source]";
98
crazyboblee63b592b2007-01-25 02:45:24 +000099 static final Scope DEFAULT_SCOPE = new Scope() {
100 public <T> Factory<T> scope(Key<T> key, Factory<T> creator) {
crazyboblee7289ac12007-02-01 00:28:09 +0000101 // We special case optimize default scope, so this never actually runs.
102 throw new AssertionError();
crazyboblee63b592b2007-01-25 02:45:24 +0000103 }
104 };
105
crazybobleee3adfd62007-02-02 21:30:08 +0000106 final ConstructionProxyFactory constructionProxyFactory;
107
crazyboblee66b415a2006-08-25 02:01:19 +0000108 /**
109 * Constructs a new builder.
crazybobleee3adfd62007-02-02 21:30:08 +0000110 *
111 * @param constructionProxyFactory to use when constructing objects
crazyboblee66b415a2006-08-25 02:01:19 +0000112 */
crazybobleee3adfd62007-02-02 21:30:08 +0000113 public ContainerBuilder(ConstructionProxyFactory constructionProxyFactory) {
crazyboblee2af06372007-02-01 02:07:53 +0000114 put(Scopes.DEFAULT_SCOPE, DEFAULT_SCOPE);
115 put(Scopes.CONTAINER_SCOPE, ContainerScope.INSTANCE);
crazyboblee63b592b2007-01-25 02:45:24 +0000116
crazyboblee7c5b2c42007-01-20 02:05:20 +0000117 bind(Container.class).to(CONTAINER_FACTORY);
118 bind(Logger.class).to(LOGGER_FACTORY);
crazybobleee3adfd62007-02-02 21:30:08 +0000119
120 this.constructionProxyFactory = nonNull(constructionProxyFactory,
121 "construction proxy factory");
122 }
123
124 /**
125 * Constructs a new builder.
126 */
127 public ContainerBuilder() {
128 this(new DefaultConstructionProxyFactory());
crazyboblee66b415a2006-08-25 02:01:19 +0000129 }
130
crazyboblee4727ee22007-01-30 03:13:38 +0000131 final List<Validation> validations = new ArrayList<Validation>();
132
133 /**
134 * Registers a type to be validated for injection when we create the
135 * container.
136 */
137 void validate(final Object source, final Class<?> type) {
138 validations.add(new Validation() {
crazybobleeef83bd22007-02-01 00:52:57 +0000139 public void run(final ContainerImpl container) {
140 container.withErrorHandler(new ConfigurationErrorHandler(source),
141 new Runnable() {
142 public void run() {
143 container.getConstructor(type);
144 }
145 });
crazyboblee4727ee22007-01-30 03:13:38 +0000146 }
147 });
148 }
149
crazyboblee235d0682007-01-31 02:25:21 +0000150 /**
151 * A validation command to run after we create the container but before
152 * we return it to the client.
153 */
crazyboblee4727ee22007-01-30 03:13:38 +0000154 interface Validation {
155 void run(ContainerImpl container);
156 }
157
crazyboblee66b415a2006-08-25 02:01:19 +0000158 /**
crazybobleedac49912007-01-29 23:17:41 +0000159 * Maps a {@link Scope} instance to a given scope name. Scopes should be
160 * mapped before used in bindings. @{@link Scoped#value()} references this
161 * name.
crazyboblee63b592b2007-01-25 02:45:24 +0000162 */
163 public void put(String name, Scope scope) {
164 if (scopes.containsKey(nonNull(name, "name"))) {
crazyboblee235d0682007-01-31 02:25:21 +0000165 addError(source(), ErrorMessage.DUPLICATE_SCOPES, name);
crazyboblee63b592b2007-01-25 02:45:24 +0000166 } else {
crazyboblee235d0682007-01-31 02:25:21 +0000167 scopes.put(nonNull(name, "name"), nonNull(scope, "scope"));
crazyboblee63b592b2007-01-25 02:45:24 +0000168 }
169 }
170
171 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000172 * Binds the given key.
173 */
174 public <T> BindingBuilder<T> bind(Key<T> key) {
crazyboblee66b415a2006-08-25 02:01:19 +0000175 ensureNotCreated();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000176 BindingBuilder<T> builder = new BindingBuilder<T>(key).from(source());
177 bindingBuilders.add(builder);
178 return builder;
crazyboblee66b415a2006-08-25 02:01:19 +0000179 }
180
181 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000182 * Binds the given type.
crazyboblee66b415a2006-08-25 02:01:19 +0000183 */
crazyboblee0baa9fc2007-01-31 02:38:54 +0000184 public <T> BindingBuilder<T> bind(TypeLiteral<T> typeLiteral) {
185 return bind(Key.get(typeLiteral));
crazyboblee66b415a2006-08-25 02:01:19 +0000186 }
187
188 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000189 * Binds the given type.
crazyboblee66b415a2006-08-25 02:01:19 +0000190 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000191 public <T> BindingBuilder<T> bind(Class<T> clazz) {
192 return bind(Key.get(clazz));
crazyboblee66b415a2006-08-25 02:01:19 +0000193 }
194
195 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000196 * Links the given key to another key effectively creating an alias for a
197 * binding.
crazyboblee66b415a2006-08-25 02:01:19 +0000198 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000199 public <T> LinkedBindingBuilder<T> link(Key<T> key) {
200 ensureNotCreated();
201 LinkedBindingBuilder<T> builder =
202 new LinkedBindingBuilder<T>(key).from(source());
203 linkedBindingBuilders.add(builder);
204 return builder;
crazyboblee66b415a2006-08-25 02:01:19 +0000205 }
206
207 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000208 * Binds a constant to the given name.
crazyboblee66b415a2006-08-25 02:01:19 +0000209 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000210 public ConstantBindingBuilder bind(String name) {
211 ensureNotCreated();
212 return bind(name, source());
crazyboblee66b415a2006-08-25 02:01:19 +0000213 }
214
215 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000216 * Binds a constant to the given name from the given source.
crazyboblee66b415a2006-08-25 02:01:19 +0000217 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000218 private ConstantBindingBuilder bind(String name, Object source) {
219 ConstantBindingBuilder builder =
220 new ConstantBindingBuilder(Objects.nonNull(name, "name")).from(source);
221 constantBindingBuilders.add(builder);
222 return builder;
crazyboblee66b415a2006-08-25 02:01:19 +0000223 }
224
225 /**
crazyboblee63b592b2007-01-25 02:45:24 +0000226 * Binds a string constant for each property.
crazyboblee66b415a2006-08-25 02:01:19 +0000227 */
crazyboblee041e9332007-01-29 22:58:35 +0000228 public void bindProperties(Map<String, String> properties) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000229 ensureNotCreated();
230 Object source = source();
crazyboblee07e41822006-11-21 01:27:08 +0000231 for (Map.Entry<String, String> entry : properties.entrySet()) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000232 String key = entry.getKey();
233 String value = entry.getValue();
234 bind(key, source).to(value);
crazyboblee07e41822006-11-21 01:27:08 +0000235 }
crazyboblee07e41822006-11-21 01:27:08 +0000236 }
237
238 /**
crazyboblee63b592b2007-01-25 02:45:24 +0000239 * Binds a string constant for each property.
crazyboblee07e41822006-11-21 01:27:08 +0000240 */
crazyboblee041e9332007-01-29 22:58:35 +0000241 public void bindProperties(Properties properties) {
donald.brown263c5bc2006-11-16 18:39:55 +0000242 ensureNotCreated();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000243 Object source = source();
244 for (Map.Entry<Object, Object> entry : properties.entrySet()) {
245 String key = (String) entry.getKey();
246 String value = (String) entry.getValue();
247 bind(key, source).to(value);
donald.brown263c5bc2006-11-16 18:39:55 +0000248 }
donald.brown263c5bc2006-11-16 18:39:55 +0000249 }
250
251 /**
crazyboblee4776db72007-01-29 23:27:30 +0000252 * Upon successful creation, the {@link Container} will inject static fields
253 * and methods in the given classes.
crazyboblee66b415a2006-08-25 02:01:19 +0000254 *
255 * @param types for which static members will be injected
256 */
crazyboblee4776db72007-01-29 23:27:30 +0000257 public void requestStaticInjection(Class<?>... types) {
crazybobleeef83bd22007-02-01 00:52:57 +0000258 staticInjections.add(new StaticInjection(source(), types));
crazyboblee041e9332007-01-29 22:58:35 +0000259 }
260
261 /**
262 * Applies the given module to this builder.
263 */
264 public void apply(Module module) {
265 module.configure(this);
crazyboblee66b415a2006-08-25 02:01:19 +0000266 }
267
crazyboblee235d0682007-01-31 02:25:21 +0000268 void addError(Object source, String message, Object... arguments) {
269 new ConfigurationErrorHandler(source).handle(message, arguments);
270 }
271
272 void addError(Object source, String message) {
273 new ConfigurationErrorHandler(source).handle(message);
274 }
275
crazyboblee66b415a2006-08-25 02:01:19 +0000276 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000277 * Adds an error message to be reported at creation time.
crazyboblee66b415a2006-08-25 02:01:19 +0000278 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000279 void add(ErrorMessage errorMessage) {
280 errorMessages.add(errorMessage);
crazyboblee66b415a2006-08-25 02:01:19 +0000281 }
282
crazyboblee9bb62022007-02-01 00:06:53 +0000283 Stopwatch stopwatch = new Stopwatch();
284
crazyboblee66b415a2006-08-25 02:01:19 +0000285 /**
286 * Creates a {@link Container} instance. Injects static members for classes
crazyboblee4776db72007-01-29 23:27:30 +0000287 * which were registered using {@link #requestStaticInjection(Class...)}.
crazyboblee66b415a2006-08-25 02:01:19 +0000288 *
crazyboblee235d0682007-01-31 02:25:21 +0000289 * @param preload If true, the container will load all container-scoped
290 * bindings now. If false, the container will lazily load them. Eager
291 * loading is appropriate for production use (catch errors early and take
292 * any performance hit up front) while lazy loading can speed development.
293 *
crazyboblee9bb62022007-02-01 00:06:53 +0000294 * @throws ContainerCreationException if configuration errors are found. The
295 * expectation is that the application will log this exception and exit.
crazyboblee66b415a2006-08-25 02:01:19 +0000296 * @throws IllegalStateException if called more than once
297 */
crazyboblee9bb62022007-02-01 00:06:53 +0000298 public synchronized Container create(boolean preload)
299 throws ContainerCreationException {
crazyboblee9bb62022007-02-01 00:06:53 +0000300 stopwatch.resetAndLog(logger, "Configuration");
301
302 // Create the container.
crazybobleea6e73982007-02-02 00:21:07 +0000303 ensureNotCreated();
304 Map<Key<?>, Binding<?>> bindings =
305 new HashMap<Key<?>, Binding<?>>();
crazybobleee3adfd62007-02-02 21:30:08 +0000306 container = new ContainerImpl(constructionProxyFactory, bindings);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000307
crazybobleea6e73982007-02-02 00:21:07 +0000308 createConstantBindings();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000309
crazyboblee9bb62022007-02-01 00:06:53 +0000310 // Commands to execute before returning the Container instance.
crazybobleea6e73982007-02-02 00:21:07 +0000311 final List<ContextualCallable<Void>> preloaders =
312 new ArrayList<ContextualCallable<Void>>();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000313
crazybobleea6e73982007-02-02 00:21:07 +0000314 createBindings(preload, preloaders);
315 createLinkedBindings();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000316
crazyboblee9bb62022007-02-01 00:06:53 +0000317 stopwatch.resetAndLog(logger, "Binding creation");
318
319 // Run validations.
320 for (Validation validation : validations) {
321 validation.run(container);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000322 }
323
crazyboblee9bb62022007-02-01 00:06:53 +0000324 stopwatch.resetAndLog(logger, "Validation");
325
crazybobleeef83bd22007-02-01 00:52:57 +0000326 for (StaticInjection staticInjection : staticInjections) {
327 staticInjection.createInjectors(container);
328 }
329
330 stopwatch.resetAndLog(logger, "Static validation");
331
crazybobleeabc4dd02007-02-01 01:44:36 +0000332 // Blow up if we encountered errors.
crazyboblee9bb62022007-02-01 00:06:53 +0000333 if (!errorMessages.isEmpty()) {
334 throw new ContainerCreationException(createErrorMessage());
335 }
336
337 // Switch to runtime error handling.
338 container.setErrorHandler(new RuntimeErrorHandler());
339
340 // Inject static members.
crazybobleeef83bd22007-02-01 00:52:57 +0000341 for (StaticInjection staticInjection : staticInjections) {
342 staticInjection.runInjectors(container);
343 }
crazyboblee9bb62022007-02-01 00:06:53 +0000344
345 stopwatch.resetAndLog(logger, "Static member injection");
346
347 // Run preloading commands.
crazybobleeabc4dd02007-02-01 01:44:36 +0000348 runPreloaders(container, preloaders);
crazyboblee9bb62022007-02-01 00:06:53 +0000349
350 stopwatch.resetAndLog(logger, "Preloading");
351
352 return container;
353 }
354
crazybobleeabc4dd02007-02-01 01:44:36 +0000355 private void runPreloaders(ContainerImpl container,
crazybobleea6e73982007-02-02 00:21:07 +0000356 final List<ContextualCallable<Void>> preloaders) {
357 container.callInContext(new ContextualCallable<Void>() {
crazybobleeabc4dd02007-02-01 01:44:36 +0000358 public Void call(InternalContext context) {
crazybobleea6e73982007-02-02 00:21:07 +0000359 for (ContextualCallable<Void> preloader : preloaders) {
crazybobleeabc4dd02007-02-01 01:44:36 +0000360 preloader.call(context);
361 }
362 return null;
363 }
364 });
365 }
366
crazyboblee9bb62022007-02-01 00:06:53 +0000367 private String createErrorMessage() {
368 StringBuilder error = new StringBuilder();
369 error.append("Guice configuration errors:\n\n");
370 int index = 1;
371 for (ErrorMessage errorMessage : errorMessages) {
372 error.append(index++)
373 .append(") ")
374 .append("Error at ")
375 .append(errorMessage.getSource())
376 .append(':')
377 .append('\n')
378 .append(" ")
379 .append(errorMessage.getMessage())
380 .append("\n\n");
381 }
382 error.append(errorMessages.size()).append(" error[s]\n");
383 return error.toString();
384 }
385
crazybobleea6e73982007-02-02 00:21:07 +0000386 private void createLinkedBindings() {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000387 for (LinkedBindingBuilder<?> builder : linkedBindingBuilders) {
crazybobleea6e73982007-02-02 00:21:07 +0000388 createLinkedBinding(builder);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000389 }
crazyboblee9bb62022007-02-01 00:06:53 +0000390 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000391
crazybobleea6e73982007-02-02 00:21:07 +0000392 private <T> void createLinkedBinding(
393 LinkedBindingBuilder<T> builder) {
394 // TODO: Support linking to a later-declared link?
395 Key<? extends T> destinationKey = builder.getDestination();
396 if (destinationKey == null) {
397 addError(builder.getSource(), ErrorMessage.MISSING_LINK_DESTINATION);
398 return;
399 }
400
401 Binding<? extends T> destination = getBinding(destinationKey);
402 if (destination == null) {
403 addError(builder.getSource(), ErrorMessage.LINK_DESTINATION_NOT_FOUND,
404 destinationKey);
405 return;
406 }
407
408 Binding<?> binding =
409 Binding.newInstance(container, builder.getKey(), builder.getSource(),
410 destination.getInternalFactory());
411
412 putBinding(binding);
413 }
414
415 @SuppressWarnings({"unchecked"})
416 private <T> Binding<T> getBinding(Key<T> destinationKey) {
417 return (Binding<T>) container.internalBindings().get(destinationKey);
418 }
419
420 private void createBindings(boolean preload,
421 List<ContextualCallable<Void>> preloaders) {
crazyboblee9bb62022007-02-01 00:06:53 +0000422 for (BindingBuilder<?> builder : bindingBuilders) {
crazybobleea6e73982007-02-02 00:21:07 +0000423 createBinding(builder, preload, preloaders);
424 }
425 }
crazyboblee4727ee22007-01-30 03:13:38 +0000426
crazybobleea6e73982007-02-02 00:21:07 +0000427 private <T> void createBinding(BindingBuilder<T> builder,
428 boolean preload, List<ContextualCallable<Void>> preloaders) {
429 final Key<T> key = builder.getKey();
430 final InternalFactory<? extends T> factory =
431 builder.getInternalFactory(container);
432 Binding<?> binding = Binding.newInstance(
433 container, key, builder.getSource(), factory);
434
435 putBinding(binding);
436
437 // Register to preload if necessary.
438 if (builder.isInContainerScope()) {
439 if (preload || builder.shouldPreload()) {
440 preloaders.add(new BindingPreloader(key, factory));
441 }
442 } else {
443 if (builder.shouldPreload()) {
444 addError(builder.getSource(), ErrorMessage.PRELOAD_NOT_ALLOWED);
crazyboblee9bb62022007-02-01 00:06:53 +0000445 }
crazyboblee66b415a2006-08-25 02:01:19 +0000446 }
crazyboblee9bb62022007-02-01 00:06:53 +0000447 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000448
crazybobleea6e73982007-02-02 00:21:07 +0000449 private void createConstantBindings() {
crazyboblee9bb62022007-02-01 00:06:53 +0000450 for (ConstantBindingBuilder builder : constantBindingBuilders) {
crazybobleea6e73982007-02-02 00:21:07 +0000451 createConstantBinding(builder);
452 }
453 }
454
455 @SuppressWarnings({"unchecked"})
456 private <T> void createConstantBinding(ConstantBindingBuilder builder) {
457 if (builder.hasValue()) {
458 Key<T> key = (Key<T>) builder.getKey();
459 InternalFactory<? extends T> factory =
460 (InternalFactory<? extends T>) builder.getInternalFactory();
461 Binding<?> binding =
462 Binding.newInstance(container, key, builder.getSource(), factory);
463 putBinding(binding);
464 } else {
465 addError(builder.getSource(), ErrorMessage.MISSING_CONSTANT_VALUE);
crazyboblee9bb62022007-02-01 00:06:53 +0000466 }
crazyboblee66b415a2006-08-25 02:01:19 +0000467 }
468
crazyboblee235d0682007-01-31 02:25:21 +0000469 void putFactory(Object source, Map<Key<?>, InternalFactory<?>> factories,
470 Key<?> key, InternalFactory<?> factory) {
471 if (factories.containsKey(key)) {
472 addError(source, ErrorMessage.BINDING_ALREADY_SET, key);
473 } else {
474 factories.put(key, factory);
475 }
476 }
477
crazybobleea6e73982007-02-02 00:21:07 +0000478 void putBinding(Binding<?> binding) {
479 Key<?> key = binding.getKey();
480 Map<Key<?>, Binding<?>> bindings = container.internalBindings();
481 Binding<?> original = bindings.get(key);
482 if (bindings.containsKey(key)) {
483 addError(binding.getSource(), ErrorMessage.BINDING_ALREADY_SET, key,
484 original.getSource());
485 } else {
486 bindings.put(key, binding);
487 }
488 }
489
crazyboblee66b415a2006-08-25 02:01:19 +0000490 /**
491 * Currently we only support creating one Container instance per builder.
492 * If we want to support creating more than one container per builder,
493 * we should move to a "factory factory" model where we create a factory
494 * instance per Container. Right now, one factory instance would be
crazyboblee235d0682007-01-31 02:25:21 +0000495 * shared across all the containers, which means container-scoped objects
496 * would be shared, etc.
crazyboblee66b415a2006-08-25 02:01:19 +0000497 */
498 private void ensureNotCreated() {
crazybobleea6e73982007-02-02 00:21:07 +0000499 if (container != null) {
crazyboblee66b415a2006-08-25 02:01:19 +0000500 throw new IllegalStateException("Container already created.");
501 }
502 }
503
504 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000505 * Binds a {@link Key} to an implementation in a given scope.
506 */
crazyboblee63b592b2007-01-25 02:45:24 +0000507 public class BindingBuilder<T> {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000508
509 Object source = ContainerBuilder.UNKNOWN_SOURCE;
510 Key<T> key;
511 InternalFactory<? extends T> factory;
512 Scope scope;
crazybobleea6e73982007-02-02 00:21:07 +0000513 String scopeName;
crazybobleeabc4dd02007-02-01 01:44:36 +0000514 boolean preload = false;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000515
516 BindingBuilder(Key<T> key) {
517 this.key = nonNull(key, "key");
518 }
519
crazyboblee235d0682007-01-31 02:25:21 +0000520 Object getSource() {
521 return source;
522 }
523
crazyboblee7c5b2c42007-01-20 02:05:20 +0000524 Key<T> getKey() {
525 return key;
526 }
527
crazyboblee63b592b2007-01-25 02:45:24 +0000528 BindingBuilder<T> from(Object source) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000529 this.source = source;
530 return this;
531 }
532
533 /**
534 * Sets the name of this binding.
535 */
536 public BindingBuilder<T> named(String name) {
537 if (!this.key.hasDefaultName()) {
crazyboblee235d0682007-01-31 02:25:21 +0000538 addError(source, ErrorMessage.NAME_ALREADY_SET);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000539 }
540
541 this.key = this.key.named(name);
542 return this;
543 }
544
545 /**
546 * Binds to instances of the given implementation class. The {@link
547 * Container} will inject the implementation instances as well. Sets the
548 * scope based on the @{@link Scoped} annotation on the implementation
549 * class if present.
550 */
551 public <I extends T> BindingBuilder<T> to(Class<I> implementation) {
crazyboblee0baa9fc2007-01-31 02:38:54 +0000552 return to(TypeLiteral.get(implementation));
crazyboblee7c5b2c42007-01-20 02:05:20 +0000553 }
554
555 /**
556 * Binds to instances of the given implementation type. The {@link
557 * Container} will inject the implementation instances as well. Sets the
558 * scope based on the @{@link Scoped} annotation on the implementation
559 * class if present.
560 */
crazyboblee0baa9fc2007-01-31 02:38:54 +0000561 public <I extends T> BindingBuilder<T> to(TypeLiteral<I> implementation) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000562 ensureImplementationIsNotSet();
crazyboblee4727ee22007-01-30 03:13:38 +0000563 validate(source, implementation.getRawType());
crazyboblee7289ac12007-02-01 00:28:09 +0000564 this.factory = new DefaultFactory<I>(key, implementation);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000565 setScopeFromType(implementation.getRawType());
566 return this;
567 }
568
569 private void setScopeFromType(Class<?> implementation) {
570 Scoped scoped = implementation.getAnnotation(Scoped.class);
571 if (scoped != null) {
572 in(scoped.value());
573 }
574 }
575
576 /**
577 * Binds to instances from the given factory.
578 */
579 public BindingBuilder<T> to(
580 final ContextualFactory<? extends T> factory) {
581 ensureImplementationIsNotSet();
582
crazyboblee7289ac12007-02-01 00:28:09 +0000583 this.factory = new InternalToContextualFactoryAdapter<T>(factory);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000584
585 return this;
586 }
587
588 /**
589 * Binds to instances from the given factory.
590 */
591 public BindingBuilder<T> to(final Factory<? extends T> factory) {
592 ensureImplementationIsNotSet();
crazyboblee7289ac12007-02-01 00:28:09 +0000593 this.factory = new InternalFactoryToFactoryAdapter<T>(factory);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000594 return this;
595 }
596
597 /**
598 * Binds to the given instance.
599 */
600 BindingBuilder<T> to(T instance) {
601 ensureImplementationIsNotSet();
602 this.factory = new ConstantFactory<T>(instance);
crazybobleea6e73982007-02-02 00:21:07 +0000603 in(Scopes.CONTAINER_SCOPE);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000604 return this;
605 }
606
607 /**
608 * Binds to instances from the given factory.
609 */
610 BindingBuilder<T> to(final InternalFactory<? extends T> factory) {
611 ensureImplementationIsNotSet();
612 this.factory = factory;
613 return this;
614 }
615
616 /**
617 * Adds an error message if the implementation has already been bound.
618 */
619 private void ensureImplementationIsNotSet() {
620 if (factory != null) {
crazyboblee235d0682007-01-31 02:25:21 +0000621 addError(source, ErrorMessage.IMPLEMENTATION_ALREADY_SET);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000622 }
623 }
624
625 /**
crazyboblee63b592b2007-01-25 02:45:24 +0000626 * Specifies the scope. References the name passed to {@link
627 * ContainerBuilder#put(String, Scope)}.
crazyboblee7c5b2c42007-01-20 02:05:20 +0000628 */
crazyboblee63b592b2007-01-25 02:45:24 +0000629 public BindingBuilder<T> in(String scopeName) {
630 ensureScopeNotSet();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000631
crazyboblee63b592b2007-01-25 02:45:24 +0000632 // We could defer this lookup to when we create the container, but this
633 // is fine for now.
crazyboblee235d0682007-01-31 02:25:21 +0000634 this.scope = scopes.get(nonNull(scopeName, "scope name"));
crazyboblee63b592b2007-01-25 02:45:24 +0000635 if (this.scope == null) {
crazyboblee235d0682007-01-31 02:25:21 +0000636 addError(source, ErrorMessage.SCOPE_NOT_FOUND, scopeName,
637 scopes.keySet());
crazyboblee63b592b2007-01-25 02:45:24 +0000638 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000639 return this;
640 }
641
crazyboblee63b592b2007-01-25 02:45:24 +0000642 /**
643 * Specifies the scope.
644 */
645 public BindingBuilder<T> in(Scope scope) {
646 ensureScopeNotSet();
647
648 this.scope = nonNull(scope, "scope");
649 return this;
650 }
651
crazyboblee2af06372007-02-01 02:07:53 +0000652 /**
crazybobleea6e73982007-02-02 00:21:07 +0000653 * Specifies container scope (i.e.&nbsp;one instance per container).
crazyboblee2af06372007-02-01 02:07:53 +0000654 */
655 public BindingBuilder<T> inContainerScope() {
656 return in(Scopes.CONTAINER_SCOPE);
657 }
658
crazyboblee63b592b2007-01-25 02:45:24 +0000659 private void ensureScopeNotSet() {
660 if (this.scope != null) {
crazyboblee235d0682007-01-31 02:25:21 +0000661 addError(source, ErrorMessage.SCOPE_ALREADY_SET);
crazyboblee63b592b2007-01-25 02:45:24 +0000662 }
663 }
664
crazybobleeabc4dd02007-02-01 01:44:36 +0000665 /**
666 * Instructs the builder to eagerly load this binding when it creates
667 * the container. Useful for application initialization logic. Currently
668 * only supported for container-scoped bindings.
669 */
670 public BindingBuilder<T> preload() {
671 this.preload = true;
672 return this;
673 }
674
675 boolean shouldPreload() {
676 return preload;
677 }
678
crazyboblee63b592b2007-01-25 02:45:24 +0000679 InternalFactory<? extends T> getInternalFactory(
680 final ContainerImpl container) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000681 // If an implementation wasn't specified, use the injection type.
682 if (this.factory == null) {
crazyboblee0baa9fc2007-01-31 02:38:54 +0000683 to(key.getType());
crazyboblee7c5b2c42007-01-20 02:05:20 +0000684 }
685
crazyboblee63b592b2007-01-25 02:45:24 +0000686 if (scope == null || scope == DEFAULT_SCOPE) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000687 return this.factory;
688 }
689
crazyboblee7289ac12007-02-01 00:28:09 +0000690 Factory<T> scoped = scope.scope(this.key,
691 new FactoryToInternalFactoryAdapter<T>(container, this.factory));
692 return new InternalFactoryToFactoryAdapter<T>(scoped);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000693 }
694
crazyboblee235d0682007-01-31 02:25:21 +0000695 boolean isInContainerScope() {
696 return this.scope == ContainerScope.INSTANCE;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000697 }
crazyboblee7289ac12007-02-01 00:28:09 +0000698 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000699
crazyboblee7289ac12007-02-01 00:28:09 +0000700 /**
701 * Injects new instances of the specified implementation class.
702 */
703 private static class DefaultFactory<T> implements InternalFactory<T> {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000704
crazybobleee3adfd62007-02-02 21:30:08 +0000705 volatile ConstructorInjector<T> constructor;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000706
crazyboblee7289ac12007-02-01 00:28:09 +0000707 private final TypeLiteral<T> implementation;
708 private final Key<? super T> key;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000709
crazyboblee7289ac12007-02-01 00:28:09 +0000710 public DefaultFactory(Key<? super T> key, TypeLiteral<T> implementation) {
711 this.key = key;
712 this.implementation = implementation;
713 }
714
715 @SuppressWarnings("unchecked")
716 public T get(InternalContext context) {
717 if (constructor == null) {
crazybobleee3adfd62007-02-02 21:30:08 +0000718 this.constructor =
719 context.getContainerImpl().getConstructor(implementation);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000720 }
crazyboblee7289ac12007-02-01 00:28:09 +0000721 return (T) constructor.construct(context, key.getRawType());
722 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000723
crazyboblee7289ac12007-02-01 00:28:09 +0000724 public String toString() {
crazybobleee3adfd62007-02-02 21:30:08 +0000725 return new ToStringBuilder(Factory.class)
726 .add("implementation", implementation)
727 .toString();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000728 }
729 }
730
731 /**
732 * Builds a constant binding.
733 */
crazyboblee63b592b2007-01-25 02:45:24 +0000734 public class ConstantBindingBuilder {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000735
736 final String name;
737 Class<?> type;
738 Object value;
739 Object source = ContainerBuilder.UNKNOWN_SOURCE;
740
741 ConstantBindingBuilder(String name) {
742 this.name = name;
743 }
744
745 Key<?> getKey() {
746 return Key.get(type, name);
747 }
748
749 boolean hasValue() {
750 return type != null;
751 }
752
753 Object getSource() {
754 return source;
755 }
756
757 InternalFactory<?> getInternalFactory() {
758 return new ConstantFactory<Object>(value);
759 }
760
crazyboblee63b592b2007-01-25 02:45:24 +0000761 ConstantBindingBuilder from(Object source) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000762 this.source = source;
763 return this;
764 }
765
766 /**
767 * Binds constant to the given value.
768 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000769 public void to(String value) {
770 to(String.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000771 }
772
773 /**
774 * Binds constant to the given value.
775 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000776 public void to(int value) {
777 to(int.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000778 }
779
780 /**
781 * Binds constant to the given value.
782 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000783 public void to(long value) {
784 to(long.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000785 }
786
787 /**
788 * Binds constant to the given value.
789 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000790 public void to(boolean value) {
791 to(boolean.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000792 }
793
794 /**
795 * Binds constant to the given value.
796 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000797 public void to(double value) {
798 to(double.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000799 }
800
801 /**
802 * Binds constant to the given value.
803 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000804 public void to(float value) {
805 to(float.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000806 }
807
808 /**
809 * Binds constant to the given value.
810 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000811 public void to(short value) {
812 to(short.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000813 }
814
815 /**
816 * Binds constant to the given value.
817 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000818 public void to(char value) {
819 to(char.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000820 }
821
822 /**
823 * Binds constant to the given value.
824 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000825 public void to(Class<?> value) {
826 to(Class.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000827 }
828
829 /**
830 * Binds constant to the given value.
831 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000832 public <E extends Enum<E>> void to(E value) {
833 to(value.getDeclaringClass(), value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000834 }
835
836 /**
837 * Maps a constant value to the given type and name.
838 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000839 <T> void to(final Class<T> type, final T value) {
crazyboblee235d0682007-01-31 02:25:21 +0000840 if (this.value != null) {
841 addError(source, ErrorMessage.CONSTANT_VALUE_ALREADY_SET);
842 } else {
843 this.type = type;
844 this.value = value;
845 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000846 }
847 }
848
849 /**
850 * Links one binding to another.
851 */
crazyboblee63b592b2007-01-25 02:45:24 +0000852 public class LinkedBindingBuilder<T> {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000853
854 Key<T> key;
855 Key<? extends T> destination;
856 Object source = ContainerBuilder.UNKNOWN_SOURCE;
857
858 LinkedBindingBuilder(Key<T> key) {
859 this.key = key;
860 }
861
862 Object getSource() {
863 return source;
864 }
865
866 Key<T> getKey() {
867 return key;
868 }
869
870 Key<? extends T> getDestination() {
871 return destination;
872 }
873
crazyboblee63b592b2007-01-25 02:45:24 +0000874 LinkedBindingBuilder<T> from(Object source) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000875 this.source = source;
876 return this;
877 }
878
879 /**
880 * Links to another binding with the given key.
881 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000882 public void to(Key<? extends T> destination) {
crazyboblee235d0682007-01-31 02:25:21 +0000883 if (this.destination != null) {
884 addError(source, ErrorMessage.LINK_DESTINATION_ALREADY_SET);
885 } else {
886 this.destination = destination;
887 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000888 }
889 }
crazyboblee4727ee22007-01-30 03:13:38 +0000890
crazyboblee235d0682007-01-31 02:25:21 +0000891 /**
892 * Handles errors up until we successfully create the container.
893 */
894 class ConfigurationErrorHandler extends AbstractErrorHandler {
crazyboblee4727ee22007-01-30 03:13:38 +0000895
896 final Object source;
897
898 ConfigurationErrorHandler(Object source) {
899 this.source = source;
900 }
901
902 public void handle(String message) {
903 add(new ErrorMessage(source, message));
904 }
905
906 public void handle(Throwable t) {
907 add(new ErrorMessage(source, t.getMessage()));
908 }
909 }
910
crazyboblee235d0682007-01-31 02:25:21 +0000911 /**
912 * Handles errors after the container is created.
913 */
914 class RuntimeErrorHandler extends AbstractErrorHandler {
crazyboblee4727ee22007-01-30 03:13:38 +0000915
916 public void handle(String message) {
917 throw new ConfigurationException(message);
918 }
919
920 public void handle(Throwable t) {
921 throw new ConfigurationException(t);
922 }
923 }
crazyboblee235d0682007-01-31 02:25:21 +0000924
925 final Set<String> skippedClassNames = new HashSet<String>(Arrays.asList(
926 ContainerBuilder.class.getName(),
927 AbstractModule.class.getName()
928 ));
929
930 /**
931 * Instructs the builder to skip the given class in the stack trace when
932 * determining the source of a binding. Use this to keep the container
933 * builder from logging utility methods as the sources of bindings (i.e.
934 * it will skip to the utility methods' callers instead).
935 *
936 * <p>Skipping only takes place after this method is called.
937 */
938 void skipSource(Class<?> clazz) {
939 skippedClassNames.add(clazz.getName());
940 }
941
942 /**
943 * Creates an object pointing to the current location within the
944 * configuration. If we run into a problem later, we'll be able to trace it
945 * back to the original source. Useful for debugging. The default
946 * implementation returns {@code ContainerBuilder}'s caller's {@code
947 * StackTraceElement}.
948 */
949 Object source() {
950 // Search up the stack until we find a class outside of this one.
951 for (final StackTraceElement element : new Throwable().getStackTrace()) {
952 String className = element.getClassName();
953 if (!skippedClassNames.contains(className)) {
954 return element;
955 }
956 }
957 throw new AssertionError();
958 }
crazybobleeef83bd22007-02-01 00:52:57 +0000959
960 /**
961 * A requested static injection.
962 */
963 class StaticInjection {
964
965 final Object source;
966 final Class<?>[] types;
967 final List<ContainerImpl.Injector> injectors =
968 new ArrayList<ContainerImpl.Injector>();
969
970 public StaticInjection(Object source, Class<?>[] types) {
971 this.source = source;
972 this.types = types;
973 }
974
975 void createInjectors(final ContainerImpl container) {
976 container.withErrorHandler(new ConfigurationErrorHandler(source),
977 new Runnable() {
978 public void run() {
979 for (Class<?> clazz : types) {
980 container.addInjectorsForFields(
981 clazz.getDeclaredFields(), true, injectors);
982 container.addInjectorsForMethods(
983 clazz.getDeclaredMethods(), true, injectors);
984 }
985 }
986 });
987 }
988
989 void runInjectors(ContainerImpl container) {
crazybobleea6e73982007-02-02 00:21:07 +0000990 container.callInContext(new ContextualCallable<Void>() {
crazybobleeef83bd22007-02-01 00:52:57 +0000991 public Void call(InternalContext context) {
992 for (ContainerImpl.Injector injector : injectors) {
993 injector.inject(context, null);
994 }
995 return null;
996 }
997 });
998 }
999 }
crazybobleeabc4dd02007-02-01 01:44:36 +00001000
1001 static class BindingPreloader
crazybobleea6e73982007-02-02 00:21:07 +00001002 implements ContextualCallable<Void> {
crazybobleeabc4dd02007-02-01 01:44:36 +00001003
1004 private final Key<?> key;
1005 private final InternalFactory<?> factory;
1006
1007 public BindingPreloader(Key<?> key, InternalFactory<?> factory) {
1008 this.key = key;
1009 this.factory = factory;
1010 }
1011
1012 public Void call(InternalContext context) {
1013 ExternalContext<?> externalContext =
1014 ExternalContext.newInstance(null, key, context.getContainerImpl());
1015 context.setExternalContext(externalContext);
1016 try {
1017 factory.get(context);
1018 return null;
1019 } finally {
1020 context.setExternalContext(null);
1021 }
1022 }
1023 }
crazyboblee66b415a2006-08-25 02:01:19 +00001024}