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