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