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