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 }