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