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