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.tools;
018    
019    import java.io.IOException;
020    import java.io.InputStream;
021    import java.util.Properties;
022    
023    import org.apache.commons.discovery.DiscoveryException;
024    import org.apache.commons.discovery.Resource;
025    import org.apache.commons.discovery.ResourceIterator;
026    import org.apache.commons.discovery.resource.ClassLoaders;
027    import org.apache.commons.discovery.resource.DiscoverResources;
028    
029    
030    /**
031     * Mechanisms to locate and load a class.
032     * The load methods locate a class only.
033     * The find methods locate a class and verify that the
034     * class implements an given interface or extends a given class.
035     * 
036     * @author Richard A. Sitze
037     * @author Craig R. McClanahan
038     * @author Costin Manolache
039     */
040    public class ResourceUtils {
041        /**
042         * Get package name.
043         * Not all class loaders 'keep' package information,
044         * in which case Class.getPackage() returns null.
045         * This means that calling Class.getPackage().getName()
046         * is unreliable at best.
047         */
048        public static String getPackageName(Class clazz) {
049            Package clazzPackage = clazz.getPackage();
050            String packageName;
051            if (clazzPackage != null) {
052                packageName = clazzPackage.getName();
053            } else {
054                String clazzName = clazz.getName();
055                packageName = new String(clazzName.toCharArray(), 0, clazzName.lastIndexOf('.'));
056            }
057            return packageName;
058        }
059        
060        
061        /**
062         * Load the resource <code>resourceName</code>.
063         * Try each classloader in succession,
064         * until first succeeds, or all fail.
065         * If all fail and <code>resouceName</code> is not absolute
066         * (doesn't start with '/' character), then retry with
067         * <code>packageName/resourceName</code> after changing all
068         * '.' to '/'.
069         * 
070         * @param resourceName The name of the resource to load.
071         */
072        public static Resource getResource(Class spi,
073                                           String resourceName,
074                                           ClassLoaders loaders)
075            throws DiscoveryException
076        {
077            DiscoverResources explorer = new DiscoverResources(loaders);
078            ResourceIterator resources = explorer.findResources(resourceName);
079            
080            if (spi != null  &&
081                !resources.hasNext()  &&
082                resourceName.charAt(0) != '/')
083            {
084                /**
085                 * If we didn't find the resource, and if the resourceName
086                 * isn't an 'absolute' path name, then qualify with
087                 * package name of the spi.
088                 */
089                resourceName = getPackageName(spi).replace('.','/') + "/" + resourceName;
090                resources = explorer.findResources(resourceName);
091            }
092            
093            return resources.hasNext()
094                   ? resources.nextResource()
095                   : null;
096        }
097        
098        /**
099         * Load named property file, optionally qualifed by spi's package name
100         * as per Class.getResource.
101         * 
102         * A property file is loaded using the following sequence of class loaders:
103         *   <ul>
104         *     <li>Thread Context Class Loader</li>
105         *     <li>DiscoverSingleton's Caller's Class Loader</li>
106         *     <li>SPI's Class Loader</li>
107         *     <li>DiscoverSingleton's (this class) Class Loader</li>
108         *     <li>System Class Loader</li>
109         *   </ul>
110         * 
111         * @param propertiesFileName The property file name.
112         * 
113         * @return Instance of a class implementing the SPI.
114         * 
115         * @exception DiscoveryException Thrown if the name of a class implementing
116         *            the SPI cannot be found, if the class cannot be loaded and
117         *            instantiated, or if the resulting class does not implement
118         *            (or extend) the SPI.
119         */    
120        public static Properties loadProperties(Class spi,
121                                                String propertiesFileName,
122                                                ClassLoaders classLoaders)
123            throws DiscoveryException
124        {
125            Properties properties = null;
126            
127            if (propertiesFileName != null) {
128                try {
129                    Resource resource = getResource(spi, propertiesFileName, classLoaders);
130                    if (resource != null) {
131                        InputStream stream = resource.getResourceAsStream();
132            
133                        if (stream != null) {
134                            properties = new Properties();
135                            try {
136                                properties.load(stream);
137                            } finally {
138                                stream.close();
139                            }
140                        }
141                    }
142                } catch (IOException e) {
143                    // ignore
144                } catch (SecurityException e) {
145                    // ignore
146                }
147            }
148            
149            return properties;
150        }
151    }