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    }