001 /*****************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved. *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file. *
007 * *
008 * Original code by *
009 *****************************************************************************/
010 package org.picocontainer;
011
012 import org.picocontainer.adapters.AbstractAdapter;
013 import org.picocontainer.adapters.InstanceAdapter;
014 import org.picocontainer.behaviors.AbstractBehaviorFactory;
015 import org.picocontainer.behaviors.AdaptingBehavior;
016 import org.picocontainer.behaviors.Cached;
017 import org.picocontainer.behaviors.Caching;
018 import org.picocontainer.behaviors.HiddenImplementation;
019 import org.picocontainer.containers.AbstractDelegatingMutablePicoContainer;
020 import org.picocontainer.containers.AbstractDelegatingPicoContainer;
021 import org.picocontainer.containers.EmptyPicoContainer;
022 import org.picocontainer.containers.ImmutablePicoContainer;
023 import org.picocontainer.converters.BuiltInConverters;
024 import org.picocontainer.converters.ConvertsNothing;
025 import org.picocontainer.injectors.AbstractInjector;
026 import org.picocontainer.injectors.AdaptingInjection;
027 import org.picocontainer.injectors.FactoryInjector;
028 import org.picocontainer.lifecycle.DefaultLifecycleState;
029 import org.picocontainer.lifecycle.LifecycleState;
030 import org.picocontainer.lifecycle.StartableLifecycleStrategy;
031 import org.picocontainer.monitors.NullComponentMonitor;
032 import org.picocontainer.parameters.DefaultConstructorParameter;
033
034 import java.io.Serializable;
035 import java.lang.annotation.Annotation;
036 import java.lang.ref.WeakReference;
037 import java.lang.reflect.Type;
038 import java.util.ArrayList;
039 import java.util.Collection;
040 import java.util.Collections;
041 import java.util.Enumeration;
042 import java.util.HashMap;
043 import java.util.HashSet;
044 import java.util.List;
045 import java.util.Map;
046 import java.util.Properties;
047 import java.util.Set;
048
049 /**
050 * <p/>
051 * The Standard {@link PicoContainer}/{@link MutablePicoContainer} implementation.
052 * Constructing a container c with a parent p container will cause c to look up components
053 * in p if they cannot be found inside c itself.
054 * </p>
055 * <p/>
056 * Using {@link Class} objects as keys to the various registerXXX() methods makes
057 * a subtle semantic difference:
058 * </p>
059 * <p/>
060 * If there are more than one registered components of the same type and one of them are
061 * registered with a {@link java.lang.Class} key of the corresponding type, this addComponent
062 * will take precedence over other components during type resolution.
063 * </p>
064 * <p/>
065 * Another place where keys that are classes make a subtle difference is in
066 * {@link HiddenImplementation}.
067 * </p>
068 * <p/>
069 * This implementation of {@link MutablePicoContainer} also supports
070 * {@link ComponentMonitorStrategy}.
071 * </p>
072 *
073 * @author Paul Hammant
074 * @author Aslak Hellesøy
075 * @author Jon Tirsén
076 * @author Thomas Heller
077 * @author Mauro Talevi
078 */
079 @SuppressWarnings("serial")
080 public class DefaultPicoContainer implements MutablePicoContainer, Converting, ComponentMonitorStrategy, Serializable {
081
082 private String name;
083
084 /**
085 * Component factory instance.
086 */
087 protected final ComponentFactory componentFactory;
088
089 /**
090 * Parent picocontainer
091 */
092 private PicoContainer parent;
093
094 /**
095 * All picocontainer children.
096 */
097 private final Set<PicoContainer> children = new HashSet<PicoContainer>();
098
099 /**
100 * Current state of the container.
101 */
102 private LifecycleState lifecycleState = new DefaultLifecycleState();
103
104 /**
105 * Keeps track of child containers started status.
106 */
107 private final Set<WeakReference<PicoContainer>> childrenStarted = new HashSet<WeakReference<PicoContainer>>();
108
109 /**
110 * Lifecycle strategy instance.
111 */
112 protected final LifecycleStrategy lifecycleStrategy;
113
114 /**
115 * Properties set at the container level, that will affect subsequent components added.
116 */
117 private final Properties containerProperties = new Properties();
118
119 /**
120 * Component monitor instance. Receives event callbacks.
121 */
122 protected ComponentMonitor componentMonitor;
123
124 /**
125 * Map used for looking up component adapters by their key.
126 */
127 private final Map<Object, ComponentAdapter<?>> componentKeyToAdapterCache = new HashMap<Object, ComponentAdapter<?> >();
128
129
130 private final List<ComponentAdapter<?>> componentAdapters = new ArrayList<ComponentAdapter<?>>();
131
132
133 protected final List<ComponentAdapter<?>> orderedComponentAdapters = new ArrayList<ComponentAdapter<?>>();
134
135
136 private transient IntoThreadLocal intoThreadLocal = new IntoThreadLocal();
137 private Converters converters;
138
139
140 /**
141 * Creates a new container with a custom ComponentFactory and a parent container.
142 * <p/>
143 * <em>
144 * Important note about caching: If you intend the components to be cached, you should pass
145 * in a factory that creates {@link Cached} instances, such as for example
146 * {@link Caching}. Caching can delegate to
147 * other ComponentAdapterFactories.
148 * </em>
149 *
150 * @param componentFactory the factory to use for creation of ComponentAdapters.
151 * @param parent the parent container (used for component dependency lookups).
152 */
153 public DefaultPicoContainer(final ComponentFactory componentFactory, final PicoContainer parent) {
154 this(componentFactory, new StartableLifecycleStrategy(new NullComponentMonitor()), parent, new NullComponentMonitor());
155 }
156
157 /**
158 * Creates a new container with a custom ComponentFactory, LifecycleStrategy for instance registration,
159 * and a parent container.
160 * <p/>
161 * <em>
162 * Important note about caching: If you intend the components to be cached, you should pass
163 * in a factory that creates {@link Cached} instances, such as for example
164 * {@link Caching}. Caching can delegate to
165 * other ComponentAdapterFactories.
166 * </em>
167 *
168 * @param componentFactory the factory to use for creation of ComponentAdapters.
169 * @param lifecycleStrategy
170 * the lifecycle strategy chosen for registered
171 * instance (not implementations!)
172 * @param parent the parent container (used for component dependency lookups).
173 */
174 public DefaultPicoContainer(final ComponentFactory componentFactory,
175 final LifecycleStrategy lifecycleStrategy,
176 final PicoContainer parent) {
177 this(componentFactory, lifecycleStrategy, parent, new NullComponentMonitor() );
178 }
179
180 public DefaultPicoContainer(final ComponentFactory componentFactory,
181 final LifecycleStrategy lifecycleStrategy,
182 final PicoContainer parent, final ComponentMonitor componentMonitor) {
183 if (componentFactory == null) {
184 throw new NullPointerException("componentFactory");
185 }
186 if (lifecycleStrategy == null) {
187 throw new NullPointerException("lifecycleStrategy");
188 }
189 this.componentFactory = componentFactory;
190 this.lifecycleStrategy = lifecycleStrategy;
191 this.parent = parent;
192 if (parent != null && !(parent instanceof EmptyPicoContainer)) {
193 this.parent = new ImmutablePicoContainer(parent);
194 }
195 this.componentMonitor = componentMonitor;
196 }
197
198 /**
199 * Creates a new container with the AdaptingInjection using a
200 * custom ComponentMonitor
201 *
202 * @param monitor the ComponentMonitor to use
203 * @param parent the parent container (used for component dependency lookups).
204 */
205 public DefaultPicoContainer(final ComponentMonitor monitor, final PicoContainer parent) {
206 this(new AdaptingBehavior(), new StartableLifecycleStrategy(monitor), parent, monitor);
207 }
208
209 /**
210 * Creates a new container with the AdaptingInjection using a
211 * custom ComponentMonitor and lifecycle strategy
212 *
213 * @param monitor the ComponentMonitor to use
214 * @param lifecycleStrategy the lifecycle strategy to use.
215 * @param parent the parent container (used for component dependency lookups).
216 */
217 public DefaultPicoContainer(final ComponentMonitor monitor, final LifecycleStrategy lifecycleStrategy, final PicoContainer parent) {
218 this(new AdaptingBehavior(), lifecycleStrategy, parent, monitor);
219 }
220
221 /**
222 * Creates a new container with the AdaptingInjection using a
223 * custom lifecycle strategy
224 *
225 * @param lifecycleStrategy the lifecycle strategy to use.
226 * @param parent the parent container (used for component dependency lookups).
227 */
228 public DefaultPicoContainer(final LifecycleStrategy lifecycleStrategy, final PicoContainer parent) {
229 this(new NullComponentMonitor(), lifecycleStrategy, parent);
230 }
231
232
233 /**
234 * Creates a new container with a custom ComponentFactory and no parent container.
235 *
236 * @param componentFactory the ComponentFactory to use.
237 */
238 public DefaultPicoContainer(final ComponentFactory componentFactory) {
239 this(componentFactory, null);
240 }
241
242 /**
243 * Creates a new container with the AdaptingInjection using a
244 * custom ComponentMonitor
245 *
246 * @param monitor the ComponentMonitor to use
247 */
248 public DefaultPicoContainer(final ComponentMonitor monitor) {
249 this(monitor, new StartableLifecycleStrategy(monitor), null);
250 }
251
252 /**
253 * Creates a new container with a (caching) {@link AdaptingInjection}
254 * and a parent container.
255 *
256 * @param parent the parent container (used for component dependency lookups).
257 */
258 public DefaultPicoContainer(final PicoContainer parent) {
259 this(new AdaptingBehavior(), parent);
260 }
261
262 /** Creates a new container with a {@link AdaptingBehavior} and no parent container. */
263 public DefaultPicoContainer() {
264 this(new AdaptingBehavior(), null);
265 }
266
267 /** {@inheritDoc} **/
268 public Collection<ComponentAdapter<?>> getComponentAdapters() {
269 return Collections.unmodifiableList(getModifiableComponentAdapterList());
270 }
271
272
273 /** {@inheritDoc} **/
274 public final ComponentAdapter<?> getComponentAdapter(final Object componentKey) {
275 ComponentAdapter<?> adapter = getComponentKeyToAdapterCache().get(componentKey);
276 if (adapter == null && parent != null) {
277 adapter = getParent().getComponentAdapter(componentKey);
278 if (adapter != null) {
279 adapter = new KnowsContainerAdapter(adapter, getParent());
280 }
281 }
282 if (adapter == null) {
283 Object inst = componentMonitor.noComponentFound(this, componentKey);
284 if (inst != null) {
285 adapter = new LateInstance(componentKey, inst);
286 }
287 }
288 return adapter;
289 }
290
291 public static class LateInstance extends AbstractAdapter {
292 private final Object instance;
293 private LateInstance(Object componentKey, Object instance) {
294 super(componentKey, instance.getClass());
295 this.instance = instance;
296 }
297
298 public Object getComponentInstance() {
299 return instance;
300 }
301
302 public Object getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException {
303 return instance;
304 }
305
306 public void verify(PicoContainer container) throws PicoCompositionException {
307 }
308
309 public String getDescriptor() {
310 return "LateInstance";
311 }
312 }
313
314 public static class KnowsContainerAdapter<T> implements ComponentAdapter<T> {
315 private final ComponentAdapter<T> ca;
316 private final PicoContainer ctr;
317
318 public KnowsContainerAdapter(ComponentAdapter<T> ca, PicoContainer ctr) {
319 this.ca = ca;
320 this.ctr = ctr;
321 }
322
323 public T getComponentInstance(Type into) throws PicoCompositionException {
324 return getComponentInstance(ctr, into);
325 }
326
327 public Object getComponentKey() {
328 return ca.getComponentKey();
329 }
330
331 public Class getComponentImplementation() {
332 return ca.getComponentImplementation();
333 }
334
335 public T getComponentInstance(PicoContainer container) throws PicoCompositionException {
336 return ca.getComponentInstance(container);
337 }
338
339 public T getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException {
340 return ca.getComponentInstance(container, into);
341 }
342
343 public void verify(PicoContainer container) throws PicoCompositionException {
344 ca.verify(container);
345 }
346
347 public void accept(PicoVisitor visitor) {
348 ca.accept(visitor);
349 }
350
351 public ComponentAdapter getDelegate() {
352 return ca.getDelegate();
353 }
354
355 public <U extends ComponentAdapter> U findAdapterOfType(Class<U> adapterType) {
356 return ca.findAdapterOfType(adapterType);
357 }
358
359 public String getDescriptor() {
360 return null;
361 }
362 }
363
364 /** {@inheritDoc} **/
365 public <T> ComponentAdapter<T> getComponentAdapter(final Class<T> componentType, final NameBinding componentNameBinding) {
366 return getComponentAdapter(componentType, componentNameBinding, null);
367 }
368
369 /** {@inheritDoc} **/
370 private <T> ComponentAdapter<T> getComponentAdapter(final Class<T> componentType, final NameBinding componentNameBinding, final Class<? extends Annotation> binding) {
371 // See http://jira.codehaus.org/secure/ViewIssue.jspa?key=PICO-115
372 ComponentAdapter<?> adapterByKey = getComponentAdapter(componentType);
373 if (adapterByKey != null) {
374 return typeComponentAdapter(adapterByKey);
375 }
376
377 List<ComponentAdapter<T>> found = binding == null ? getComponentAdapters(componentType) : getComponentAdapters(componentType, binding);
378
379 if (found.size() == 1) {
380 return found.get(0);
381 } else if (found.isEmpty()) {
382 if (parent != null) {
383 return getParent().getComponentAdapter(componentType, componentNameBinding);
384 } else {
385 return null;
386 }
387 } else {
388 if (componentNameBinding != null) {
389 String parameterName = componentNameBinding.getName();
390 if (parameterName != null) {
391 ComponentAdapter<?> ca = getComponentAdapter(parameterName);
392 if (ca != null && componentType.isAssignableFrom(ca.getComponentImplementation())) {
393 return typeComponentAdapter(ca);
394 }
395 }
396 }
397 Class<?>[] foundClasses = new Class[found.size()];
398 for (int i = 0; i < foundClasses.length; i++) {
399 foundClasses[i] = found.get(i).getComponentImplementation();
400 }
401
402 throw new AbstractInjector.AmbiguousComponentResolutionException(componentType, foundClasses);
403 }
404 }
405
406 /** {@inheritDoc} **/
407 public <T> ComponentAdapter<T> getComponentAdapter(final Class<T> componentType, final Class<? extends Annotation> binding) {
408 // 1
409 return getComponentAdapter(componentType, null, binding);
410 }
411
412 /** {@inheritDoc} **/
413 public <T> List<ComponentAdapter<T>> getComponentAdapters(final Class<T> componentType) {
414 return getComponentAdapters(componentType, null);
415 }
416
417 /** {@inheritDoc} **/
418 public <T> List<ComponentAdapter<T>> getComponentAdapters(final Class<T> componentType, final Class<? extends Annotation> binding) {
419 if (componentType == null) {
420 return Collections.emptyList();
421 }
422 List<ComponentAdapter<T>> found = new ArrayList<ComponentAdapter<T>>();
423 for (ComponentAdapter<?> componentAdapter : getComponentAdapters()) {
424 Object k = componentAdapter.getComponentKey();
425
426 if (componentType.isAssignableFrom(componentAdapter.getComponentImplementation()) &&
427 (!(k instanceof BindKey) || (k instanceof BindKey && (((BindKey<?>)k).getAnnotation() == null || binding == null ||
428 ((BindKey<?>)k).getAnnotation() == binding)))) {
429 found.add((ComponentAdapter<T>)typeComponentAdapter(componentAdapter));
430 }
431 }
432 return found;
433 }
434
435 protected MutablePicoContainer addAdapterInternal(ComponentAdapter<?> componentAdapter) {
436 Object componentKey = componentAdapter.getComponentKey();
437 if (getComponentKeyToAdapterCache().containsKey(componentKey)) {
438 throw new PicoCompositionException("Duplicate Keys not allowed. Duplicate for '" + componentKey + "'");
439 }
440 getModifiableComponentAdapterList().add(componentAdapter);
441 getComponentKeyToAdapterCache().put(componentKey, componentAdapter);
442 return this;
443 }
444
445 /**
446 * {@inheritDoc}
447 * This method can be used to override the ComponentAdapter created by the {@link ComponentFactory}
448 * passed to the constructor of this container.
449 */
450 public MutablePicoContainer addAdapter(final ComponentAdapter<?> componentAdapter) {
451 return addAdapter(componentAdapter, this.containerProperties);
452 }
453
454 /** {@inheritDoc} **/
455 public MutablePicoContainer addAdapter(final ComponentAdapter<?> componentAdapter, final Properties properties) {
456 Properties tmpProperties = (Properties)properties.clone();
457 if (AbstractBehaviorFactory.removePropertiesIfPresent(tmpProperties, Characteristics.NONE) == false && componentFactory instanceof BehaviorFactory) {
458 MutablePicoContainer container = addAdapterInternal(((BehaviorFactory)componentFactory).addComponentAdapter(
459 componentMonitor,
460 lifecycleStrategy,
461 tmpProperties,
462 componentAdapter));
463 throwIfPropertiesLeft(tmpProperties);
464 return container;
465 } else {
466 return addAdapterInternal(componentAdapter);
467 }
468
469 }
470
471
472 /** {@inheritDoc} **/
473 public <T> ComponentAdapter<T> removeComponent(final Object componentKey) {
474 lifecycleState.removingComponent();
475
476 ComponentAdapter<T> adapter = (ComponentAdapter<T>) getComponentKeyToAdapterCache().remove(componentKey);
477 getModifiableComponentAdapterList().remove(adapter);
478 getOrderedComponentAdapters().remove(adapter);
479 return adapter;
480 }
481
482 /**
483 * {@inheritDoc}
484 * The returned ComponentAdapter will be an {@link org.picocontainer.adapters.InstanceAdapter}.
485 */
486 public MutablePicoContainer addComponent(final Object implOrInstance) {
487 return addComponent(implOrInstance, this.containerProperties);
488 }
489
490 private MutablePicoContainer addComponent(final Object implOrInstance, final Properties props) {
491 Class<?> clazz;
492 if (implOrInstance instanceof String) {
493 return addComponent(implOrInstance, implOrInstance);
494 }
495 if (implOrInstance instanceof Class) {
496 clazz = (Class<?>)implOrInstance;
497 } else {
498 clazz = implOrInstance.getClass();
499 }
500 return addComponent(clazz, implOrInstance, props);
501 }
502
503
504 public MutablePicoContainer addConfig(final String name, final Object val) {
505 return addAdapterInternal(new InstanceAdapter<Object>(name, val, lifecycleStrategy, componentMonitor));
506 }
507
508
509 /**
510 * {@inheritDoc}
511 * The returned ComponentAdapter will be instantiated by the {@link ComponentFactory}
512 * passed to the container's constructor.
513 */
514 public MutablePicoContainer addComponent(final Object componentKey,
515 final Object componentImplementationOrInstance,
516 final Parameter... parameters) {
517 return this.addComponent(componentKey, componentImplementationOrInstance, this.containerProperties, parameters);
518 }
519
520 private MutablePicoContainer addComponent(final Object componentKey,
521 final Object componentImplementationOrInstance,
522 final Properties properties,
523 Parameter... parameters) {
524 if (parameters != null && parameters.length == 0) {
525 parameters = null; // backwards compatibility! solve this better later - Paul
526 }
527
528 //New replacement for Parameter.ZERO.
529 if (parameters != null && parameters.length == 1 && DefaultConstructorParameter.INSTANCE.equals(parameters[0])) {
530 parameters = new Parameter[0];
531 }
532
533 if (componentImplementationOrInstance instanceof Class) {
534 Properties tmpProperties = (Properties) properties.clone();
535 ComponentAdapter<?> adapter = componentFactory.createComponentAdapter(componentMonitor,
536 lifecycleStrategy,
537 tmpProperties,
538 componentKey,
539 (Class<?>)componentImplementationOrInstance,
540 parameters);
541 AbstractBehaviorFactory.removePropertiesIfPresent(tmpProperties, Characteristics.USE_NAMES);
542 throwIfPropertiesLeft(tmpProperties);
543 if (lifecycleState.isStarted()) {
544 addAdapterIfStartable(adapter);
545 potentiallyStartAdapter(adapter);
546 }
547 return addAdapterInternal(adapter);
548 } else {
549 ComponentAdapter<?> adapter =
550 new InstanceAdapter<Object>(componentKey, componentImplementationOrInstance, lifecycleStrategy, componentMonitor);
551 if (lifecycleState.isStarted()) {
552 addAdapterIfStartable(adapter);
553 potentiallyStartAdapter(adapter);
554 }
555 return addAdapter(adapter, properties);
556 }
557 }
558
559 private void throwIfPropertiesLeft(final Properties tmpProperties) {
560 if(tmpProperties.size() > 0) {
561 throw new PicoCompositionException("Unprocessed Characteristics:" + tmpProperties +", please refer to http://picocontainer.org/help/unprocessed-properties-help.html");
562 }
563 }
564
565 private void addOrderedComponentAdapter(final ComponentAdapter<?> componentAdapter) {
566 if (!getOrderedComponentAdapters().contains(componentAdapter)) {
567 getOrderedComponentAdapters().add(componentAdapter);
568 }
569 }
570
571 public List<Object> getComponents() throws PicoException {
572 return getComponents(Object.class);
573 }
574
575 public <T> List<T> getComponents(final Class<T> componentType) {
576 if (componentType == null) {
577 return Collections.emptyList();
578 }
579
580 Map<ComponentAdapter<T>, T> adapterToInstanceMap = new HashMap<ComponentAdapter<T>, T>();
581 for (ComponentAdapter<?> componentAdapter : getModifiableComponentAdapterList()) {
582 if (componentType.isAssignableFrom(componentAdapter.getComponentImplementation())) {
583 ComponentAdapter<T> typedComponentAdapter = typeComponentAdapter(componentAdapter);
584 T componentInstance = getLocalInstance(typedComponentAdapter);
585
586 adapterToInstanceMap.put(typedComponentAdapter, componentInstance);
587 }
588 }
589 List<T> result = new ArrayList<T>();
590 for (ComponentAdapter<?> componentAdapter : getOrderedComponentAdapters()) {
591 final T componentInstance = adapterToInstanceMap.get(componentAdapter);
592 if (componentInstance != null) {
593 // may be null in the case of the "implicit" addAdapter
594 // representing "this".
595 result.add(componentInstance);
596 }
597 }
598 return result;
599 }
600
601 private <T> T getLocalInstance(final ComponentAdapter<T> typedComponentAdapter) {
602 T componentInstance = typedComponentAdapter.getComponentInstance(this, ComponentAdapter.NOTHING.class);
603
604 // This is to ensure all are added. (Indirect dependencies will be added
605 // from InstantiatingComponentAdapter).
606 addOrderedComponentAdapter(typedComponentAdapter);
607
608 return componentInstance;
609 }
610
611 @SuppressWarnings({ "unchecked" })
612 private static <T> ComponentAdapter<T> typeComponentAdapter(final ComponentAdapter<?> componentAdapter) {
613 return (ComponentAdapter<T>)componentAdapter;
614 }
615
616 public Object getComponent(final Object componentKeyOrType) {
617 return getComponent(componentKeyOrType, null);
618 }
619
620 public Object getComponent(final Object componentKeyOrType, Type into) {
621 synchronized (this) {
622 if (intoThreadLocal == null) {
623 intoThreadLocal = new IntoThreadLocal();
624 }
625 }
626 intoThreadLocal.set(into);
627 return getComponent(componentKeyOrType, (Class<? extends Annotation>) null);
628 }
629
630 public Object getComponent(final Object componentKeyOrType, final Class<? extends Annotation> annotation) {
631 ComponentAdapter<?> componentAdapter;
632 Object component;
633 if (annotation != null) {
634 componentAdapter = getComponentAdapter((Class<?>)componentKeyOrType, annotation);
635 component = componentAdapter == null ? null : getInstance(componentAdapter, null);
636 } else if (componentKeyOrType instanceof Class) {
637 componentAdapter = getComponentAdapter((Class<?>)componentKeyOrType, (NameBinding) null);
638 component = componentAdapter == null ? null : getInstance(componentAdapter, (Class<?>)componentKeyOrType);
639 } else {
640 componentAdapter = getComponentAdapter(componentKeyOrType);
641 component = componentAdapter == null ? null : getInstance(componentAdapter, null);
642 }
643 return decorateComponent(component, componentAdapter);
644 }
645
646 /**
647 * This is invoked when getComponent(..) is called. It allows extendees to decorate a
648 * component before it is returned to the caller.
649 * @param component the component that will be returned for getComponent(..)
650 * @param componentAdapter the component adapter that made that component
651 * @return the component (the same as that passed in by default)
652 */
653 protected Object decorateComponent(Object component, ComponentAdapter<?> componentAdapter) {
654 if (componentAdapter instanceof ComponentLifecycle<?>
655 && lifecycleStrategy.isLazy(componentAdapter) // is Lazy
656 && !((ComponentLifecycle<?>) componentAdapter).isStarted()) {
657 ((ComponentLifecycle<?>)componentAdapter).start(this);
658 }
659 return component;
660 }
661
662 public <T> T getComponent(final Class<T> componentType) {
663 Object o = getComponent((Object)componentType, null);
664 return componentType.cast(o);
665 }
666
667 public <T> T getComponent(final Class<T> componentType, final Class<? extends Annotation> binding) {
668 Object o = getComponent((Object)componentType, binding);
669 return componentType.cast(o);
670 }
671
672
673 private Object getInstance(final ComponentAdapter<?> componentAdapter, Class componentKey) {
674 // check whether this is our adapter
675 // we need to check this to ensure up-down dependencies cannot be followed
676 final boolean isLocal = getModifiableComponentAdapterList().contains(componentAdapter);
677
678 if (isLocal || componentAdapter instanceof LateInstance) {
679 Object instance;
680 try {
681 if (componentAdapter instanceof FactoryInjector) {
682 instance = ((FactoryInjector) componentAdapter).getComponentInstance(this, intoThreadLocal.get());
683 } else {
684 synchronized (this) {
685 if (intoThreadLocal == null) {
686 intoThreadLocal = new IntoThreadLocal();
687 }
688 }
689 instance = componentAdapter.getComponentInstance(this, intoThreadLocal.get());
690 }
691 } catch (AbstractInjector.CyclicDependencyException e) {
692 if (parent != null) {
693 instance = getParent().getComponent(componentAdapter.getComponentKey());
694 if (instance != null) {
695 return instance;
696 }
697 }
698 throw e;
699 }
700 addOrderedComponentAdapter(componentAdapter);
701
702 return instance;
703 } else if (parent != null) {
704 return getParent().getComponent(componentAdapter.getComponentKey());
705 }
706
707 return null;
708 }
709
710
711 /** {@inheritDoc} **/
712 public PicoContainer getParent() {
713 return parent;
714 }
715
716 /** {@inheritDoc} **/
717 public <T> ComponentAdapter<T> removeComponentByInstance(final T componentInstance) {
718 for (ComponentAdapter<?> componentAdapter : getModifiableComponentAdapterList()) {
719 if (getLocalInstance(componentAdapter).equals(componentInstance)) {
720 return removeComponent(componentAdapter.getComponentKey());
721 }
722 }
723 return null;
724 }
725
726 /**
727 * Start the components of this PicoContainer and all its logical child containers.
728 * The starting of the child container is only attempted if the parent
729 * container start successfully. The child container for which start is attempted
730 * is tracked so that upon stop, only those need to be stopped.
731 * The lifecycle operation is delegated to the component adapter,
732 * if it is an instance of {@link Behavior lifecycle manager}.
733 * The actual {@link LifecycleStrategy lifecycle strategy} supported
734 * depends on the concrete implementation of the adapter.
735 *
736 * @see Behavior
737 * @see LifecycleStrategy
738 * @see #makeChildContainer()
739 * @see #addChildContainer(PicoContainer)
740 * @see #removeChildContainer(PicoContainer)
741 */
742 public void start() {
743
744 lifecycleState.starting();
745
746 startAdapters();
747 childrenStarted.clear();
748 for (PicoContainer child : children) {
749 childrenStarted.add(new WeakReference<PicoContainer>(child));
750 if (child instanceof Startable) {
751 ((Startable)child).start();
752 }
753 }
754 }
755
756 /**
757 * Stop the components of this PicoContainer and all its logical child containers.
758 * The stopping of the child containers is only attempted for those that have been
759 * started, possibly not successfully.
760 * The lifecycle operation is delegated to the component adapter,
761 * if it is an instance of {@link Behavior lifecycle manager}.
762 * The actual {@link LifecycleStrategy lifecycle strategy} supported
763 * depends on the concrete implementation of the adapter.
764 *
765 * @see Behavior
766 * @see LifecycleStrategy
767 * @see #makeChildContainer()
768 * @see #addChildContainer(PicoContainer)
769 * @see #removeChildContainer(PicoContainer)
770 */
771 public void stop() {
772
773 lifecycleState.stopping();
774
775 for (PicoContainer child : children) {
776 if (childStarted(child)) {
777 if (child instanceof Startable) {
778 ((Startable)child).stop();
779 }
780 }
781 }
782 stopAdapters();
783 lifecycleState.stopped();
784 }
785
786 /**
787 * Checks the status of the child container to see if it's been started
788 * to prevent IllegalStateException upon stop
789 *
790 * @param child the child PicoContainer
791 *
792 * @return A boolean, <code>true</code> if the container is started
793 */
794 private boolean childStarted(final PicoContainer child) {
795 for (WeakReference<PicoContainer> eachChild : childrenStarted) {
796 PicoContainer ref = eachChild.get();
797 if (ref == null) {
798 continue;
799 }
800
801 if (child.equals(ref)) {
802 return true;
803 }
804 }
805 return false;
806 }
807
808 /**
809 * Dispose the components of this PicoContainer and all its logical child containers.
810 * The lifecycle operation is delegated to the component adapter,
811 * if it is an instance of {@link Behavior lifecycle manager}.
812 * The actual {@link LifecycleStrategy lifecycle strategy} supported
813 * depends on the concrete implementation of the adapter.
814 *
815 * @see Behavior
816 * @see LifecycleStrategy
817 * @see #makeChildContainer()
818 * @see #addChildContainer(PicoContainer)
819 * @see #removeChildContainer(PicoContainer)
820 */
821 public void dispose() {
822 if (lifecycleState.isStarted()) {
823 stop();
824 }
825
826 lifecycleState.disposing();
827
828 for (PicoContainer child : children) {
829 if (child instanceof MutablePicoContainer) {
830 ((Disposable)child).dispose();
831 }
832 }
833 disposeAdapters();
834
835 lifecycleState.disposed();
836 }
837
838 public void setLifecycleState(LifecycleState lifecycleState) {
839 this.lifecycleState = lifecycleState;
840 }
841
842 public MutablePicoContainer makeChildContainer() {
843 DefaultPicoContainer pc = new DefaultPicoContainer(componentFactory, lifecycleStrategy, this, componentMonitor);
844 addChildContainer(pc);
845 return pc;
846 }
847
848 /**
849 * Checks for identical references in the child container. It doesn't
850 * traverse an entire hierarchy, namely it simply checks for child containers
851 * that are equal to the current container.
852 * @param child
853 */
854 private void checkCircularChildDependencies(PicoContainer child) {
855 final String MESSAGE = "Cannot have circular dependency between parent %s and child: %s";
856 if (child == this) {
857 throw new IllegalArgumentException(String.format(MESSAGE,this,child));
858 }
859
860 //Todo: Circular Import Dependency on AbstractDelegatingPicoContainer
861 if (child instanceof AbstractDelegatingPicoContainer) {
862 AbstractDelegatingPicoContainer delegateChild = (AbstractDelegatingPicoContainer) child;
863 while(delegateChild != null) {
864 PicoContainer delegateInstance = delegateChild.getDelegate();
865 if (this == delegateInstance) {
866 throw new IllegalArgumentException(String.format(MESSAGE,this,child));
867 }
868 if (delegateInstance instanceof AbstractDelegatingPicoContainer) {
869 delegateChild = (AbstractDelegatingPicoContainer) delegateInstance;
870 } else {
871 delegateChild = null;
872 }
873 }
874 }
875
876 }
877
878 public MutablePicoContainer addChildContainer(final PicoContainer child) {
879 checkCircularChildDependencies(child);
880 if (children.add(child)) {
881 // TODO Should only be added if child container has also be started
882 if (lifecycleState.isStarted()) {
883 childrenStarted.add(new WeakReference<PicoContainer>(child));
884 }
885 }
886 return this;
887 }
888
889 public boolean removeChildContainer(final PicoContainer child) {
890 final boolean result = children.remove(child);
891 WeakReference<PicoContainer> foundRef = null;
892 for (WeakReference<PicoContainer> eachChild : childrenStarted) {
893 PicoContainer ref = eachChild.get();
894 if (ref.equals(child)) {
895 foundRef = eachChild;
896 break;
897 }
898 }
899
900 if (foundRef != null) {
901 childrenStarted.remove(foundRef);
902 }
903
904 return result;
905 }
906
907 public MutablePicoContainer change(final Properties... properties) {
908 for (Properties c : properties) {
909 Enumeration<String> e = (Enumeration<String>) c.propertyNames();
910 while (e.hasMoreElements()) {
911 String s = e.nextElement();
912 containerProperties.setProperty(s,c.getProperty(s));
913 }
914 }
915 return this;
916 }
917
918 public MutablePicoContainer as(final Properties... properties) {
919 return new AsPropertiesPicoContainer(properties);
920 }
921
922 public void accept(final PicoVisitor visitor) {
923
924 //TODO Pico 3 : change accept signatures to allow abort at any point in the traversal.
925 boolean shouldContinue = visitor.visitContainer(this);
926 if (!shouldContinue) {
927 return;
928 }
929
930
931 componentFactory.accept(visitor); // will cascade through behaviors
932 final List<ComponentAdapter<?>> componentAdapters = new ArrayList<ComponentAdapter<?>>(getComponentAdapters());
933 for (ComponentAdapter<?> componentAdapter : componentAdapters) {
934 componentAdapter.accept(visitor);
935 }
936 final List<PicoContainer> allChildren = new ArrayList<PicoContainer>(children);
937 for (PicoContainer child : allChildren) {
938 child.accept(visitor);
939 }
940 }
941
942 /**
943 * Changes monitor in the ComponentFactory, the component adapters
944 * and the child containers, if these support a ComponentMonitorStrategy.
945 * {@inheritDoc}
946 */
947 public void changeMonitor(final ComponentMonitor monitor) {
948 this.componentMonitor = monitor;
949 if (lifecycleStrategy instanceof ComponentMonitorStrategy) {
950 ((ComponentMonitorStrategy)lifecycleStrategy).changeMonitor(monitor);
951 }
952 for (ComponentAdapter<?> adapter : getModifiableComponentAdapterList()) {
953 if (adapter instanceof ComponentMonitorStrategy) {
954 ((ComponentMonitorStrategy)adapter).changeMonitor(monitor);
955 }
956 }
957 for (PicoContainer child : children) {
958 if (child instanceof ComponentMonitorStrategy) {
959 ((ComponentMonitorStrategy)child).changeMonitor(monitor);
960 }
961 }
962 }
963
964 /**
965 * Returns the first current monitor found in the ComponentFactory, the component adapters
966 * and the child containers, if these support a ComponentMonitorStrategy.
967 * {@inheritDoc}
968 *
969 * @throws PicoCompositionException if no component monitor is found in container or its children
970 */
971 public ComponentMonitor currentMonitor() {
972 return componentMonitor;
973 }
974
975 /**
976 * {@inheritDoc}
977 * Loops over all component adapters and invokes
978 * start(PicoContainer) method on the ones which are LifecycleManagers
979 */
980 private void startAdapters() {
981 Collection<ComponentAdapter<?>> adapters = getComponentAdapters();
982 for (ComponentAdapter<?> adapter : adapters) {
983 addAdapterIfStartable(adapter);
984 }
985 adapters = getOrderedComponentAdapters();
986 // clone the adapters
987 List<ComponentAdapter<?>> adaptersClone = new ArrayList<ComponentAdapter<?>>(adapters);
988 for (final ComponentAdapter<?> adapter : adaptersClone) {
989 potentiallyStartAdapter(adapter);
990 }
991 }
992
993 protected void potentiallyStartAdapter(ComponentAdapter<?> adapter) {
994 if (adapter instanceof ComponentLifecycle) {
995 if (!lifecycleStrategy.isLazy(adapter)) {
996 ((ComponentLifecycle<?>)adapter).start(this);
997 }
998 }
999 }
1000
1001 private void addAdapterIfStartable(ComponentAdapter<?> adapter) {
1002 if (adapter instanceof ComponentLifecycle) {
1003 ComponentLifecycle<?> componentLifecycle = (ComponentLifecycle<?>)adapter;
1004 if (componentLifecycle.componentHasLifecycle()) {
1005 // create an instance, it will be added to the ordered CA list
1006 instantiateComponentAsIsStartable(adapter);
1007 addOrderedComponentAdapter(adapter);
1008 }
1009 }
1010 }
1011
1012 protected void instantiateComponentAsIsStartable(ComponentAdapter<?> adapter) {
1013 if (!lifecycleStrategy.isLazy(adapter)) {
1014 adapter.getComponentInstance(DefaultPicoContainer.this, ComponentAdapter.NOTHING.class);
1015 }
1016 }
1017
1018 /**
1019 * {@inheritDoc}
1020 * Loops over started component adapters (in inverse order) and invokes
1021 * stop(PicoContainer) method on the ones which are LifecycleManagers
1022 */
1023 private void stopAdapters() {
1024 for (int i = getOrderedComponentAdapters().size() - 1; 0 <= i; i--) {
1025 ComponentAdapter<?> adapter = getOrderedComponentAdapters().get(i);
1026 if (adapter instanceof ComponentLifecycle) {
1027 ComponentLifecycle<?> componentLifecycle = (ComponentLifecycle<?>)adapter;
1028 if (componentLifecycle.componentHasLifecycle() && componentLifecycle.isStarted()) {
1029 componentLifecycle.stop(DefaultPicoContainer.this);
1030 }
1031 }
1032 }
1033 }
1034
1035 /**
1036 * {@inheritDoc}
1037 * Loops over all component adapters (in inverse order) and invokes
1038 * dispose(PicoContainer) method on the ones which are LifecycleManagers
1039 */
1040 private void disposeAdapters() {
1041 for (int i = getOrderedComponentAdapters().size() - 1; 0 <= i; i--) {
1042 ComponentAdapter<?> adapter = getOrderedComponentAdapters().get(i);
1043 if (adapter instanceof ComponentLifecycle) {
1044 ComponentLifecycle<?> componentLifecycle = (ComponentLifecycle<?>)adapter;
1045 componentLifecycle.dispose(DefaultPicoContainer.this);
1046 }
1047 }
1048 }
1049
1050
1051
1052 /**
1053 * @return the orderedComponentAdapters
1054 */
1055 protected List<ComponentAdapter<?>> getOrderedComponentAdapters() {
1056 return orderedComponentAdapters;
1057 }
1058
1059 /**
1060 * @return the componentKeyToAdapterCache
1061 */
1062 protected Map<Object, ComponentAdapter<?>> getComponentKeyToAdapterCache() {
1063 return componentKeyToAdapterCache;
1064 }
1065
1066 /**
1067 * @return the componentAdapters
1068 */
1069 protected List<ComponentAdapter<?>> getModifiableComponentAdapterList() {
1070 return componentAdapters;
1071 }
1072
1073 public void setName(String name) {
1074 this.name = name;
1075 }
1076
1077 @Override
1078 public String toString() {
1079 return String.format("%s:%d<%s", (name != null ? name : super.toString()), this.componentAdapters.size(), (parent != null ? parent.toString() : "|"));
1080 }
1081
1082 /**
1083 * If this container has a set of converters, then return it.
1084 * If it does not, and the parent (or their parent ..) does, use that
1085 * If they do not, return a NullObject implementation (ConversNothing)
1086 * @return the converters
1087 */
1088 public synchronized Converters getConverters() {
1089 if (converters == null) {
1090 if (parent == null || (parent instanceof Converting && ((Converting) parent).getConverters() instanceof ConvertsNothing)) {
1091 converters = new BuiltInConverters();
1092 } else {
1093 return ((Converting) parent).getConverters();
1094 }
1095 }
1096 return converters;
1097 }
1098
1099 private class AsPropertiesPicoContainer extends AbstractDelegatingMutablePicoContainer {
1100
1101 private final Properties properties;
1102
1103 public AsPropertiesPicoContainer(final Properties... props) {
1104 super(DefaultPicoContainer.this);
1105 properties = (Properties) containerProperties.clone();
1106 for (Properties c : props) {
1107 Enumeration<?> e = c.propertyNames();
1108 while (e.hasMoreElements()) {
1109 String s = (String)e.nextElement();
1110 properties.setProperty(s,c.getProperty(s));
1111 }
1112 }
1113 }
1114
1115 @Override
1116 @SuppressWarnings("unused")
1117 public MutablePicoContainer as( Properties... props) {
1118 throw new PicoCompositionException("Syntax 'as(FOO).as(BAR)' not allowed, do 'as(FOO, BAR)' instead");
1119 }
1120
1121 @Override
1122 public MutablePicoContainer makeChildContainer() {
1123 return getDelegate().makeChildContainer();
1124 }
1125
1126 @Override
1127 public MutablePicoContainer addComponent(final Object componentKey,
1128 final Object componentImplementationOrInstance,
1129 final Parameter... parameters) throws PicoCompositionException {
1130 return DefaultPicoContainer.this.addComponent(componentKey,
1131 componentImplementationOrInstance,
1132 properties,
1133 parameters);
1134 }
1135
1136 @Override
1137 public MutablePicoContainer addComponent(final Object implOrInstance) throws PicoCompositionException {
1138 return DefaultPicoContainer.this.addComponent(implOrInstance, properties);
1139 }
1140
1141 @Override
1142 public MutablePicoContainer addAdapter(final ComponentAdapter<?> componentAdapter) throws PicoCompositionException {
1143 return DefaultPicoContainer.this.addAdapter(componentAdapter, properties);
1144 }
1145 }
1146
1147 private static class IntoThreadLocal extends ThreadLocal<Type> implements Serializable {
1148 protected Type initialValue() {
1149 return ComponentAdapter.NOTHING.class;
1150 }
1151 }
1152 }