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