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