    001    /*******************************************************************************
    002     * Copyright (C) PicoContainer Organization. All rights reserved.
    003     * ---------------------------------------------------------------------------
    004     * The software in this package is published under the terms of the BSD style
    005     * license a copy of which has been included with this distribution in the
    006     * LICENSE.txt file.
    007     ******************************************************************************/
    008    package org.picocontainer.classname;
    009
    010    import org.picocontainer.ComponentAdapter;
    011    import org.picocontainer.ComponentFactory;
    012    import org.picocontainer.ComponentMonitor;
    013    import org.picocontainer.ComponentMonitorStrategy;
    014    import org.picocontainer.LifecycleStrategy;
    015    import org.picocontainer.MutablePicoContainer;
    016    import org.picocontainer.Parameter;
    017    import org.picocontainer.PicoClassNotFoundException;
    018    import org.picocontainer.PicoContainer;
    019    import org.picocontainer.PicoException;
    020    import org.picocontainer.security.CustomPermissionsURLClassLoader;
    021    import org.picocontainer.DefaultPicoContainer;
    022    import org.picocontainer.PicoCompositionException;
    023    import org.picocontainer.NameBinding;
    024    import org.picocontainer.PicoVisitor;
    025    import org.picocontainer.lifecycle.LifecycleState;
    026    import org.picocontainer.classname.ClassPathElement;
    027    import org.picocontainer.classname.ClassLoadingPicoContainer;
    028    import org.picocontainer.behaviors.Caching;
    029    import org.picocontainer.containers.AbstractDelegatingMutablePicoContainer;
    030
    031    import java.lang.annotation.Annotation;
    032    import java.lang.reflect.Type;
    033    import java.net.URL;
    034    import java.security.AccessController;
    035    import java.security.PrivilegedAction;
    036    import java.security.Permissions;
    037    import java.util.ArrayList;
    038    import java.util.Collection;
    039    import java.util.HashMap;
    040    import java.util.Iterator;
    041    import java.util.List;
    042    import java.util.Map;
    043    import java.util.Properties;
    044
    045    /**
    046     * Default implementation of ClassLoadingPicoContainer.
    047     *
    048     * @author Paul Hammant
    049     * @author Mauro Talevi
    050     * @author Michael Rimov
    051     */
    052    @SuppressWarnings("serial")
    053    public class DefaultClassLoadingPicoContainer extends AbstractDelegatingMutablePicoContainer implements
    054            ClassLoadingPicoContainer, ComponentMonitorStrategy {
    055
    056        /**
    057         * Conversion Map to allow for primitives to be boxed to Object types.
    058         */
    059        private static final transient Map<String, String> primitiveNameToBoxedName = new HashMap<String, String>();
    060
    061        static {
    062            primitiveNameToBoxedName.put("int", Integer.class.getName());
    063            primitiveNameToBoxedName.put("byte", Byte.class.getName());
    064            primitiveNameToBoxedName.put("short", Short.class.getName());
    065            primitiveNameToBoxedName.put("long", Long.class.getName());
    066            primitiveNameToBoxedName.put("float", Float.class.getName());
    067            primitiveNameToBoxedName.put("double", Double.class.getName());
    068            primitiveNameToBoxedName.put("boolean", Boolean.class.getName());
    069        }
    070
    071        private final transient List<ClassPathElement> classPathElements = new ArrayList<ClassPathElement>();
    072        private final transient ClassLoader parentClassLoader;
    073
    074        private transient ClassLoader componentClassLoader;
    075        private transient boolean componentClassLoaderLocked;
    076
    077        protected final Map<String, PicoContainer> namedChildContainers = new HashMap<String, PicoContainer>();
    078
    079        public DefaultClassLoadingPicoContainer(ClassLoader classLoader, ComponentFactory componentFactory, PicoContainer parent) {
    080            super(new DefaultPicoContainer(componentFactory, parent));
    081            parentClassLoader = classLoader;
    082        }
    083
    084        public DefaultClassLoadingPicoContainer(ClassLoader classLoader, MutablePicoContainer delegate) {
    085            super(delegate);
    086            parentClassLoader = classLoader;
    087
    088        }
    089
    090        public DefaultClassLoadingPicoContainer(ClassLoader classLoader, PicoContainer parent, ComponentMonitor componentMonitor) {
    091            super(new DefaultPicoContainer(new Caching(), parent));
    092            parentClassLoader = classLoader;
    093            ((ComponentMonitorStrategy) getDelegate()).changeMonitor(componentMonitor);
    094        }
    095
    096        public DefaultClassLoadingPicoContainer(ComponentFactory componentFactory) {
    097            super(new DefaultPicoContainer(componentFactory, null));
    098            parentClassLoader = DefaultClassLoadingPicoContainer.class.getClassLoader();
    099        }
    100
    101
    102        public DefaultClassLoadingPicoContainer(PicoContainer parent) {
    103            super(new DefaultPicoContainer(parent));
    104            parentClassLoader = DefaultClassLoadingPicoContainer.class.getClassLoader();
    105        }
    106
    107        public DefaultClassLoadingPicoContainer(MutablePicoContainer delegate) {
    108            super(delegate);
    109            parentClassLoader = DefaultClassLoadingPicoContainer.class.getClassLoader();
    110        }
    111
    112        public DefaultClassLoadingPicoContainer(ClassLoader classLoader) {
    113            super(new DefaultPicoContainer());
    114            parentClassLoader = classLoader;
    115        }
    116
    117        public DefaultClassLoadingPicoContainer() {
    118            super(new DefaultPicoContainer());
    119            parentClassLoader = DefaultClassLoadingPicoContainer.class.getClassLoader();
    120        }
    121
    122        public DefaultClassLoadingPicoContainer(ComponentFactory componentFactory, LifecycleStrategy lifecycleStrategy,
    123                PicoContainer parent, ClassLoader cl, ComponentMonitor componentMonitor) {
    124
    125            super(new DefaultPicoContainer(componentFactory, lifecycleStrategy, parent, componentMonitor));
    126            parentClassLoader = (cl != null) ? cl : DefaultClassLoadingPicoContainer.class.getClassLoader();
    127        }
    128
    129        protected DefaultClassLoadingPicoContainer createChildContainer() {
    130            MutablePicoContainer child = getDelegate().makeChildContainer();
    131            DefaultClassLoadingPicoContainer container = new DefaultClassLoadingPicoContainer(getComponentClassLoader(), child);
    132            container.changeMonitor(currentMonitor());
    133            return container;
    134        }
    135
    136        /**
    137         * Propagates the monitor change down the delegate chain if a delegate that implements ComponentMonitorStrategy
    138         * exists.  Because of the ComponentMonitorStrategy API, not all delegates can have their API changed.  If
    139         * a delegate implementing ComponentMonitorStrategy cannot be found, an exception is thrown.
    140         * @throws IllegalStateException if no delegate can be found that implements ComponentMonitorStrategy.
    141         * @param monitor the monitor to swap.
    142         */
    143        public void changeMonitor(ComponentMonitor monitor) {
    144
    145            MutablePicoContainer picoDelegate = getDelegate();
    146            while (picoDelegate != null) {
    147                    if (picoDelegate instanceof ComponentMonitorStrategy) {
    148                            ((ComponentMonitorStrategy)picoDelegate).changeMonitor(monitor);
    149                            return;
    150                    }
    151
    152                    if (picoDelegate instanceof AbstractDelegatingMutablePicoContainer) {
    153                            picoDelegate = ((AbstractDelegatingMutablePicoContainer)picoDelegate).getDelegate();
    154                    } else {
    155                            break;
    156                    }
    157            }
    158
    159            throw new IllegalStateException("Could not find delegate picocontainer that implemented ComponentMonitorStrategy");
    160
    161
    162        }
    163
    164        public ComponentMonitor currentMonitor() {
    165            MutablePicoContainer picoDelegate = getDelegate();
    166            while (picoDelegate != null) {
    167                    if (picoDelegate instanceof ComponentMonitorStrategy) {
    168                            return ((ComponentMonitorStrategy)picoDelegate).currentMonitor();
    169                    }
    170
    171                    if (picoDelegate instanceof AbstractDelegatingMutablePicoContainer) {
    172                            picoDelegate = ((AbstractDelegatingMutablePicoContainer)picoDelegate).getDelegate();
    173                    } else {
    174                            break;
    175                    }
    176            }
    177
    178            throw new IllegalStateException("Could not find delegate picocontainer that implemented ComponentMonitorStrategy");
    179        }
    180
    181        public final Object getComponent(Object componentKeyOrType) throws PicoException {
    182
    183            if (componentKeyOrType instanceof ClassName) {
    184                componentKeyOrType = loadClass(componentKeyOrType.toString());
    185            }
    186
    187            Object instance = getDelegate().getComponent(componentKeyOrType);
    188
    189            if (instance != null) {
    190                return instance;
    191            }
    192
    193            ComponentAdapter<?> componentAdapter = null;
    194            if (componentKeyOrType.toString().startsWith("*")) {
    195                String candidateClassName = componentKeyOrType.toString().substring(1);
    196                Collection<ComponentAdapter<?>> cas = getComponentAdapters();
    197                for (ComponentAdapter<?> ca : cas) {
    198                    Object key = ca.getComponentKey();
    199                    if (key instanceof Class && candidateClassName.equals(((Class<?>) key).getName())) {
    200                        componentAdapter = ca;
    201                        break;
    202                    }
    203                }
    204            }
    205            if (componentAdapter != null) {
    206                return componentAdapter.getComponentInstance(this, ComponentAdapter.NOTHING.class);
    207            } else {
    208                return getComponentInstanceFromChildren(componentKeyOrType);
    209            }
    210        }
    211
    212        private Object getComponentInstanceFromChildren(Object componentKey) {
    213            String componentKeyPath = componentKey.toString();
    214            int ix = componentKeyPath.indexOf('/');
    215            if (ix != -1) {
    216                String firstElement = componentKeyPath.substring(0, ix);
    217                String remainder = componentKeyPath.substring(ix + 1, componentKeyPath.length());
    218                Object o = getNamedContainers().get(firstElement);
    219                if (o != null) {
    220                    MutablePicoContainer child = (MutablePicoContainer) o;
    221                    return child.getComponent(remainder);
    222                }
    223            }
    224            return null;
    225        }
    226
    227        public final MutablePicoContainer makeChildContainer() {
    228            return makeChildContainer("containers" + namedChildContainers.size());
    229        }
    230
    231        /**
    232         * Makes a child container with the same basic characteristics of
    233         * <tt>this</tt> object (ComponentFactory, PicoContainer type, Behavior,
    234         * etc)
    235         *
    236         * @param name the name of the child container
    237         * @return The child MutablePicoContainer
    238         */
    239        public ClassLoadingPicoContainer makeChildContainer(String name) {
    240            DefaultClassLoadingPicoContainer child = createChildContainer();
    241            MutablePicoContainer parentDelegate = getDelegate();
    242            parentDelegate.removeChildContainer(child.getDelegate());
    243            parentDelegate.addChildContainer(child);
    244            namedChildContainers.put(name, child);
    245            return child;
    246        }
    247
    248        public boolean removeChildContainer(PicoContainer child) {
    249            boolean result = getDelegate().removeChildContainer(child);
    250            Iterator<Map.Entry<String, PicoContainer>> children = namedChildContainers.entrySet().iterator();
    251            while (children.hasNext()) {
    252                Map.Entry<String, PicoContainer> e = children.next();
    253                PicoContainer pc = e.getValue();
    254                if (pc == child) {
    255                    children.remove();
    256                }
    257            }
    258            return result;
    259        }
    260
    261        protected final Map<String, PicoContainer> getNamedContainers() {
    262            return namedChildContainers;
    263        }
    264
    265        public ClassPathElement addClassLoaderURL(URL url) {
    266            if (componentClassLoaderLocked) {
    267                throw new IllegalStateException("ClassLoader URLs cannot be added once this instance is locked");
    268            }
    269
    270            ClassPathElement classPathElement = new ClassPathElement(url);
    271            classPathElements.add(classPathElement);
    272            return classPathElement;
    273        }
    274
    275        public MutablePicoContainer addComponent(Object implOrInstance) {
    276            if (implOrInstance instanceof ClassName) {
    277                super.addComponent(loadClass(implOrInstance.toString()));
    278            } else {
    279                super.addComponent(implOrInstance);
    280            }
    281            return this;
    282        }
    283
    284        public MutablePicoContainer addComponent(Object key, Object componentImplementationOrInstance,
    285                Parameter... parameters) {
    286            super.addComponent(classNameToClassIfApplicable(key),
    287                    classNameToClassIfApplicable(componentImplementationOrInstance), parameters);
    288            return this;
    289        }
    290
    291        private Object classNameToClassIfApplicable(Object key) {
    292            if (key instanceof ClassName) {
    293                key = loadClass(key.toString());
    294            }
    295            return key;
    296        }
    297
    298        public MutablePicoContainer addAdapter(ComponentAdapter<?> componentAdapter) throws PicoCompositionException {
    299            super.addAdapter(componentAdapter);
    300            return this;
    301        }
    302
    303        public ClassLoader getComponentClassLoader() {
    304            if (componentClassLoader == null) {
    305                componentClassLoaderLocked = true;
    306                componentClassLoader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
    307                    public ClassLoader run() {
    308                        return new CustomPermissionsURLClassLoader(getURLs(classPathElements), makePermissions(),
    309                                parentClassLoader);
    310                    }
    311                });
    312            }
    313            return componentClassLoader;
    314        }
    315
    316        public MutablePicoContainer addChildContainer(PicoContainer child) {
    317            getDelegate().addChildContainer(child);
    318            namedChildContainers.put("containers" + namedChildContainers.size(), child);
    319            return this;
    320        }
    321
    322        public ClassLoadingPicoContainer addChildContainer(String name, PicoContainer child) {
    323
    324            super.addChildContainer(child);
    325
    326            namedChildContainers.put(name, child);
    327            return this;
    328        }
    329
    330        private Class<?> loadClass(final String className) {
    331            ClassLoader classLoader = getComponentClassLoader();
    332            // this is deliberately not a doPrivileged operation.
    333            String cn = getClassName(className);
    334            try {
    335                return classLoader.loadClass(cn);
    336            } catch (ClassNotFoundException e) {
    337                throw new PicoClassNotFoundException(cn, e);
    338            }
    339        }
    340
    341        private Map<URL, Permissions> makePermissions() {
    342            Map<URL, Permissions> permissionsMap = new HashMap<URL, Permissions>();
    343            for (ClassPathElement cpe : classPathElements) {
    344                Permissions permissionCollection = cpe.getPermissionCollection();
    345                permissionsMap.put(cpe.getUrl(), permissionCollection);
    346            }
    347            return permissionsMap;
    348        }
    349
    350        private URL[] getURLs(List<ClassPathElement> classPathElemelements) {
    351            final URL[] urls = new URL[classPathElemelements.size()];
    352            for (int i = 0; i < urls.length; i++) {
    353                urls[i] = (classPathElemelements.get(i)).getUrl();
    354            }
    355            return urls;
    356        }
    357
    358        private static String getClassName(String primitiveOrClass) {
    359            String fromMap = primitiveNameToBoxedName.get(primitiveOrClass);
    360            return fromMap != null ? fromMap : primitiveOrClass;
    361        }
    362
    363        public ComponentAdapter<?> getComponentAdapter(Object componentKey) {
    364            Object componentKey2 = componentKey;
    365            if (componentKey instanceof ClassName) {
    366                componentKey2 = loadClass(componentKey.toString());
    367            }
    368            return super.getComponentAdapter(componentKey2);
    369        }
    370
    371        public MutablePicoContainer change(Properties... properties) {
    372            super.change(properties);
    373            return this;
    374        }
    375
    376        public MutablePicoContainer as(Properties... properties) {
    377            return new AsPropertiesPicoContainer(properties);
    378        }
    379
    380        private class AsPropertiesPicoContainer implements ClassLoadingPicoContainer {
    381            private MutablePicoContainer delegate;
    382
    383            public AsPropertiesPicoContainer(Properties... props) {
    384                delegate = DefaultClassLoadingPicoContainer.this.getDelegate().as(props);
    385            }
    386
    387            public ClassPathElement addClassLoaderURL(URL url) {
    388                return DefaultClassLoadingPicoContainer.this.addClassLoaderURL(url);
    389            }
    390
    391            public ClassLoader getComponentClassLoader() {
    392                return DefaultClassLoadingPicoContainer.this.getComponentClassLoader();
    393            }
    394
    395            public ClassLoadingPicoContainer makeChildContainer(String name) {
    396                return DefaultClassLoadingPicoContainer.this.makeChildContainer(name);
    397            }
    398
    399            public ClassLoadingPicoContainer addChildContainer(String name, PicoContainer child) {
    400                return (ClassLoadingPicoContainer) DefaultClassLoadingPicoContainer.this.addChildContainer(child);
    401            }
    402
    403            public MutablePicoContainer addComponent(Object componentKey, Object componentImplementationOrInstance,
    404                    Parameter... parameters) {
    405                delegate.addComponent(classNameToClassIfApplicable(componentKey),
    406                        classNameToClassIfApplicable(componentImplementationOrInstance), parameters);
    407                return DefaultClassLoadingPicoContainer.this;
    408            }
    409
    410            public MutablePicoContainer addComponent(Object implOrInstance) {
    411                delegate.addComponent(classNameToClassIfApplicable(implOrInstance));
    412                return DefaultClassLoadingPicoContainer.this;
    413            }
    414
    415            public MutablePicoContainer addConfig(String name, Object val) {
    416                delegate.addConfig(name, val);
    417                return DefaultClassLoadingPicoContainer.this;
    418            }
    419
    420            public MutablePicoContainer addAdapter(ComponentAdapter<?> componentAdapter) {
    421                delegate.addAdapter(componentAdapter);
    422                return DefaultClassLoadingPicoContainer.this;
    423            }
    424
    425            public ComponentAdapter removeComponent(Object componentKey) {
    426                return delegate.removeComponent(componentKey);
    427            }
    428
    429            public ComponentAdapter removeComponentByInstance(Object componentInstance) {
    430                return delegate.removeComponentByInstance(componentInstance);
    431            }
    432
    433            public MutablePicoContainer makeChildContainer() {
    434                return DefaultClassLoadingPicoContainer.this.makeChildContainer();
    435            }
    436
    437            public MutablePicoContainer addChildContainer(PicoContainer child) {
    438                return DefaultClassLoadingPicoContainer.this.addChildContainer(child);
    439            }
    440
    441            public boolean removeChildContainer(PicoContainer child) {
    442                return DefaultClassLoadingPicoContainer.this.removeChildContainer(child);
    443            }
    444
    445            public MutablePicoContainer change(Properties... properties) {
    446                return DefaultClassLoadingPicoContainer.this.change(properties);
    447            }
    448
    449            public MutablePicoContainer as(Properties... properties) {
    450                return new AsPropertiesPicoContainer(properties);
    451            }
    452
    453            public Object getComponent(Object componentKeyOrType) {
    454                return DefaultClassLoadingPicoContainer.this.getComponent(componentKeyOrType);
    455            }
    456
    457            public Object getComponent(Object componentKeyOrType, Type into) {
    458                return DefaultClassLoadingPicoContainer.this.getComponent(componentKeyOrType, into);
    459            }
    460
    461            public <T> T getComponent(Class<T> componentType) {
    462                return DefaultClassLoadingPicoContainer.this.getComponent(componentType);
    463            }
    464
    465            public <T> T getComponent(Class<T> componentType, Class<? extends Annotation> binding) {
    466                return DefaultClassLoadingPicoContainer.this.getComponent(componentType, binding);
    467            }
    468
    469            public List<Object> getComponents() {
    470                return DefaultClassLoadingPicoContainer.this.getComponents();
    471            }
    472
    473            public PicoContainer getParent() {
    474                return DefaultClassLoadingPicoContainer.this.getParent();
    475            }
    476
    477            public ComponentAdapter<?> getComponentAdapter(Object componentKey) {
    478                return DefaultClassLoadingPicoContainer.this.getComponentAdapter(componentKey);
    479            }
    480
    481            public <T> ComponentAdapter<T> getComponentAdapter(Class<T> componentType, NameBinding componentNameBinding) {
    482                return DefaultClassLoadingPicoContainer.this.getComponentAdapter(componentType, componentNameBinding);
    483            }
    484
    485            public <T> ComponentAdapter<T> getComponentAdapter(Class<T> componentType, Class<? extends Annotation> binding) {
    486                return DefaultClassLoadingPicoContainer.this.getComponentAdapter(componentType, binding);
    487            }
    488
    489            public Collection<ComponentAdapter<?>> getComponentAdapters() {
    490                return DefaultClassLoadingPicoContainer.this.getComponentAdapters();
    491            }
    492
    493            public <T> List<ComponentAdapter<T>> getComponentAdapters(Class<T> componentType) {
    494                return DefaultClassLoadingPicoContainer.this.getComponentAdapters(componentType);
    495            }
    496
    497            public <T> List<ComponentAdapter<T>> getComponentAdapters(Class<T> componentType,
    498                    Class<? extends Annotation> binding) {
    499                return DefaultClassLoadingPicoContainer.this.getComponentAdapters(componentType, binding);
    500            }
    501
    502            public <T> List<T> getComponents(Class<T> componentType) {
    503                return DefaultClassLoadingPicoContainer.this.getComponents(componentType);
    504            }
    505
    506            public void accept(PicoVisitor visitor) {
    507                DefaultClassLoadingPicoContainer.this.accept(visitor);
    508            }
    509
    510            public void start() {
    511
    512            }
    513
    514            public void stop() {
    515
    516            }
    517
    518            public void dispose() {
    519
    520            }
    521
    522            public void setName(String name) {
    523                DefaultClassLoadingPicoContainer.this.setName(name);
    524            }
    525
    526            public void setLifecycleState(LifecycleState lifecycleState) {
    527                DefaultClassLoadingPicoContainer.this.setLifecycleState(lifecycleState);
    528            }
    529        }
    530
    531    }