    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    }