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