1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.discovery.tools;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.util.Properties;
22
23 import org.apache.commons.discovery.DiscoveryException;
24 import org.apache.commons.discovery.Resource;
25 import org.apache.commons.discovery.ResourceIterator;
26 import org.apache.commons.discovery.resource.ClassLoaders;
27 import org.apache.commons.discovery.resource.DiscoverResources;
28
29 /**
30 * Mechanisms to locate and load a class.
31 *
32 * The load methods locate a class only.
33 * The find methods locate a class and verify that the
34 * class implements an given interface or extends a given class.
35 */
36 public class ResourceUtils {
37
38 /**
39 * Get package name.
40 *
41 * Not all class loaders 'keep' package information,
42 * in which case Class.getPackage() returns null.
43 * This means that calling Class.getPackage().getName()
44 * is unreliable at best.
45 *
46 * @param clazz The class from which the package has to be extracted
47 * @return The string representation of the input class package
48 */
49 public static String getPackageName(Class<?> clazz) {
50 Package clazzPackage = clazz.getPackage();
51 String packageName;
52 if (clazzPackage != null) {
53 packageName = clazzPackage.getName();
54 } else {
55 String clazzName = clazz.getName();
56 packageName = new String(clazzName.toCharArray(), 0, clazzName.lastIndexOf('.'));
57 }
58 return packageName;
59 }
60
61 /**
62 * Load the resource <code>resourceName</code>.
63 *
64 * Try each classloader in succession,
65 * until first succeeds, or all fail.
66 * If all fail and <code>resouceName</code> is not absolute
67 * (doesn't start with '/' character), then retry with
68 * <code>packageName/resourceName</code> after changing all
69 * '.' to '/'.
70 *
71 * @param spi The SPI type
72 * @param resourceName The name of the resource to load.
73 * @param loaders the class loaders holder
74 * @return The discovered {@link Resource} instance
75 * @throws DiscoveryException if the class implementing
76 * the SPI cannot be found, cannot be loaded and
77 * instantiated, or if the resulting class does not implement
78 * (or extend) the SPI
79 */
80 public static Resource getResource(Class<?> spi,
81 String resourceName,
82 ClassLoaders loaders) throws DiscoveryException {
83 DiscoverResources explorer = new DiscoverResources(loaders);
84 ResourceIterator resources = explorer.findResources(resourceName);
85
86 if (spi != null &&
87 !resources.hasNext() &&
88 resourceName.charAt(0) != '/') {
89 /**
90 * If we didn't find the resource, and if the resourceName
91 * isn't an 'absolute' path name, then qualify with
92 * package name of the spi.
93 */
94 resourceName = getPackageName(spi).replace('.','/') + "/" + resourceName;
95 resources = explorer.findResources(resourceName);
96 }
97
98 return resources.hasNext()
99 ? 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 }