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