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