001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.discovery.resource;
018    
019    import java.util.Vector;
020    
021    import org.apache.commons.discovery.jdk.JDKHooks;
022    
023    
024    /**
025     * There are many different contexts in which
026     * loaders can be used.  This provides a holder
027     * for a set of class loaders, so that they
028     * don't have to be build back up everytime...
029     *
030     * @author Richard A. Sitze
031     * @author Craig R. McClanahan
032     * @author Costin Manolache
033     */
034    public class ClassLoaders
035    {
036        protected Vector classLoaders = new Vector();
037        
038        /** Construct a new class loader set
039         */
040        public ClassLoaders() {
041        }
042        
043        public int size() {
044            return classLoaders.size();
045        }
046        
047        public ClassLoader get(int idx) {
048            return (ClassLoader)classLoaders.elementAt(idx);
049        }
050    
051        /**
052         * Specify a new class loader to be used in searching.
053         * The order of loaders determines the order of the result.
054         * It is recommended to add the most specific loaders first.
055         */
056        public void put(ClassLoader classLoader) {
057            if (classLoader != null) {
058                classLoaders.addElement(classLoader);
059            }
060        }
061        
062    
063        /**
064         * Specify a new class loader to be used in searching.
065         * The order of loaders determines the order of the result.
066         * It is recommended to add the most specific loaders first.
067         * 
068         * @param prune if true, verify that the class loader is
069         *              not an Ancestor (@see isAncestor) before
070         *              adding it to our list.
071         */
072        public void put(ClassLoader classLoader, boolean prune) {
073            if (classLoader != null  &&  !(prune && isAncestor(classLoader))) {
074                classLoaders.addElement(classLoader);
075            }
076        }
077        
078        
079        /**
080         * Check to see if <code>classLoader</code> is an
081         * ancestor of any contained class loader.
082         * 
083         * This can be used to eliminate redundant class loaders
084         * IF all class loaders defer to parent class loaders
085         * before resolving a class.
086         * 
087         * It may be that this is not always true.  Therefore,
088         * this check is not done internally to eliminate
089         * redundant class loaders, but left to the discretion
090         * of the user.
091         */
092        public boolean isAncestor(final ClassLoader classLoader) {
093            /* bootstrap classloader, at root of all trees! */
094            if (classLoader == null)
095                return true;
096    
097            for (int idx = 0; idx < size(); idx++) {
098                for(ClassLoader walker = get(idx);
099                    walker != null;
100                    walker = walker.getParent())
101                {
102                    if (walker == classLoader) {
103                        return true;
104                    }
105                }
106            }
107            return false;
108        }
109    
110    
111        /**
112         * Utility method.  Returns a preloaded ClassLoaders instance
113         * containing the following class loaders, in order:
114         * 
115         * <ul>
116         *   <li>spi.getClassLoader</li>
117         *   <li>seeker.getClassLoader</li>
118         *   <li>System Class Loader</li>
119         * </ul>
120         * 
121         * Note that the thread context class loader is NOT present.
122         * This is a reasonable set of loaders to try if the resource to be found
123         * should be restricted to a libraries containing the SPI and Factory.
124         * 
125         * @param spi WHAT is being looked for (an implementation of this class,
126         *            a default property file related to this class).
127         * @param factory WHO is performing the lookup.
128         * @param prune Determines if ancestors are allowed to be loaded or not.
129         */    
130        public static ClassLoaders getLibLoaders(Class spi, Class factory, boolean prune) {
131            ClassLoaders loaders = new ClassLoaders();
132            
133            if (spi != null) loaders.put(spi.getClassLoader());
134            if (factory != null) loaders.put(factory.getClassLoader(), prune);
135            loaders.put(JDKHooks.getJDKHooks().getSystemClassLoader(), prune);
136            
137            return loaders;
138        }
139        
140        /**
141         * Utility method.  Returns a preloaded ClassLoaders instance
142         * containing the following class loaders, in order:
143         * 
144         * <ul>
145         *   <li>Thread Context Class Loader</li>
146         *   <li>spi.getClassLoader</li>
147         *   <li>seeker.getClassLoader</li>
148         *   <li>System Class Loader</li>
149         * </ul>
150         * 
151         * Note that the thread context class loader IS  present.
152         * This is a reasonable set of loaders to try if the resource to be found
153         * may be provided by an application.
154         * 
155         * @param spi WHAT is being looked for (an implementation of this class,
156         *            a default property file related to this class).
157         * @param factory WHO is performing the lookup (factory).
158         * @param prune Determines if ancestors are allowed to be loaded or not.
159         */    
160        public static ClassLoaders getAppLoaders(Class spi, Class factory, boolean prune) {
161            ClassLoaders loaders = new ClassLoaders();
162    
163            loaders.put(JDKHooks.getJDKHooks().getThreadContextClassLoader());
164            if (spi != null) loaders.put(spi.getClassLoader(), prune);
165            if (factory != null) loaders.put(factory.getClassLoader(), prune);
166            loaders.put(JDKHooks.getJDKHooks().getSystemClassLoader(), prune);
167            
168            return loaders;
169        }
170    }