001    /*****************************************************************************
002     * Copyright (C) PicoContainer Organization. All rights reserved.            *
003     * ------------------------------------------------------------------------- *
004     * The software in this package is published under the terms of the BSD      *
005     * style license a copy of which has been included with this distribution in *
006     * the LICENSE.txt file.                                                     *
007     *                                                                           *
008     * Original code by                                                          *
009     *****************************************************************************/
010    package org.picocontainer;
011    
012    import org.picocontainer.adapters.InstanceAdapter;
013    import org.picocontainer.behaviors.Cached;
014    import org.picocontainer.behaviors.Caching;
015    import org.picocontainer.behaviors.HiddenImplementation;
016    import org.picocontainer.behaviors.AdaptiveBehavior;
017    import org.picocontainer.behaviors.AbstractBehaviorFactory;
018    import org.picocontainer.containers.AbstractDelegatingMutablePicoContainer;
019    import org.picocontainer.containers.EmptyPicoContainer;
020    import org.picocontainer.containers.ImmutablePicoContainer;
021    import org.picocontainer.injectors.AbstractInjector;
022    import org.picocontainer.injectors.AdaptiveInjection;
023    import org.picocontainer.lifecycle.StartableLifecycleStrategy;
024    import org.picocontainer.monitors.NullComponentMonitor;
025    
026    import java.io.Serializable;
027    import java.util.ArrayList;
028    import java.util.Collections;
029    import java.util.HashMap;
030    import java.util.HashSet;
031    import java.util.List;
032    import java.util.Map;
033    import java.util.Set;
034    import java.util.Properties;
035    import java.util.Enumeration;
036    import java.util.Collection;
037    
038    /**
039     * <p/>
040     * The Standard {@link PicoContainer}/{@link MutablePicoContainer} implementation.
041     * Constructing a container c with a parent p container will cause c to look up components
042     * in p if they cannot be found inside c itself.
043     * </p>
044     * <p/>
045     * Using {@link Class} objects as keys to the various registerXXX() methods makes
046     * a subtle semantic difference:
047     * </p>
048     * <p/>
049     * If there are more than one registered components of the same type and one of them are
050     * registered with a {@link java.lang.Class} key of the corresponding type, this addComponent
051     * will take precedence over other components during type resolution.
052     * </p>
053     * <p/>
054     * Another place where keys that are classes make a subtle difference is in
055     * {@link HiddenImplementation}.
056     * </p>
057     * <p/>
058     * This implementation of {@link MutablePicoContainer} also supports
059     * {@link ComponentMonitorStrategy}.
060     * </p>
061     *
062     * @author Paul Hammant
063     * @author Aslak Helles&oslash;y
064     * @author Jon Tirs&eacute;n
065     * @author Thomas Heller
066     * @author Mauro Talevi
067     */
068    public class DefaultPicoContainer implements MutablePicoContainer, ComponentMonitorStrategy, Serializable {
069    
070        private final Map<Object, ComponentAdapter> componentKeyToAdapterCache = new HashMap<Object, ComponentAdapter>();
071        private final List<ComponentAdapter<?>> componentAdapters = new ArrayList<ComponentAdapter<?>>();
072        // Keeps track of instantiation order.
073        private final List<ComponentAdapter<?>> orderedComponentAdapters = new ArrayList<ComponentAdapter<?>>();
074    
075    
076        private ComponentFactory componentFactory;
077        private PicoContainer parent;
078        private final Set<PicoContainer> children = new HashSet<PicoContainer>();
079    
080        // Keeps track of the container started status
081        private boolean started = false;
082        // Keeps track of the container disposed status
083        private boolean disposed = false;
084        // Keeps track of child containers started status
085        private final Set<Integer> childrenStarted = new HashSet<Integer>();
086    
087        private LifecycleStrategy lifecycleStrategy;
088        private final Properties componentProperties = new Properties();
089        private ComponentMonitor componentMonitor;
090    
091        /** List collecting the CAs which have been successfully started */
092        private final List<Integer> startedComponentAdapters = new ArrayList<Integer>();
093    
094        /**
095         * Creates a new container with a custom ComponentFactory and a parent container.
096         * <p/>
097         * <em>
098         * Important note about caching: If you intend the components to be cached, you should pass
099         * in a factory that creates {@link Cached} instances, such as for example
100         * {@link Caching}. Caching can delegate to
101         * other ComponentAdapterFactories.
102         * </em>
103         *
104         * @param componentFactory the factory to use for creation of ComponentAdapters.
105         * @param parent                  the parent container (used for component dependency lookups).
106         */
107        public DefaultPicoContainer(ComponentFactory componentFactory, PicoContainer parent) {
108            this(componentFactory, new StartableLifecycleStrategy(new NullComponentMonitor()), parent, new NullComponentMonitor());
109        }
110    
111        /**
112         * Creates a new container with a custom ComponentFactory, LifecycleStrategy for instance registration,
113         * and a parent container.
114         * <p/>
115         * <em>
116         * Important note about caching: If you intend the components to be cached, you should pass
117         * in a factory that creates {@link Cached} instances, such as for example
118         * {@link Caching}. Caching can delegate to
119         * other ComponentAdapterFactories.
120         * </em>
121         *
122         * @param componentFactory the factory to use for creation of ComponentAdapters.
123         * @param lifecycleStrategy
124         *                                the lifecylce strategy chosen for regiered
125         *                                instance (not implementations!)
126         * @param parent                  the parent container (used for component dependency lookups).
127         */
128        public DefaultPicoContainer(ComponentFactory componentFactory,
129                                    LifecycleStrategy lifecycleStrategy,
130                                    PicoContainer parent) {
131            this(componentFactory, lifecycleStrategy, parent, new NullComponentMonitor() );
132        }
133    
134        public DefaultPicoContainer(ComponentFactory componentFactory,
135                                    LifecycleStrategy lifecycleStrategy,
136                                    PicoContainer parent, ComponentMonitor componentMonitor) {
137            if (componentFactory == null) throw new NullPointerException("componentFactory");
138            if (lifecycleStrategy == null) throw new NullPointerException("lifecycleStrategy");
139            this.componentFactory = componentFactory;
140            this.lifecycleStrategy = lifecycleStrategy;
141            this.parent = parent;
142            if (parent != null && !(parent instanceof EmptyPicoContainer)) {
143                this.parent = new ImmutablePicoContainer(parent);
144            }
145            this.componentMonitor = componentMonitor;
146        }
147    
148        /**
149         * Creates a new container with the AdaptiveInjection using a
150         * custom ComponentMonitor
151         *
152         * @param monitor the ComponentMonitor to use
153         * @param parent  the parent container (used for component dependency lookups).
154         */
155        public DefaultPicoContainer(ComponentMonitor monitor, PicoContainer parent) {
156            this(new AdaptiveBehavior(), new StartableLifecycleStrategy(monitor), parent, monitor);
157        }
158    
159        /**
160         * Creates a new container with the AdaptiveInjection using a
161         * custom ComponentMonitor and lifecycle strategy
162         *
163         * @param monitor           the ComponentMonitor to use
164         * @param lifecycleStrategy the lifecycle strategy to use.
165         * @param parent            the parent container (used for component dependency lookups).
166         */
167        public DefaultPicoContainer(ComponentMonitor monitor, LifecycleStrategy lifecycleStrategy, PicoContainer parent) {
168            this(new AdaptiveBehavior(), lifecycleStrategy, parent, monitor);
169        }
170    
171        /**
172         * Creates a new container with the AdaptiveInjection using a
173         * custom lifecycle strategy
174         *
175         * @param lifecycleStrategy the lifecycle strategy to use.
176         * @param parent            the parent container (used for component dependency lookups).
177         */
178        public DefaultPicoContainer(LifecycleStrategy lifecycleStrategy, PicoContainer parent) {
179            this(new NullComponentMonitor(), lifecycleStrategy, parent);
180        }
181    
182    
183        /**
184         * Creates a new container with a custom ComponentFactory and no parent container.
185         *
186         * @param componentFactory the ComponentFactory to use.
187         */
188        public DefaultPicoContainer(ComponentFactory componentFactory) {
189            this(componentFactory, null);
190        }
191    
192        /**
193         * Creates a new container with the AdaptiveInjection using a
194         * custom ComponentMonitor
195         *
196         * @param monitor the ComponentMonitor to use
197         */
198        public DefaultPicoContainer(ComponentMonitor monitor) {
199            this(monitor, new StartableLifecycleStrategy(monitor), null);
200        }
201    
202        /**
203         * Creates a new container with a (caching) {@link AdaptiveInjection}
204         * and a parent container.
205         *
206         * @param parent the parent container (used for component dependency lookups).
207         */
208        public DefaultPicoContainer(PicoContainer parent) {
209            this(new AdaptiveBehavior(), parent);
210        }
211    
212        /** Creates a new container with a {@link AdaptiveBehavior} and no parent container. */
213        public DefaultPicoContainer() {
214            this(new AdaptiveBehavior(), null);
215        }
216    
217        public Collection<ComponentAdapter<?>> getComponentAdapters() {
218            return Collections.unmodifiableList(componentAdapters);
219        }
220    
221        public final ComponentAdapter<?> getComponentAdapter(Object componentKey) {
222            ComponentAdapter adapter = componentKeyToAdapterCache.get(componentKey);
223            if (adapter == null && parent != null) {
224                adapter = parent.getComponentAdapter(componentKey);
225            }
226            return adapter;
227        }
228    
229        public <T> ComponentAdapter<T> getComponentAdapter(Class<T> componentType, ParameterName componentParameterName) {
230            // See http://jira.codehaus.org/secure/ViewIssue.jspa?key=PICO-115
231            ComponentAdapter<?> adapterByKey = getComponentAdapter((Object)componentType);
232            if (adapterByKey != null) {
233                return typeComponentAdapter(adapterByKey);
234            }
235    
236            List<ComponentAdapter<T>> found = getComponentAdapters(componentType);
237    
238            if (found.size() == 1) {
239                return found.get(0);
240            } else if (found.isEmpty()) {
241                if (parent != null) {
242                    return parent.getComponentAdapter(componentType, componentParameterName);
243                } else {
244                    return null;
245                }
246            } else {
247                if (componentParameterName != null) {
248                    String parameterName = componentParameterName.getName();
249                    if (parameterName != null) {
250                        ComponentAdapter ca = getComponentAdapter(parameterName);
251                        if (ca != null && componentType.isAssignableFrom(ca.getComponentImplementation())) {
252                            return ca;
253                        }
254                    }
255                }
256                Class[] foundClasses = new Class[found.size()];
257                for (int i = 0; i < foundClasses.length; i++) {
258                    foundClasses[i] = found.get(i).getComponentImplementation();
259                }
260    
261                throw new AbstractInjector.AmbiguousComponentResolutionException(componentType, foundClasses);
262            }
263        }
264    
265        public <T> List<ComponentAdapter<T>> getComponentAdapters(Class<T> componentType) {
266            if (componentType == null) {
267                return Collections.emptyList();
268            }
269            List<ComponentAdapter<T>> found = new ArrayList<ComponentAdapter<T>>();
270            for (ComponentAdapter<?> componentAdapter : getComponentAdapters()) {
271                if (componentType.isAssignableFrom(componentAdapter.getComponentImplementation())) {
272                    ComponentAdapter<T> typedComponentAdapter = typeComponentAdapter(componentAdapter);
273                    found.add(typedComponentAdapter);
274                }
275            }
276            return found;
277        }
278    
279        protected MutablePicoContainer addAdapterInternal(ComponentAdapter componentAdapter) {
280            Object componentKey = componentAdapter.getComponentKey();
281            if (componentKeyToAdapterCache.containsKey(componentKey)) {
282                throw new PicoCompositionException("Duplicate Keys not allowed. Duplicate for '" + componentKey + "'");
283            }
284            componentAdapters.add(componentAdapter);
285            componentKeyToAdapterCache.put(componentKey, componentAdapter);
286            return this;
287        }
288    
289        /**
290         * {@inheritDoc}
291         * This method can be used to override the ComponentAdapter created by the {@link ComponentFactory}
292         * passed to the constructor of this container.
293         */
294        public MutablePicoContainer addAdapter(ComponentAdapter componentAdapter) {
295            return addAdapter(componentAdapter,  this.componentProperties);
296        }
297    
298        public MutablePicoContainer addAdapter(ComponentAdapter componentAdapter, Properties properties) {
299            Properties tmpProperties = (Properties)properties.clone();
300            if (AbstractBehaviorFactory.removePropertiesIfPresent(tmpProperties, Characteristics.NONE) == false && componentFactory instanceof BehaviorFactory) {
301                MutablePicoContainer container = addAdapterInternal(((BehaviorFactory)componentFactory).addComponentAdapter(
302                    componentMonitor,
303                    lifecycleStrategy,
304                    tmpProperties,
305                    componentAdapter));
306                throwIfPropertiesLeft(tmpProperties);
307                return container;
308            } else {
309                return addAdapterInternal(componentAdapter);
310            }
311    
312        }
313    
314    
315        public ComponentAdapter removeComponent(Object componentKey) {
316            if (started) {
317                throw new PicoCompositionException("Cannot remove components after the container has started");
318            }
319            ComponentAdapter adapter = componentKeyToAdapterCache.remove(componentKey);
320            componentAdapters.remove(adapter);
321            orderedComponentAdapters.remove(adapter);
322            return adapter;
323        }
324    
325        /**
326         * {@inheritDoc}
327         * The returned ComponentAdapter will be an {@link org.picocontainer.adapters.InstanceAdapter}.
328         */
329        public MutablePicoContainer addComponent(Object implOrInstance) {
330            return addComponent(implOrInstance, this.componentProperties);
331        }
332    
333        private MutablePicoContainer addComponent(Object implOrInstance, Properties props) {
334            Class clazz;
335            if (implOrInstance instanceof String) {
336                addComponent((String) implOrInstance, implOrInstance);
337            }
338            if (implOrInstance instanceof Class) {
339                clazz = (Class)implOrInstance;
340            } else {
341                clazz = implOrInstance.getClass();
342            }
343            return addComponent(clazz, implOrInstance, props);
344        }
345    
346    
347        public MutablePicoContainer addConfig(String name, Object val) {
348            // shall be removed somehow...
349            initForConfig();
350            return addAdapterInternal(new InstanceAdapter(name, val, lifecycleStrategy, componentMonitor));
351        }
352    
353        /**
354         * TODO: need for this method is being disputed
355         */
356        private void initForConfig() {
357            final String DUMMY_PICOCONTAINER_CONFIG_ITEM = "DUMMY_PICOCONTAINER_CONFIG_ITEM_";
358            if (getComponent(DUMMY_PICOCONTAINER_CONFIG_ITEM + 1) != null) {
359                return;
360            }
361            int i = 0;
362            addComponent(DUMMY_PICOCONTAINER_CONFIG_ITEM + ++i, DUMMY_PICOCONTAINER_CONFIG_ITEM + i);
363            addComponent(DUMMY_PICOCONTAINER_CONFIG_ITEM + ++i, DUMMY_PICOCONTAINER_CONFIG_ITEM + i);
364            addComponent(DUMMY_PICOCONTAINER_CONFIG_ITEM + ++i, 0);
365            addComponent(DUMMY_PICOCONTAINER_CONFIG_ITEM + ++i, 0);
366            addComponent(DUMMY_PICOCONTAINER_CONFIG_ITEM + ++i, false);
367            addComponent(DUMMY_PICOCONTAINER_CONFIG_ITEM + ++i, false);
368            addComponent(DUMMY_PICOCONTAINER_CONFIG_ITEM + ++i, 0L);
369            addComponent(DUMMY_PICOCONTAINER_CONFIG_ITEM + ++i, 0L);
370        }
371    
372        /**
373         * {@inheritDoc}
374         * The returned ComponentAdapter will be instantiated by the {@link ComponentFactory}
375         * passed to the container's constructor.
376         */
377        public MutablePicoContainer addComponent(Object componentKey,
378                                                 Object componentImplementationOrInstance,
379                                                 Parameter... parameters) {
380            return this.addComponent(componentKey, componentImplementationOrInstance, this.componentProperties, parameters);
381        }
382    
383        private MutablePicoContainer addComponent(Object componentKey,
384                                                 Object componentImplementationOrInstance,
385                                                 final Properties properties,
386                                                 Parameter... parameters) {
387            if (parameters != null && parameters.length == 0 && parameters != Parameter.ZERO) {
388                parameters = null; // backwards compatibility!  solve this better later - Paul
389            }
390            if (componentImplementationOrInstance instanceof Class) {
391                Properties tmpProperties = (Properties) properties.clone();
392                ComponentAdapter componentAdapter = componentFactory.createComponentAdapter(componentMonitor,
393                                                                                                   lifecycleStrategy,
394                                                                                                   tmpProperties,
395                                                                                                   componentKey,
396                                                                                                   (Class)componentImplementationOrInstance,
397                                                                                                   parameters);
398                throwIfPropertiesLeft(tmpProperties);
399                return addAdapterInternal(componentAdapter);
400            } else {
401                ComponentAdapter componentAdapter =
402                    new InstanceAdapter(componentKey, componentImplementationOrInstance, lifecycleStrategy, componentMonitor);
403                return addAdapter(componentAdapter, properties);
404            }
405        }
406    
407        private void throwIfPropertiesLeft(Properties tmpProperties) {
408            if(tmpProperties.size() > 0) {
409                throw new PicoCompositionException("Unprocessed Characteristics:" + tmpProperties +", refer http://picocontainer.org/unprocessed-properties-help.html");
410            }
411        }
412    
413        private void addOrderedComponentAdapter(ComponentAdapter componentAdapter) {
414            if (!orderedComponentAdapters.contains(componentAdapter)) {
415                orderedComponentAdapters.add(componentAdapter);
416            }
417        }
418    
419        public List getComponents() throws PicoException {
420            return getComponents(Object.class);
421        }
422    
423        public <T> List<T> getComponents(Class<T> componentType) {
424            if (componentType == null) {
425                return Collections.emptyList();
426            }
427    
428            Map<ComponentAdapter<T>, T> adapterToInstanceMap = new HashMap<ComponentAdapter<T>, T>();
429            for (ComponentAdapter<?> componentAdapter : componentAdapters) {
430                if (componentType.isAssignableFrom(componentAdapter.getComponentImplementation())) {
431                    ComponentAdapter<T> typedComponentAdapter = typeComponentAdapter(componentAdapter);
432                    T componentInstance = getLocalInstance(typedComponentAdapter);
433    
434                    adapterToInstanceMap.put(typedComponentAdapter, componentInstance);
435                }
436            }
437            List<T> result = new ArrayList<T>();
438            for (ComponentAdapter componentAdapter : orderedComponentAdapters) {
439                final T componentInstance = adapterToInstanceMap.get(componentAdapter);
440                if (componentInstance != null) {
441                    // may be null in the case of the "implicit" addAdapter
442                    // representing "this".
443                    result.add(componentInstance);
444                }
445            }
446            return result;
447        }
448    
449        private <T> T getLocalInstance(ComponentAdapter<T> typedComponentAdapter) {
450            T componentInstance = typedComponentAdapter.getComponentInstance(this);
451    
452            // This is to ensure all are added. (Indirect dependencies will be added
453            // from InstantiatingComponentAdapter).
454            addOrderedComponentAdapter(typedComponentAdapter);
455    
456            return componentInstance;
457        }
458    
459        @SuppressWarnings({ "unchecked" })
460        private static <T> ComponentAdapter<T> typeComponentAdapter(ComponentAdapter<?> componentAdapter) {
461            return (ComponentAdapter<T>)componentAdapter;
462        }
463    
464        public Object getComponent(Object componentKeyOrType) {
465            Object retVal;
466            if (componentKeyOrType instanceof Class) {
467                final ComponentAdapter<?> componentAdapter = getComponentAdapter((Class<?>)componentKeyOrType, null);
468                retVal = componentAdapter == null ? null : getInstance(componentAdapter);
469            } else {
470                ComponentAdapter<?> componentAdapter = getComponentAdapter(componentKeyOrType);
471                retVal = componentAdapter == null ? null : getInstance(componentAdapter);
472            }
473            if (retVal == null) {
474                retVal = componentMonitor.noComponentFound(this, componentKeyOrType);
475            }
476            return retVal;
477        }
478    
479        public <T> T getComponent(Class<T> componentType) {
480            Object o = getComponent((Object)componentType);
481            return componentType.cast(o);
482        }
483    
484        private Object getInstance(ComponentAdapter componentAdapter) {
485            // check wether this is our adapter
486            // we need to check this to ensure up-down dependencies cannot be followed
487            final boolean isLocal = componentAdapters.contains(componentAdapter);
488    
489            if (isLocal) {
490                Object instance;
491                try {
492                    instance = componentAdapter.getComponentInstance(this);
493                } catch (AbstractInjector.CyclicDependencyException e) {
494                    if (parent != null) {
495                        instance = parent.getComponent(componentAdapter.getComponentKey());
496                        if (instance != null) {
497                            return instance;
498                        }
499                    }
500                    throw e;
501                }
502                addOrderedComponentAdapter(componentAdapter);
503    
504                return instance;
505            } else if (parent != null) {
506                return parent.getComponent(componentAdapter.getComponentKey());
507            }
508    
509            return null;
510        }
511    
512    
513        public PicoContainer getParent() {
514            return parent;
515        }
516    
517        public ComponentAdapter removeComponentByInstance(Object componentInstance) {
518            for (ComponentAdapter<?> componentAdapter : componentAdapters) {
519                if (getLocalInstance(componentAdapter).equals(componentInstance)) {
520                    return removeComponent(componentAdapter.getComponentKey());
521                }
522            }
523            return null;
524        }
525    
526        /**
527         * Start the components of this PicoContainer and all its logical child containers.
528         * The starting of the child container is only attempted if the parent
529         * container start successfully.  The child container for which start is attempted
530         * is tracked so that upon stop, only those need to be stopped.
531         * The lifecycle operation is delegated to the component adapter,
532         * if it is an instance of {@link Behavior lifecycle manager}.
533         * The actual {@link LifecycleStrategy lifecycle strategy} supported
534         * depends on the concrete implementation of the adapter.
535         *
536         * @see Behavior
537         * @see LifecycleStrategy
538         * @see #makeChildContainer()
539         * @see #addChildContainer(PicoContainer)
540         * @see #removeChildContainer(PicoContainer)
541         */
542        public void start() {
543            if (disposed) throw new IllegalStateException("Already disposed");
544            if (started) throw new IllegalStateException("Already started");
545            started = true;
546            startAdapters();
547            childrenStarted.clear();
548            for (PicoContainer child : children) {
549                childrenStarted.add(child.hashCode());
550                if (child instanceof Startable) {
551                    ((Startable)child).start();
552                }
553            }
554        }
555    
556        /**
557         * Stop the components of this PicoContainer and all its logical child containers.
558         * The stopping of the child containers is only attempted for those that have been
559         * started, possibly not successfully.
560         * The lifecycle operation is delegated to the component adapter,
561         * if it is an instance of {@link Behavior lifecycle manager}.
562         * The actual {@link LifecycleStrategy lifecycle strategy} supported
563         * depends on the concrete implementation of the adapter.
564         *
565         * @see Behavior
566         * @see LifecycleStrategy
567         * @see #makeChildContainer()
568         * @see #addChildContainer(PicoContainer)
569         * @see #removeChildContainer(PicoContainer)
570         */
571        public void stop() {
572            if (disposed) throw new IllegalStateException("Already disposed");
573            if (!started) throw new IllegalStateException("Not started");
574            for (PicoContainer child : children) {
575                if (childStarted(child)) {
576                    if (child instanceof Startable) {
577                        ((Startable)child).stop();
578                    }
579                }
580            }
581            stopAdapters();
582            started = false;
583        }
584    
585        /**
586         * Checks the status of the child container to see if it's been started
587         * to prevent IllegalStateException upon stop
588         *
589         * @param child the child PicoContainer
590         *
591         * @return A boolean, <code>true</code> if the container is started
592         */
593        private boolean childStarted(PicoContainer child) {
594            return childrenStarted.contains(new Integer(child.hashCode()));
595        }
596    
597        /**
598         * Dispose the components of this PicoContainer and all its logical child containers.
599         * The lifecycle operation is delegated to the component adapter,
600         * if it is an instance of {@link Behavior lifecycle manager}.
601         * The actual {@link LifecycleStrategy lifecycle strategy} supported
602         * depends on the concrete implementation of the adapter.
603         *
604         * @see Behavior
605         * @see LifecycleStrategy
606         * @see #makeChildContainer()
607         * @see #addChildContainer(PicoContainer)
608         * @see #removeChildContainer(PicoContainer)
609         */
610        public void dispose() {
611            if (disposed) throw new IllegalStateException("Already disposed");
612            for (PicoContainer child : children) {
613                if (child instanceof MutablePicoContainer) {
614                    ((Disposable)child).dispose();
615                }
616            }
617            disposeAdapters();
618            disposed = true;
619        }
620    
621        public MutablePicoContainer makeChildContainer() {
622            DefaultPicoContainer pc = new DefaultPicoContainer(componentFactory, lifecycleStrategy, this);
623            addChildContainer(pc);
624            return pc;
625        }
626    
627        public MutablePicoContainer addChildContainer(PicoContainer child) {
628            if (children.add(child)) {
629                // @todo Should only be added if child container has also be started
630                if (started) {
631                    childrenStarted.add(child.hashCode());
632                }
633            }
634            return this;
635        }
636    
637        public boolean removeChildContainer(PicoContainer child) {
638            final boolean result = children.remove(child);
639            childrenStarted.remove(new Integer(child.hashCode()));
640            return result;
641        }
642    
643        public MutablePicoContainer change(Properties... properties) {
644            for (Properties c : properties) {
645                Enumeration e = c.propertyNames();
646                while (e.hasMoreElements()) {
647                    String s = (String)e.nextElement();
648                    componentProperties.setProperty(s,c.getProperty(s));
649                }
650            }
651            return this;
652        }
653    
654        public MutablePicoContainer as(Properties... properties) {
655            return new AsPropertiesPicoContainer(properties);
656        }
657    
658        public void accept(PicoVisitor visitor) {
659            visitor.visitContainer(this);
660            final List<ComponentAdapter> componentAdapters = new ArrayList<ComponentAdapter>(getComponentAdapters());
661            for (ComponentAdapter componentAdapter : componentAdapters) {
662                componentAdapter.accept(visitor);
663            }
664            final List<PicoContainer> allChildren = new ArrayList<PicoContainer>(children);
665            for (PicoContainer child : allChildren) {
666                child.accept(visitor);
667            }
668        }
669    
670        /**
671         * Changes monitor in the ComponentFactory, the component adapters
672         * and the child containers, if these support a ComponentMonitorStrategy.
673         * {@inheritDoc}
674         */
675        public void changeMonitor(ComponentMonitor monitor) {
676            this.componentMonitor = monitor;
677            if (lifecycleStrategy instanceof ComponentMonitorStrategy) {
678                ((ComponentMonitorStrategy)lifecycleStrategy).changeMonitor(monitor);
679            }
680            for (ComponentAdapter adapter : componentAdapters) {
681                if (adapter instanceof ComponentMonitorStrategy) {
682                    ((ComponentMonitorStrategy)adapter).changeMonitor(monitor);
683                }
684            }
685            for (PicoContainer child : children) {
686                if (child instanceof ComponentMonitorStrategy) {
687                    ((ComponentMonitorStrategy)child).changeMonitor(monitor);
688                }
689            }
690        }
691    
692        /**
693         * Returns the first current monitor found in the ComponentFactory, the component adapters
694         * and the child containers, if these support a ComponentMonitorStrategy.
695         * {@inheritDoc}
696         *
697         * @throws PicoCompositionException if no component monitor is found in container or its children
698         */
699        public ComponentMonitor currentMonitor() {
700            return componentMonitor;
701        }
702    
703        /**
704         * {@inheritDoc}
705         * Loops over all component adapters and invokes
706         * start(PicoContainer) method on the ones which are LifecycleManagers
707         */
708        private void startAdapters() {
709            Collection<ComponentAdapter<?>> adapters = getComponentAdapters();
710            for (ComponentAdapter adapter : adapters) {
711                if (adapter instanceof Behavior) {
712                    Behavior behaviorAdapter = (Behavior)adapter;
713                    if (behaviorAdapter.componentHasLifecycle()) {
714                        // create an instance, it will be added to the ordered CA list
715                        adapter.getComponentInstance(DefaultPicoContainer.this);
716                        addOrderedComponentAdapter(adapter);
717                    }
718                }
719            }
720            adapters = orderedComponentAdapters;
721            // clear list of started CAs
722            startedComponentAdapters.clear();
723            // clone the adapters
724            List<ComponentAdapter<?>> adaptersClone = new ArrayList<ComponentAdapter<?>>(adapters);
725            for (final ComponentAdapter adapter : adaptersClone) {
726                if (adapter instanceof Behavior) {
727                    Behavior manager = (Behavior)adapter;
728                    manager.start(DefaultPicoContainer.this);
729                    startedComponentAdapters.add(adaptersClone.indexOf(adapter));
730                }
731            }
732        }
733    
734        /**
735         * {@inheritDoc}
736         * Loops over started component adapters (in inverse order) and invokes
737         * stop(PicoContainer) method on the ones which are LifecycleManagers
738         */
739        private void stopAdapters() {
740            for (int i = startedComponentAdapters.size() - 1; 0 <= i; i--) {
741                ComponentAdapter adapter = orderedComponentAdapters.get(startedComponentAdapters.get(i));
742                if (adapter instanceof Behavior) {
743                    Behavior manager = (Behavior)adapter;
744                    manager.stop(DefaultPicoContainer.this);
745                }
746            }
747        }
748    
749        /**
750         * {@inheritDoc}
751         * Loops over all component adapters (in inverse order) and invokes
752         * dispose(PicoContainer) method on the ones which are LifecycleManagers
753         */
754        private void disposeAdapters() {
755            for (int i = orderedComponentAdapters.size() - 1; 0 <= i; i--) {
756                ComponentAdapter adapter = orderedComponentAdapters.get(i);
757                if (adapter instanceof Behavior) {
758                    Behavior manager = (Behavior)adapter;
759                    manager.dispose(DefaultPicoContainer.this);
760                }
761            }
762        }
763    
764    
765        private class AsPropertiesPicoContainer extends AbstractDelegatingMutablePicoContainer {
766            private Properties properties;
767    
768            public AsPropertiesPicoContainer(Properties... props) {
769                super(DefaultPicoContainer.this);
770                properties = (Properties) componentProperties.clone();
771                for (Properties c : props) {
772                    Enumeration e = c.propertyNames();
773                    while (e.hasMoreElements()) {
774                        String s = (String)e.nextElement();
775                        properties.setProperty(s,c.getProperty(s));
776                    }
777                }
778            }
779    
780            public MutablePicoContainer makeChildContainer() {
781                return getDelegate().makeChildContainer();
782            }
783    
784            public MutablePicoContainer addComponent(Object componentKey,
785                                                     Object componentImplementationOrInstance,
786                                                     Parameter... parameters) throws PicoCompositionException {
787                return DefaultPicoContainer.this.addComponent(componentKey,
788                                          componentImplementationOrInstance,
789                                          properties,
790                                          parameters);
791            }
792    
793            public MutablePicoContainer addComponent(Object implOrInstance) throws PicoCompositionException {
794                return DefaultPicoContainer.this.addComponent(implOrInstance, properties);
795            }
796    
797            public MutablePicoContainer addAdapter(ComponentAdapter componentAdapter) throws PicoCompositionException {
798                return DefaultPicoContainer.this.addAdapter(componentAdapter, properties);
799            }
800        }
801    
802    }