blob: 419b0bb350e8c4f245254865922eff4a572b6361 [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
19import java.lang.reflect.Member;
20import java.util.ArrayList;
21import java.util.Arrays;
22import java.util.HashMap;
23import java.util.LinkedHashMap;
24import java.util.List;
25import java.util.Map;
26import java.util.logging.Logger;
27
28/**
29 * Builds a dependency injection {@link Container}. The combination of
30 * dependency type and name uniquely identifies a dependency mapping; you can
31 * use the same name for two different types. Not safe for concurrent use.
32 *
33 * <p>Adds the following factories by default:
34 *
35 * <ul>
36 * <li>Injects the current {@link Container}.
37 * <li>Injects the {@link Logger} for the injected member's declaring class.
38 * </ul>
39 *
40 * @author crazybob@google.com (Bob Lee)
41 */
42public final class ContainerBuilder {
43
44 final Map<Key<?>, InternalFactory<?>> factories =
45 new HashMap<Key<?>, InternalFactory<?>>();
46 final List<InternalFactory<?>> singletonFactories =
47 new ArrayList<InternalFactory<?>>();
48 final List<Class<?>> staticInjections = new ArrayList<Class<?>>();
49 boolean created;
50
51 private static final InternalFactory<Container> CONTAINER_FACTORY =
52 new InternalFactory<Container>() {
53 public Container create(InternalContext context) {
54 return context.getContainer();
55 }
56 };
57
58 private static final InternalFactory<Logger> LOGGER_FACTORY =
59 new InternalFactory<Logger>() {
60 public Logger create(InternalContext context) {
61 Member member = context.getExternalContext().getMember();
62 return member == null ? Logger.getAnonymousLogger()
63 : Logger.getLogger(member.getDeclaringClass().getName());
64 }
65 };
66
67 /**
68 * Constructs a new builder.
69 */
70 public ContainerBuilder() {
71 // In the current container as the default Container implementation.
72 factories.put(Key.newInstance(Container.class, Container.DEFAULT_NAME),
73 CONTAINER_FACTORY);
74
75 // Inject the logger for the injected member's declaring class.
76 factories.put(Key.newInstance(Logger.class, Container.DEFAULT_NAME),
77 LOGGER_FACTORY);
78 }
79
80 /**
81 * Maps a dependency. All methods in this class ultimately funnel through
82 * here.
83 */
84 private <T> ContainerBuilder factory(final Key<T> key,
85 InternalFactory<? extends T> factory, Scope scope) {
86 ensureNotCreated();
87 checkKey(key);
88 final InternalFactory<? extends T> scopedFactory =
89 scope.scopeFactory(key.getType(), key.getName(), factory);
90 factories.put(key, scopedFactory);
91 if (scope == Scope.SINGLETON) {
92 singletonFactories.add(new InternalFactory<T>() {
93 public T create(InternalContext context) {
94 try {
95 context.setExternalContext(ExternalContext.newInstance(
96 null, key, context.getContainerImpl()));
97 return scopedFactory.create(context);
98 } finally {
99 context.setExternalContext(null);
100 }
101 }
102 });
103 }
104 return this;
105 }
106
107 /**
108 * Ensures a key isn't already mapped.
109 */
110 private void checkKey(Key<?> key) {
111 if (factories.containsKey(key)) {
112 throw new DependencyException(
113 "Dependency mapping for " + key + " already exists.");
114 }
115 }
116
117 /**
118 * Maps a factory to a given dependency type and name.
119 *
120 * @param type of dependency
121 * @param name of dependency
122 * @param factory creates objects to inject
123 * @param scope scope of injected instances
124 * @return this builder
125 */
126 public <T> ContainerBuilder factory(final Class<T> type, final String name,
127 final Factory<? extends T> factory, Scope scope) {
128 InternalFactory<T> internalFactory =
129 new InternalFactory<T>() {
130
131 public T create(InternalContext context) {
132 try {
133 Context externalContext = context.getExternalContext();
134 return factory.create(externalContext);
135 } catch (Exception e) {
136 throw new RuntimeException(e);
137 }
138 }
139
140 public String toString() {
141 return new LinkedHashMap<String, Object>() {{
142 put("type", type);
143 put("name", name);
144 put("factory", factory);
145 }}.toString();
146 }
147 };
148
149 return factory(Key.newInstance(type, name), internalFactory, scope);
150 }
151
152 /**
153 * Convenience method.&nbsp;Equivalent to {@code factory(type,
154 * Container.DEFAULT_NAME, factory, scope)}.
155 *
156 * @see #factory(Class, String, Factory, Scope)
157 */
158 public <T> ContainerBuilder factory(Class<T> type,
159 Factory<? extends T> factory, Scope scope) {
160 return factory(type, Container.DEFAULT_NAME, factory, scope);
161 }
162
163 /**
164 * Convenience method.&nbsp;Equivalent to {@code factory(type, name, factory,
165 * Scope.DEFAULT)}.
166 *
167 * @see #factory(Class, String, Factory, Scope)
168 */
169 public <T> ContainerBuilder factory(Class<T> type, String name,
170 Factory<? extends T> factory) {
171 return factory(type, name, factory, Scope.DEFAULT);
172 }
173
174 /**
175 * Convenience method.&nbsp;Equivalent to {@code factory(type,
176 * Container.DEFAULT_NAME, factory, Scope.DEFAULT)}.
177 *
178 * @see #factory(Class, String, Factory, Scope)
179 */
180 public <T> ContainerBuilder factory(Class<T> type,
181 Factory<? extends T> factory) {
182 return factory(type, Container.DEFAULT_NAME, factory, Scope.DEFAULT);
183 }
184
185 /**
186 * Maps an implementation class to a given dependency type and name. Creates
187 * instances using the container, recursively injecting dependencies.
188 *
189 * @param type of dependency
190 * @param name of dependency
191 * @param implementation class
192 * @param scope scope of injected instances
193 * @return this builder
194 */
195 public <T> ContainerBuilder factory(final Class<T> type, final String name,
196 final Class<? extends T> implementation, final Scope scope) {
197 // This factory creates new instances of the given implementation.
198 // We have to lazy load the constructor because the Container
199 // hasn't been created yet.
200 InternalFactory<? extends T> factory = new InternalFactory<T>() {
201
202 volatile ContainerImpl.ConstructorInjector<? extends T> constructor;
203
204 @SuppressWarnings("unchecked")
205 public T create(InternalContext context) {
206 if (constructor == null) {
207 this.constructor =
208 context.getContainerImpl().getConstructor(implementation);
209 }
210 return (T) constructor.construct(context, type);
211 }
212
213 public String toString() {
214 return new LinkedHashMap<String, Object>() {{
215 put("type", type);
216 put("name", name);
217 put("implementation", implementation);
218 put("scope", scope);
219 }}.toString();
220 }
221 };
222
223 return factory(Key.newInstance(type, name), factory, scope);
224 }
225
226 /**
227 * Maps an implementation class to a given dependency type and name. Creates
228 * instances using the container, recursively injecting dependencies.
229 *
230 * <p>Sets scope to value from {@link Scoped} annotation on the
231 * implementation class. Defaults to {@link Scope#DEFAULT} if no annotation
232 * is found.
233 *
234 * @param type of dependency
235 * @param name of dependency
236 * @param implementation class
237 * @return this builder
238 */
239 public <T> ContainerBuilder factory(final Class<T> type, String name,
240 final Class<? extends T> implementation) {
241 Scoped scoped = implementation.getAnnotation(Scoped.class);
242 Scope scope = scoped == null ? Scope.DEFAULT : scoped.value();
243 return factory(type, name, implementation, scope);
244 }
245
246 /**
247 * Convenience method.&nbsp;Equivalent to {@code factory(type,
248 * Container.DEFAULT_NAME, implementation)}.
249 *
250 * @see #factory(Class, String, Class)
251 */
252 public <T> ContainerBuilder factory(Class<T> type,
253 Class<? extends T> implementation) {
254 return factory(type, Container.DEFAULT_NAME, implementation);
255 }
256
257 /**
258 * Convenience method.&nbsp;Equivalent to {@code factory(type,
259 * Container.DEFAULT_NAME, type)}.
260 *
261 * @see #factory(Class, String, Class)
262 */
263 public <T> ContainerBuilder factory(Class<T> type) {
264 return factory(type, Container.DEFAULT_NAME, type);
265 }
266
267 /**
268 * Convenience method.&nbsp;Equivalent to {@code factory(type, name, type)}.
269 *
270 * @see #factory(Class, String, Class)
271 */
272 public <T> ContainerBuilder factory(Class<T> type, String name) {
273 return factory(type, name, type);
274 }
275
276 /**
277 * Convenience method.&nbsp;Equivalent to {@code factory(type,
278 * Container.DEFAULT_NAME, implementation, scope)}.
279 *
280 * @see #factory(Class, String, Class, Scope)
281 */
282 public <T> ContainerBuilder factory(Class<T> type,
283 Class<? extends T> implementation, Scope scope) {
284 return factory(type, Container.DEFAULT_NAME, implementation, scope);
285 }
286
287 /**
288 * Convenience method.&nbsp;Equivalent to {@code factory(type,
289 * Container.DEFAULT_NAME, type, scope)}.
290 *
291 * @see #factory(Class, String, Class, Scope)
292 */
293 public <T> ContainerBuilder factory(Class<T> type, Scope scope) {
294 return factory(type, Container.DEFAULT_NAME, type, scope);
295 }
296
297 /**
298 * Convenience method.&nbsp;Equivalent to {@code factory(type, name, type,
299 * scope)}.
300 *
301 * @see #factory(Class, String, Class, Scope)
302 */
303 public <T> ContainerBuilder factory(Class<T> type, String name, Scope scope) {
304 return factory(type, name, type, scope);
305 }
306
307 /**
donald.brown263c5bc2006-11-16 18:39:55 +0000308 * Convenience method.&nbsp;Equivalent to {@code alias(type, Container.DEFAULT_NAME,
309 * type)}.
310 *
311 * @see #alias(Class, String, String)
312 */
313 public <T> ContainerBuilder alias(Class<T> type, String alias) {
314 return alias(type, Container.DEFAULT_NAME, alias);
315 }
316
317 /**
318 * Maps an existing factory to a new name.
319 *
320 * @param type of dependency
321 * @param name of dependency
322 * @param alias of to the dependency
323 * @return this builder
324 */
325 public <T> ContainerBuilder alias(Class<T> type, String name, String alias) {
326 return alias(Key.newInstance(type, name), Key.newInstance(type, alias));
327 }
328
329 /**
330 * Maps an existing dependency. All methods in this class ultimately funnel through
331 * here.
332 */
333 private <T> ContainerBuilder alias(final Key<T> key,
334 final Key<T> aliasKey) {
335 ensureNotCreated();
336 checkKey(aliasKey);
337
338 final InternalFactory<? extends T> scopedFactory =
339 (InternalFactory<? extends T>)factories.get(key);
340 if (scopedFactory == null) {
341 throw new DependencyException(
342 "Dependency mapping for " + key + " doesn't exists.");
343 }
344 factories.put(aliasKey, scopedFactory);
345 return this;
346 }
347
348 /**
crazyboblee66b415a2006-08-25 02:01:19 +0000349 * Maps a constant value to the given name.
350 */
351 public ContainerBuilder constant(String name, String value) {
352 return constant(String.class, name, value);
353 }
354
355 /**
356 * Maps a constant value to the given name.
357 */
358 public ContainerBuilder constant(String name, int value) {
359 return constant(int.class, name, value);
360 }
361
362 /**
363 * Maps a constant value to the given name.
364 */
365 public ContainerBuilder constant(String name, long value) {
366 return constant(long.class, name, value);
367 }
368
369 /**
370 * Maps a constant value to the given name.
371 */
372 public ContainerBuilder constant(String name, boolean value) {
373 return constant(boolean.class, name, value);
374 }
375
376 /**
377 * Maps a constant value to the given name.
378 */
379 public ContainerBuilder constant(String name, double value) {
380 return constant(double.class, name, value);
381 }
382
383 /**
384 * Maps a constant value to the given name.
385 */
386 public ContainerBuilder constant(String name, float value) {
387 return constant(float.class, name, value);
388 }
389
390 /**
391 * Maps a constant value to the given name.
392 */
393 public ContainerBuilder constant(String name, short value) {
394 return constant(short.class, name, value);
395 }
396
397 /**
398 * Maps a constant value to the given name.
399 */
400 public ContainerBuilder constant(String name, char value) {
401 return constant(char.class, name, value);
402 }
403
404 /**
405 * Maps a class to the given name.
406 */
407 public ContainerBuilder constant(String name, Class value) {
408 return constant(Class.class, name, value);
409 }
410
411 /**
412 * Maps an enum to the given name.
413 */
414 public <E extends Enum<E>> ContainerBuilder constant(String name, E value) {
415 return constant(value.getDeclaringClass(), name, value);
416 }
417
418 /**
419 * Maps a constant value to the given type and name.
420 */
421 private <T> ContainerBuilder constant(final Class<T> type, final String name,
422 final T value) {
423 InternalFactory<T> factory = new InternalFactory<T>() {
424 public T create(InternalContext ignored) {
425 return value;
426 }
427
428 public String toString() {
429 return new LinkedHashMap<String, Object>() {
430 {
431 put("type", type);
432 put("name", name);
433 put("value", value);
434 }
435 }.toString();
436 }
437 };
438
439 return factory(Key.newInstance(type, name), factory, Scope.DEFAULT);
440 }
441
442 /**
443 * Upon creation, the {@link Container} will inject static fields and methods
444 * into the given classes.
445 *
446 * @param types for which static members will be injected
447 */
448 public ContainerBuilder injectStatics(Class<?>... types) {
449 staticInjections.addAll(Arrays.asList(types));
450 return this;
451 }
452
453 /**
454 * Returns true if this builder contains a mapping for the given type and
455 * name.
456 */
457 public boolean contains(Class<?> type, String name) {
458 return factories.containsKey(Key.newInstance(type, name));
459 }
460
461 /**
462 * Convenience method.&nbsp;Equivalent to {@code contains(type,
463 * Container.DEFAULT_NAME)}.
464 */
465 public boolean contains(Class<?> type) {
466 return contains(type, Container.DEFAULT_NAME);
467 }
468
469 /**
470 * Creates a {@link Container} instance. Injects static members for classes
471 * which were registered using {@link #injectStatics(Class...)}.
472 *
473 * @param loadSingletons If true, the container will load all singletons
474 * now. If false, the container will lazily load singletons. Eager loading
475 * is appropriate for production use while lazy loading can speed
476 * development.
477 * @throws IllegalStateException if called more than once
478 */
479 public Container create(boolean loadSingletons) {
480 ensureNotCreated();
481 created = true;
482 final ContainerImpl container = new ContainerImpl(
483 new HashMap<Key<?>, InternalFactory<?>>(factories));
484 if (loadSingletons) {
485 container.callInContext(new ContainerImpl.ContextualCallable<Void>() {
486 public Void call(InternalContext context) {
487 for (InternalFactory<?> factory : singletonFactories) {
488 factory.create(context);
489 }
490 return null;
491 }
492 });
493 }
494 container.injectStatics(staticInjections);
495 return container;
496 }
497
498 /**
499 * Currently we only support creating one Container instance per builder.
500 * If we want to support creating more than one container per builder,
501 * we should move to a "factory factory" model where we create a factory
502 * instance per Container. Right now, one factory instance would be
503 * shared across all the containers, singletons synchronize on the
504 * container when lazy loading, etc.
505 */
506 private void ensureNotCreated() {
507 if (created) {
508 throw new IllegalStateException("Container already created.");
509 }
510 }
511
512 /**
513 * Implemented by classes which participate in building a container.
514 */
515 public interface Command {
516
517 /**
518 * Contributes factories to the given builder.
519 *
520 * @param builder
521 */
522 void build(ContainerBuilder builder);
523 }
524}