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