blob: c8cf9406e4c15b91d217dfc6ef5e346cc3d275d4 [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;
crazyboblee7c5b2c42007-01-20 02:05:20 +000021import static com.google.inject.util.Objects.nonNull;
22
crazyboblee66b415a2006-08-25 02:01:19 +000023import java.lang.reflect.Member;
24import java.util.ArrayList;
25import java.util.Arrays;
26import java.util.HashMap;
crazyboblee235d0682007-01-31 02:25:21 +000027import java.util.LinkedHashSet;
crazyboblee66b415a2006-08-25 02:01:19 +000028import java.util.List;
29import java.util.Map;
crazyboblee07e41822006-11-21 01:27:08 +000030import java.util.Properties;
crazyboblee7c5b2c42007-01-20 02:05:20 +000031import java.util.Set;
crazyboblee235d0682007-01-31 02:25:21 +000032import java.util.HashSet;
crazyboblee66b415a2006-08-25 02:01:19 +000033import java.util.logging.Logger;
34
35/**
crazyboblee7c5b2c42007-01-20 02:05:20 +000036 * Builds a dependency injection {@link Container}. Binds {@link Key}s to
crazyboblee235d0682007-01-31 02:25:21 +000037 * implementations. A binding implementation could be anything from a constant
38 * value to an object in the HTTP session.
crazyboblee66b415a2006-08-25 02:01:19 +000039 *
crazyboblee7c5b2c42007-01-20 02:05:20 +000040 * <p>Not safe for concurrent use.
41 *
42 * <p>Default bindings include:
crazyboblee66b415a2006-08-25 02:01:19 +000043 *
44 * <ul>
crazyboblee78e1cc12007-01-26 02:18:06 +000045 * <li>A {@code Factory<T>} for each binding of type {@code T}
crazyboblee7c5b2c42007-01-20 02:05:20 +000046 * <li>The {@link Container} iself
47 * <li>The {@link Logger} for the class being injected
crazyboblee66b415a2006-08-25 02:01:19 +000048 * </ul>
49 *
crazyboblee07e41822006-11-21 01:27:08 +000050 * <p>Converts constants as needed from {@code String} to any primitive type
crazyboblee7c5b2c42007-01-20 02:05:20 +000051 * in addition to {@code enum} and {@code Class<?>}.
crazyboblee07e41822006-11-21 01:27:08 +000052 *
crazyboblee66b415a2006-08-25 02:01:19 +000053 * @author crazybob@google.com (Bob Lee)
54 */
55public final class ContainerBuilder {
56
crazyboblee63b592b2007-01-25 02:45:24 +000057 private static final Logger logger =
58 Logger.getLogger(ContainerBuilder.class.getName());
59
crazyboblee7c5b2c42007-01-20 02:05:20 +000060 final List<BindingBuilder<?>> bindingBuilders =
61 new ArrayList<BindingBuilder<?>>();
62 final List<ConstantBindingBuilder> constantBindingBuilders =
63 new ArrayList<ConstantBindingBuilder>();
64 final List<LinkedBindingBuilder<?>> linkedBindingBuilders =
65 new ArrayList<LinkedBindingBuilder<?>>();
crazyboblee63b592b2007-01-25 02:45:24 +000066 final Map<String, Scope> scopes = new HashMap<String, Scope>();
crazyboblee7c5b2c42007-01-20 02:05:20 +000067
crazybobleeef83bd22007-02-01 00:52:57 +000068 final List<StaticInjection> staticInjections =
69 new ArrayList<StaticInjection>();
crazyboblee7c5b2c42007-01-20 02:05:20 +000070
crazyboblee66b415a2006-08-25 02:01:19 +000071 boolean created;
72
crazyboblee7c5b2c42007-01-20 02:05:20 +000073 /**
74 * Keeps error messages in order and prevents duplicates.
75 */
76 Set<ErrorMessage> errorMessages = new LinkedHashSet<ErrorMessage>();
77
crazyboblee66b415a2006-08-25 02:01:19 +000078 private static final InternalFactory<Container> CONTAINER_FACTORY =
79 new InternalFactory<Container>() {
crazyboblee7c5b2c42007-01-20 02:05:20 +000080 public Container get(InternalContext context) {
crazyboblee66b415a2006-08-25 02:01:19 +000081 return context.getContainer();
82 }
83 };
84
85 private static final InternalFactory<Logger> LOGGER_FACTORY =
86 new InternalFactory<Logger>() {
crazyboblee7c5b2c42007-01-20 02:05:20 +000087 public Logger get(InternalContext context) {
crazyboblee66b415a2006-08-25 02:01:19 +000088 Member member = context.getExternalContext().getMember();
89 return member == null ? Logger.getAnonymousLogger()
90 : Logger.getLogger(member.getDeclaringClass().getName());
91 }
92 };
93
crazyboblee7c5b2c42007-01-20 02:05:20 +000094 static final String UNKNOWN_SOURCE = "[unknown source]";
95
crazyboblee63b592b2007-01-25 02:45:24 +000096 static final Scope DEFAULT_SCOPE = new Scope() {
97 public <T> Factory<T> scope(Key<T> key, Factory<T> creator) {
crazyboblee7289ac12007-02-01 00:28:09 +000098 // We special case optimize default scope, so this never actually runs.
99 throw new AssertionError();
crazyboblee63b592b2007-01-25 02:45:24 +0000100 }
101 };
102
crazyboblee66b415a2006-08-25 02:01:19 +0000103 /**
104 * Constructs a new builder.
105 */
106 public ContainerBuilder() {
crazyboblee63b592b2007-01-25 02:45:24 +0000107 put(Scopes.DEFAULT, DEFAULT_SCOPE);
crazyboblee235d0682007-01-31 02:25:21 +0000108 put(Scopes.CONTAINER, ContainerScope.INSTANCE);
crazyboblee63b592b2007-01-25 02:45:24 +0000109
crazyboblee7c5b2c42007-01-20 02:05:20 +0000110 bind(Container.class).to(CONTAINER_FACTORY);
111 bind(Logger.class).to(LOGGER_FACTORY);
crazyboblee66b415a2006-08-25 02:01:19 +0000112 }
113
crazyboblee4727ee22007-01-30 03:13:38 +0000114 final List<Validation> validations = new ArrayList<Validation>();
115
116 /**
117 * Registers a type to be validated for injection when we create the
118 * container.
119 */
120 void validate(final Object source, final Class<?> type) {
121 validations.add(new Validation() {
crazybobleeef83bd22007-02-01 00:52:57 +0000122 public void run(final ContainerImpl container) {
123 container.withErrorHandler(new ConfigurationErrorHandler(source),
124 new Runnable() {
125 public void run() {
126 container.getConstructor(type);
127 }
128 });
crazyboblee4727ee22007-01-30 03:13:38 +0000129 }
130 });
131 }
132
crazyboblee235d0682007-01-31 02:25:21 +0000133 /**
134 * A validation command to run after we create the container but before
135 * we return it to the client.
136 */
crazyboblee4727ee22007-01-30 03:13:38 +0000137 interface Validation {
138 void run(ContainerImpl container);
139 }
140
crazyboblee66b415a2006-08-25 02:01:19 +0000141 /**
crazybobleedac49912007-01-29 23:17:41 +0000142 * Maps a {@link Scope} instance to a given scope name. Scopes should be
143 * mapped before used in bindings. @{@link Scoped#value()} references this
144 * name.
crazyboblee63b592b2007-01-25 02:45:24 +0000145 */
146 public void put(String name, Scope scope) {
147 if (scopes.containsKey(nonNull(name, "name"))) {
crazyboblee235d0682007-01-31 02:25:21 +0000148 addError(source(), ErrorMessage.DUPLICATE_SCOPES, name);
crazyboblee63b592b2007-01-25 02:45:24 +0000149 } else {
crazyboblee235d0682007-01-31 02:25:21 +0000150 scopes.put(nonNull(name, "name"), nonNull(scope, "scope"));
crazyboblee63b592b2007-01-25 02:45:24 +0000151 }
152 }
153
154 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000155 * Binds the given key.
156 */
157 public <T> BindingBuilder<T> bind(Key<T> key) {
crazyboblee66b415a2006-08-25 02:01:19 +0000158 ensureNotCreated();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000159 BindingBuilder<T> builder = new BindingBuilder<T>(key).from(source());
160 bindingBuilders.add(builder);
161 return builder;
crazyboblee66b415a2006-08-25 02:01:19 +0000162 }
163
164 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000165 * Binds the given type.
crazyboblee66b415a2006-08-25 02:01:19 +0000166 */
crazyboblee0baa9fc2007-01-31 02:38:54 +0000167 public <T> BindingBuilder<T> bind(TypeLiteral<T> typeLiteral) {
168 return bind(Key.get(typeLiteral));
crazyboblee66b415a2006-08-25 02:01:19 +0000169 }
170
171 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000172 * Binds the given type.
crazyboblee66b415a2006-08-25 02:01:19 +0000173 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000174 public <T> BindingBuilder<T> bind(Class<T> clazz) {
175 return bind(Key.get(clazz));
crazyboblee66b415a2006-08-25 02:01:19 +0000176 }
177
178 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000179 * Links the given key to another key effectively creating an alias for a
180 * binding.
crazyboblee66b415a2006-08-25 02:01:19 +0000181 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000182 public <T> LinkedBindingBuilder<T> link(Key<T> key) {
183 ensureNotCreated();
184 LinkedBindingBuilder<T> builder =
185 new LinkedBindingBuilder<T>(key).from(source());
186 linkedBindingBuilders.add(builder);
187 return builder;
crazyboblee66b415a2006-08-25 02:01:19 +0000188 }
189
190 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000191 * Binds a constant to the given name.
crazyboblee66b415a2006-08-25 02:01:19 +0000192 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000193 public ConstantBindingBuilder bind(String name) {
194 ensureNotCreated();
195 return bind(name, source());
crazyboblee66b415a2006-08-25 02:01:19 +0000196 }
197
198 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000199 * Binds a constant to the given name from the given source.
crazyboblee66b415a2006-08-25 02:01:19 +0000200 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000201 private ConstantBindingBuilder bind(String name, Object source) {
202 ConstantBindingBuilder builder =
203 new ConstantBindingBuilder(Objects.nonNull(name, "name")).from(source);
204 constantBindingBuilders.add(builder);
205 return builder;
crazyboblee66b415a2006-08-25 02:01:19 +0000206 }
207
208 /**
crazyboblee63b592b2007-01-25 02:45:24 +0000209 * Binds a string constant for each property.
crazyboblee66b415a2006-08-25 02:01:19 +0000210 */
crazyboblee041e9332007-01-29 22:58:35 +0000211 public void bindProperties(Map<String, String> properties) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000212 ensureNotCreated();
213 Object source = source();
crazyboblee07e41822006-11-21 01:27:08 +0000214 for (Map.Entry<String, String> entry : properties.entrySet()) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000215 String key = entry.getKey();
216 String value = entry.getValue();
217 bind(key, source).to(value);
crazyboblee07e41822006-11-21 01:27:08 +0000218 }
crazyboblee07e41822006-11-21 01:27:08 +0000219 }
220
221 /**
crazyboblee63b592b2007-01-25 02:45:24 +0000222 * Binds a string constant for each property.
crazyboblee07e41822006-11-21 01:27:08 +0000223 */
crazyboblee041e9332007-01-29 22:58:35 +0000224 public void bindProperties(Properties properties) {
donald.brown263c5bc2006-11-16 18:39:55 +0000225 ensureNotCreated();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000226 Object source = source();
227 for (Map.Entry<Object, Object> entry : properties.entrySet()) {
228 String key = (String) entry.getKey();
229 String value = (String) entry.getValue();
230 bind(key, source).to(value);
donald.brown263c5bc2006-11-16 18:39:55 +0000231 }
donald.brown263c5bc2006-11-16 18:39:55 +0000232 }
233
234 /**
crazyboblee4776db72007-01-29 23:27:30 +0000235 * Upon successful creation, the {@link Container} will inject static fields
236 * and methods in the given classes.
crazyboblee66b415a2006-08-25 02:01:19 +0000237 *
238 * @param types for which static members will be injected
239 */
crazyboblee4776db72007-01-29 23:27:30 +0000240 public void requestStaticInjection(Class<?>... types) {
crazybobleeef83bd22007-02-01 00:52:57 +0000241 staticInjections.add(new StaticInjection(source(), types));
crazyboblee041e9332007-01-29 22:58:35 +0000242 }
243
244 /**
245 * Applies the given module to this builder.
246 */
247 public void apply(Module module) {
248 module.configure(this);
crazyboblee66b415a2006-08-25 02:01:19 +0000249 }
250
crazyboblee235d0682007-01-31 02:25:21 +0000251 void addError(Object source, String message, Object... arguments) {
252 new ConfigurationErrorHandler(source).handle(message, arguments);
253 }
254
255 void addError(Object source, String message) {
256 new ConfigurationErrorHandler(source).handle(message);
257 }
258
crazyboblee66b415a2006-08-25 02:01:19 +0000259 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000260 * Adds an error message to be reported at creation time.
crazyboblee66b415a2006-08-25 02:01:19 +0000261 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000262 void add(ErrorMessage errorMessage) {
263 errorMessages.add(errorMessage);
crazyboblee66b415a2006-08-25 02:01:19 +0000264 }
265
crazyboblee9bb62022007-02-01 00:06:53 +0000266 Stopwatch stopwatch = new Stopwatch();
267
crazyboblee66b415a2006-08-25 02:01:19 +0000268 /**
269 * Creates a {@link Container} instance. Injects static members for classes
crazyboblee4776db72007-01-29 23:27:30 +0000270 * which were registered using {@link #requestStaticInjection(Class...)}.
crazyboblee66b415a2006-08-25 02:01:19 +0000271 *
crazyboblee235d0682007-01-31 02:25:21 +0000272 * @param preload If true, the container will load all container-scoped
273 * bindings now. If false, the container will lazily load them. Eager
274 * loading is appropriate for production use (catch errors early and take
275 * any performance hit up front) while lazy loading can speed development.
276 *
crazyboblee9bb62022007-02-01 00:06:53 +0000277 * @throws ContainerCreationException if configuration errors are found. The
278 * expectation is that the application will log this exception and exit.
crazyboblee66b415a2006-08-25 02:01:19 +0000279 * @throws IllegalStateException if called more than once
280 */
crazyboblee9bb62022007-02-01 00:06:53 +0000281 public synchronized Container create(boolean preload)
282 throws ContainerCreationException {
283 // Only one Container per builder.
crazyboblee66b415a2006-08-25 02:01:19 +0000284 ensureNotCreated();
285 created = true;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000286
crazyboblee9bb62022007-02-01 00:06:53 +0000287 stopwatch.resetAndLog(logger, "Configuration");
288
289 // Create the container.
crazyboblee7c5b2c42007-01-20 02:05:20 +0000290 HashMap<Key<?>, InternalFactory<?>> factories =
291 new HashMap<Key<?>, InternalFactory<?>>();
crazyboblee63b592b2007-01-25 02:45:24 +0000292 ContainerImpl container = new ContainerImpl(factories);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000293
crazyboblee9bb62022007-02-01 00:06:53 +0000294 createConstantBindings(factories);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000295
crazyboblee9bb62022007-02-01 00:06:53 +0000296 // Commands to execute before returning the Container instance.
crazyboblee235d0682007-01-31 02:25:21 +0000297 final List<ContainerImpl.ContextualCallable<Void>> preloaders =
crazyboblee7c5b2c42007-01-20 02:05:20 +0000298 new ArrayList<ContainerImpl.ContextualCallable<Void>>();
299
crazyboblee9bb62022007-02-01 00:06:53 +0000300 createBindings(container, factories, preload, preloaders);
301 createLinkedBindings(factories);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000302
crazyboblee9bb62022007-02-01 00:06:53 +0000303 stopwatch.resetAndLog(logger, "Binding creation");
304
305 // Run validations.
306 for (Validation validation : validations) {
307 validation.run(container);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000308 }
309
crazyboblee9bb62022007-02-01 00:06:53 +0000310 stopwatch.resetAndLog(logger, "Validation");
311
crazybobleeef83bd22007-02-01 00:52:57 +0000312 for (StaticInjection staticInjection : staticInjections) {
313 staticInjection.createInjectors(container);
314 }
315
316 stopwatch.resetAndLog(logger, "Static validation");
317
crazyboblee9bb62022007-02-01 00:06:53 +0000318 // Blow up.
319 if (!errorMessages.isEmpty()) {
320 throw new ContainerCreationException(createErrorMessage());
321 }
322
323 // Switch to runtime error handling.
324 container.setErrorHandler(new RuntimeErrorHandler());
325
326 // Inject static members.
crazybobleeef83bd22007-02-01 00:52:57 +0000327 for (StaticInjection staticInjection : staticInjections) {
328 staticInjection.runInjectors(container);
329 }
crazyboblee9bb62022007-02-01 00:06:53 +0000330
331 stopwatch.resetAndLog(logger, "Static member injection");
332
333 // Run preloading commands.
334 if (preload) {
335 container.callInContext(new ContainerImpl.ContextualCallable<Void>() {
336 public Void call(InternalContext context) {
337 for (ContainerImpl.ContextualCallable<Void> preloader
338 : preloaders) {
339 preloader.call(context);
340 }
341 return null;
342 }
343 });
344 }
345
346 stopwatch.resetAndLog(logger, "Preloading");
347
348 return container;
349 }
350
351 private String createErrorMessage() {
352 StringBuilder error = new StringBuilder();
353 error.append("Guice configuration errors:\n\n");
354 int index = 1;
355 for (ErrorMessage errorMessage : errorMessages) {
356 error.append(index++)
357 .append(") ")
358 .append("Error at ")
359 .append(errorMessage.getSource())
360 .append(':')
361 .append('\n')
362 .append(" ")
363 .append(errorMessage.getMessage())
364 .append("\n\n");
365 }
366 error.append(errorMessages.size()).append(" error[s]\n");
367 return error.toString();
368 }
369
370 private void createLinkedBindings(
371 HashMap<Key<?>, InternalFactory<?>> factories) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000372 for (LinkedBindingBuilder<?> builder : linkedBindingBuilders) {
373 // TODO: Support alias to a later-declared alias.
374 Key<?> destination = builder.getDestination();
375 if (destination == null) {
crazyboblee235d0682007-01-31 02:25:21 +0000376 addError(builder.getSource(), ErrorMessage.MISSING_LINK_DESTINATION);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000377 continue;
378 }
379
380 InternalFactory<?> factory = factories.get(destination);
381 if (factory == null) {
crazyboblee235d0682007-01-31 02:25:21 +0000382 addError(builder.getSource(), ErrorMessage.LINK_DESTINATION_NOT_FOUND,
383 destination);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000384 continue;
385 }
386
crazyboblee235d0682007-01-31 02:25:21 +0000387 putFactory(builder.getSource(), factories, builder.getKey(), factory);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000388 }
crazyboblee9bb62022007-02-01 00:06:53 +0000389 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000390
crazyboblee9bb62022007-02-01 00:06:53 +0000391 private void createBindings(ContainerImpl container,
392 HashMap<Key<?>, InternalFactory<?>> factories, boolean preload,
393 List<ContainerImpl.ContextualCallable<Void>> preloaders) {
394 for (BindingBuilder<?> builder : bindingBuilders) {
395 final Key<?> key = builder.getKey();
396 final InternalFactory<?> factory = builder.getInternalFactory(container);
397 putFactory(builder.getSource(), factories, key, factory);
crazyboblee4727ee22007-01-30 03:13:38 +0000398
crazyboblee9bb62022007-02-01 00:06:53 +0000399 if (preload && builder.isInContainerScope()) {
400 preloaders.add(new ContainerImpl.ContextualCallable<Void>() {
401 public Void call(InternalContext context) {
402 context.setExternalContext(
403 ExternalContext.newInstance(null, key,
404 context.getContainerImpl()));
405 try {
406 factory.get(context);
407 return null;
408 } finally {
409 context.setExternalContext(null);
410 }
crazyboblee66b415a2006-08-25 02:01:19 +0000411 }
crazyboblee9bb62022007-02-01 00:06:53 +0000412 });
413 }
crazyboblee66b415a2006-08-25 02:01:19 +0000414 }
crazyboblee9bb62022007-02-01 00:06:53 +0000415 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000416
crazyboblee7289ac12007-02-01 00:28:09 +0000417 private void createConstantBindings(
418 HashMap<Key<?>, InternalFactory<?>> factories) {
crazyboblee9bb62022007-02-01 00:06:53 +0000419 for (ConstantBindingBuilder builder : constantBindingBuilders) {
420 if (builder.hasValue()) {
421 Key<?> key = builder.getKey();
422 InternalFactory<?> factory = builder.getInternalFactory();
423 putFactory(builder.getSource(), factories, key, factory);
424 } else {
425 addError(builder.getSource(), ErrorMessage.MISSING_CONSTANT_VALUE);
426 }
427 }
crazyboblee66b415a2006-08-25 02:01:19 +0000428 }
429
crazyboblee235d0682007-01-31 02:25:21 +0000430 void putFactory(Object source, Map<Key<?>, InternalFactory<?>> factories,
431 Key<?> key, InternalFactory<?> factory) {
432 if (factories.containsKey(key)) {
433 addError(source, ErrorMessage.BINDING_ALREADY_SET, key);
434 } else {
435 factories.put(key, factory);
436 }
437 }
438
crazyboblee66b415a2006-08-25 02:01:19 +0000439 /**
440 * Currently we only support creating one Container instance per builder.
441 * If we want to support creating more than one container per builder,
442 * we should move to a "factory factory" model where we create a factory
443 * instance per Container. Right now, one factory instance would be
crazyboblee235d0682007-01-31 02:25:21 +0000444 * shared across all the containers, which means container-scoped objects
445 * would be shared, etc.
crazyboblee66b415a2006-08-25 02:01:19 +0000446 */
447 private void ensureNotCreated() {
448 if (created) {
449 throw new IllegalStateException("Container already created.");
450 }
451 }
452
453 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000454 * Binds a {@link Key} to an implementation in a given scope.
455 */
crazyboblee63b592b2007-01-25 02:45:24 +0000456 public class BindingBuilder<T> {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000457
458 Object source = ContainerBuilder.UNKNOWN_SOURCE;
459 Key<T> key;
460 InternalFactory<? extends T> factory;
461 Scope scope;
462
463 BindingBuilder(Key<T> key) {
464 this.key = nonNull(key, "key");
465 }
466
crazyboblee235d0682007-01-31 02:25:21 +0000467 Object getSource() {
468 return source;
469 }
470
crazyboblee7c5b2c42007-01-20 02:05:20 +0000471 Key<T> getKey() {
472 return key;
473 }
474
crazyboblee63b592b2007-01-25 02:45:24 +0000475 BindingBuilder<T> from(Object source) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000476 this.source = source;
477 return this;
478 }
479
480 /**
481 * Sets the name of this binding.
482 */
483 public BindingBuilder<T> named(String name) {
484 if (!this.key.hasDefaultName()) {
crazyboblee235d0682007-01-31 02:25:21 +0000485 addError(source, ErrorMessage.NAME_ALREADY_SET);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000486 }
487
488 this.key = this.key.named(name);
489 return this;
490 }
491
492 /**
493 * Binds to instances of the given implementation class. The {@link
494 * Container} will inject the implementation instances as well. Sets the
495 * scope based on the @{@link Scoped} annotation on the implementation
496 * class if present.
497 */
498 public <I extends T> BindingBuilder<T> to(Class<I> implementation) {
crazyboblee0baa9fc2007-01-31 02:38:54 +0000499 return to(TypeLiteral.get(implementation));
crazyboblee7c5b2c42007-01-20 02:05:20 +0000500 }
501
502 /**
503 * Binds to instances of the given implementation type. The {@link
504 * Container} will inject the implementation instances as well. Sets the
505 * scope based on the @{@link Scoped} annotation on the implementation
506 * class if present.
507 */
crazyboblee0baa9fc2007-01-31 02:38:54 +0000508 public <I extends T> BindingBuilder<T> to(TypeLiteral<I> implementation) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000509 ensureImplementationIsNotSet();
crazyboblee4727ee22007-01-30 03:13:38 +0000510 validate(source, implementation.getRawType());
crazyboblee7289ac12007-02-01 00:28:09 +0000511 this.factory = new DefaultFactory<I>(key, implementation);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000512 setScopeFromType(implementation.getRawType());
513 return this;
514 }
515
516 private void setScopeFromType(Class<?> implementation) {
517 Scoped scoped = implementation.getAnnotation(Scoped.class);
518 if (scoped != null) {
519 in(scoped.value());
520 }
521 }
522
523 /**
524 * Binds to instances from the given factory.
525 */
526 public BindingBuilder<T> to(
527 final ContextualFactory<? extends T> factory) {
528 ensureImplementationIsNotSet();
529
crazyboblee7289ac12007-02-01 00:28:09 +0000530 this.factory = new InternalToContextualFactoryAdapter<T>(factory);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000531
532 return this;
533 }
534
535 /**
536 * Binds to instances from the given factory.
537 */
538 public BindingBuilder<T> to(final Factory<? extends T> factory) {
539 ensureImplementationIsNotSet();
crazyboblee7289ac12007-02-01 00:28:09 +0000540 this.factory = new InternalFactoryToFactoryAdapter<T>(factory);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000541 return this;
542 }
543
544 /**
545 * Binds to the given instance.
546 */
547 BindingBuilder<T> to(T instance) {
548 ensureImplementationIsNotSet();
549 this.factory = new ConstantFactory<T>(instance);
550 return this;
551 }
552
553 /**
554 * Binds to instances from the given factory.
555 */
556 BindingBuilder<T> to(final InternalFactory<? extends T> factory) {
557 ensureImplementationIsNotSet();
558 this.factory = factory;
559 return this;
560 }
561
562 /**
563 * Adds an error message if the implementation has already been bound.
564 */
565 private void ensureImplementationIsNotSet() {
566 if (factory != null) {
crazyboblee235d0682007-01-31 02:25:21 +0000567 addError(source, ErrorMessage.IMPLEMENTATION_ALREADY_SET);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000568 }
569 }
570
571 /**
crazyboblee63b592b2007-01-25 02:45:24 +0000572 * Specifies the scope. References the name passed to {@link
573 * ContainerBuilder#put(String, Scope)}.
crazyboblee7c5b2c42007-01-20 02:05:20 +0000574 */
crazyboblee63b592b2007-01-25 02:45:24 +0000575 public BindingBuilder<T> in(String scopeName) {
576 ensureScopeNotSet();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000577
crazyboblee63b592b2007-01-25 02:45:24 +0000578 // We could defer this lookup to when we create the container, but this
579 // is fine for now.
crazyboblee235d0682007-01-31 02:25:21 +0000580 this.scope = scopes.get(nonNull(scopeName, "scope name"));
crazyboblee63b592b2007-01-25 02:45:24 +0000581 if (this.scope == null) {
crazyboblee235d0682007-01-31 02:25:21 +0000582 addError(source, ErrorMessage.SCOPE_NOT_FOUND, scopeName,
583 scopes.keySet());
crazyboblee63b592b2007-01-25 02:45:24 +0000584 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000585 return this;
586 }
587
crazyboblee63b592b2007-01-25 02:45:24 +0000588 /**
589 * Specifies the scope.
590 */
591 public BindingBuilder<T> in(Scope scope) {
592 ensureScopeNotSet();
593
594 this.scope = nonNull(scope, "scope");
595 return this;
596 }
597
598 private void ensureScopeNotSet() {
599 if (this.scope != null) {
crazyboblee235d0682007-01-31 02:25:21 +0000600 addError(source, ErrorMessage.SCOPE_ALREADY_SET);
crazyboblee63b592b2007-01-25 02:45:24 +0000601 }
602 }
603
604 InternalFactory<? extends T> getInternalFactory(
605 final ContainerImpl container) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000606 // If an implementation wasn't specified, use the injection type.
607 if (this.factory == null) {
crazyboblee0baa9fc2007-01-31 02:38:54 +0000608 to(key.getType());
crazyboblee7c5b2c42007-01-20 02:05:20 +0000609 }
610
crazyboblee63b592b2007-01-25 02:45:24 +0000611 if (scope == null || scope == DEFAULT_SCOPE) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000612 return this.factory;
613 }
614
crazyboblee7289ac12007-02-01 00:28:09 +0000615 Factory<T> scoped = scope.scope(this.key,
616 new FactoryToInternalFactoryAdapter<T>(container, this.factory));
617 return new InternalFactoryToFactoryAdapter<T>(scoped);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000618 }
619
crazyboblee235d0682007-01-31 02:25:21 +0000620 boolean isInContainerScope() {
621 return this.scope == ContainerScope.INSTANCE;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000622 }
crazyboblee7289ac12007-02-01 00:28:09 +0000623 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000624
crazyboblee7289ac12007-02-01 00:28:09 +0000625 /**
626 * Injects new instances of the specified implementation class.
627 */
628 private static class DefaultFactory<T> implements InternalFactory<T> {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000629
crazyboblee7289ac12007-02-01 00:28:09 +0000630 volatile ContainerImpl.ConstructorInjector<T> constructor;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000631
crazyboblee7289ac12007-02-01 00:28:09 +0000632 private final TypeLiteral<T> implementation;
633 private final Key<? super T> key;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000634
crazyboblee7289ac12007-02-01 00:28:09 +0000635 public DefaultFactory(Key<? super T> key, TypeLiteral<T> implementation) {
636 this.key = key;
637 this.implementation = implementation;
638 }
639
640 @SuppressWarnings("unchecked")
641 public T get(InternalContext context) {
642 if (constructor == null) {
643 // This unnecessary cast is a workaround for an annoying compiler
644 // bug I keep running into.
645 Object c = context.getContainerImpl().getConstructor(implementation);
646 this.constructor = (ContainerImpl.ConstructorInjector<T>) c;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000647 }
crazyboblee7289ac12007-02-01 00:28:09 +0000648 return (T) constructor.construct(context, key.getRawType());
649 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000650
crazyboblee7289ac12007-02-01 00:28:09 +0000651 public String toString() {
652 return implementation.toString();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000653 }
654 }
655
656 /**
657 * Builds a constant binding.
658 */
crazyboblee63b592b2007-01-25 02:45:24 +0000659 public class ConstantBindingBuilder {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000660
661 final String name;
662 Class<?> type;
663 Object value;
664 Object source = ContainerBuilder.UNKNOWN_SOURCE;
665
666 ConstantBindingBuilder(String name) {
667 this.name = name;
668 }
669
670 Key<?> getKey() {
671 return Key.get(type, name);
672 }
673
674 boolean hasValue() {
675 return type != null;
676 }
677
678 Object getSource() {
679 return source;
680 }
681
682 InternalFactory<?> getInternalFactory() {
683 return new ConstantFactory<Object>(value);
684 }
685
crazyboblee63b592b2007-01-25 02:45:24 +0000686 ConstantBindingBuilder from(Object source) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000687 this.source = source;
688 return this;
689 }
690
691 /**
692 * Binds constant to the given value.
693 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000694 public void to(String value) {
695 to(String.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000696 }
697
698 /**
699 * Binds constant to the given value.
700 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000701 public void to(int value) {
702 to(int.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000703 }
704
705 /**
706 * Binds constant to the given value.
707 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000708 public void to(long value) {
709 to(long.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000710 }
711
712 /**
713 * Binds constant to the given value.
714 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000715 public void to(boolean value) {
716 to(boolean.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000717 }
718
719 /**
720 * Binds constant to the given value.
721 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000722 public void to(double value) {
723 to(double.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000724 }
725
726 /**
727 * Binds constant to the given value.
728 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000729 public void to(float value) {
730 to(float.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000731 }
732
733 /**
734 * Binds constant to the given value.
735 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000736 public void to(short value) {
737 to(short.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000738 }
739
740 /**
741 * Binds constant to the given value.
742 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000743 public void to(char value) {
744 to(char.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000745 }
746
747 /**
748 * Binds constant to the given value.
749 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000750 public void to(Class<?> value) {
751 to(Class.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000752 }
753
754 /**
755 * Binds constant to the given value.
756 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000757 public <E extends Enum<E>> void to(E value) {
758 to(value.getDeclaringClass(), value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000759 }
760
761 /**
762 * Maps a constant value to the given type and name.
763 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000764 <T> void to(final Class<T> type, final T value) {
crazyboblee235d0682007-01-31 02:25:21 +0000765 if (this.value != null) {
766 addError(source, ErrorMessage.CONSTANT_VALUE_ALREADY_SET);
767 } else {
768 this.type = type;
769 this.value = value;
770 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000771 }
772 }
773
774 /**
775 * Links one binding to another.
776 */
crazyboblee63b592b2007-01-25 02:45:24 +0000777 public class LinkedBindingBuilder<T> {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000778
779 Key<T> key;
780 Key<? extends T> destination;
781 Object source = ContainerBuilder.UNKNOWN_SOURCE;
782
783 LinkedBindingBuilder(Key<T> key) {
784 this.key = key;
785 }
786
787 Object getSource() {
788 return source;
789 }
790
791 Key<T> getKey() {
792 return key;
793 }
794
795 Key<? extends T> getDestination() {
796 return destination;
797 }
798
crazyboblee63b592b2007-01-25 02:45:24 +0000799 LinkedBindingBuilder<T> from(Object source) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000800 this.source = source;
801 return this;
802 }
803
804 /**
805 * Links to another binding with the given key.
806 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000807 public void to(Key<? extends T> destination) {
crazyboblee235d0682007-01-31 02:25:21 +0000808 if (this.destination != null) {
809 addError(source, ErrorMessage.LINK_DESTINATION_ALREADY_SET);
810 } else {
811 this.destination = destination;
812 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000813 }
814 }
crazyboblee4727ee22007-01-30 03:13:38 +0000815
crazyboblee235d0682007-01-31 02:25:21 +0000816 /**
817 * Handles errors up until we successfully create the container.
818 */
819 class ConfigurationErrorHandler extends AbstractErrorHandler {
crazyboblee4727ee22007-01-30 03:13:38 +0000820
821 final Object source;
822
823 ConfigurationErrorHandler(Object source) {
824 this.source = source;
825 }
826
827 public void handle(String message) {
828 add(new ErrorMessage(source, message));
829 }
830
831 public void handle(Throwable t) {
832 add(new ErrorMessage(source, t.getMessage()));
833 }
834 }
835
crazyboblee235d0682007-01-31 02:25:21 +0000836 /**
837 * Handles errors after the container is created.
838 */
839 class RuntimeErrorHandler extends AbstractErrorHandler {
crazyboblee4727ee22007-01-30 03:13:38 +0000840
841 public void handle(String message) {
842 throw new ConfigurationException(message);
843 }
844
845 public void handle(Throwable t) {
846 throw new ConfigurationException(t);
847 }
848 }
crazyboblee235d0682007-01-31 02:25:21 +0000849
850 final Set<String> skippedClassNames = new HashSet<String>(Arrays.asList(
851 ContainerBuilder.class.getName(),
852 AbstractModule.class.getName()
853 ));
854
855 /**
856 * Instructs the builder to skip the given class in the stack trace when
857 * determining the source of a binding. Use this to keep the container
858 * builder from logging utility methods as the sources of bindings (i.e.
859 * it will skip to the utility methods' callers instead).
860 *
861 * <p>Skipping only takes place after this method is called.
862 */
863 void skipSource(Class<?> clazz) {
864 skippedClassNames.add(clazz.getName());
865 }
866
867 /**
868 * Creates an object pointing to the current location within the
869 * configuration. If we run into a problem later, we'll be able to trace it
870 * back to the original source. Useful for debugging. The default
871 * implementation returns {@code ContainerBuilder}'s caller's {@code
872 * StackTraceElement}.
873 */
874 Object source() {
875 // Search up the stack until we find a class outside of this one.
876 for (final StackTraceElement element : new Throwable().getStackTrace()) {
877 String className = element.getClassName();
878 if (!skippedClassNames.contains(className)) {
879 return element;
880 }
881 }
882 throw new AssertionError();
883 }
crazybobleeef83bd22007-02-01 00:52:57 +0000884
885 /**
886 * A requested static injection.
887 */
888 class StaticInjection {
889
890 final Object source;
891 final Class<?>[] types;
892 final List<ContainerImpl.Injector> injectors =
893 new ArrayList<ContainerImpl.Injector>();
894
895 public StaticInjection(Object source, Class<?>[] types) {
896 this.source = source;
897 this.types = types;
898 }
899
900 void createInjectors(final ContainerImpl container) {
901 container.withErrorHandler(new ConfigurationErrorHandler(source),
902 new Runnable() {
903 public void run() {
904 for (Class<?> clazz : types) {
905 container.addInjectorsForFields(
906 clazz.getDeclaredFields(), true, injectors);
907 container.addInjectorsForMethods(
908 clazz.getDeclaredMethods(), true, injectors);
909 }
910 }
911 });
912 }
913
914 void runInjectors(ContainerImpl container) {
915 container.callInContext(new ContainerImpl.ContextualCallable<Void>() {
916 public Void call(InternalContext context) {
917 for (ContainerImpl.Injector injector : injectors) {
918 injector.inject(context, null);
919 }
920 return null;
921 }
922 });
923 }
924 }
crazyboblee66b415a2006-08-25 02:01:19 +0000925}