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