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