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    }