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