1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.myfaces.trinidad.util;
20
21 import java.io.BufferedReader;
22 import java.io.InputStream;
23 import java.io.IOException;
24
25 import java.io.InputStreamReader;
26
27 import java.net.URL;
28
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.Enumeration;
32 import java.util.HashSet;
33 import java.util.List;
34
35 import java.util.Set;
36
37 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
38
39 /**
40 * Utility methods for accessing classes and resources using an appropriate
41 * class loader.
42 *
43 * @version $Name: $ ($Revision: adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/util/ClassLoaderUtils.java#0 $) $Date: 10-nov-2005.18:49:08 $
44 */
45 public class ClassLoaderUtils
46 {
47 // Utility class only, no instances
48 private ClassLoaderUtils()
49 {
50 }
51
52 /**
53 * Loads the class with the specified name. For Java 2 callers, the
54 * current thread's context class loader is preferred, falling back on the
55 * system class loader of the caller when the current thread's context is not
56 * set, or the caller is pre Java 2.
57 *
58 * @param name the name of the class
59 * @return the resulting <code>Class</code> object
60 * @exception ClassNotFoundException if the class was not found
61 */
62 public static Class<?> loadClass(
63 String name) throws ClassNotFoundException
64 {
65 return loadClass(name, null);
66 }
67
68 /**
69 * Locates the resource with the specified name. For Java 2 callers, the
70 * current thread's context class loader is preferred, falling back on the
71 * system class loader of the caller when the current thread's context is not
72 * set, or the caller is pre Java 2.
73 *
74 * @param name the name of the resource
75 * @return the resulting <code>URL</code> object
76 */
77 public static URL getResource(
78 String name)
79 {
80 return getResource(name, null);
81 }
82
83 /**
84 * Locates the stream resource with the specified name. For Java 2 callers,
85 * the current thread's context class loader is preferred, falling back on
86 * the system class loader of the caller when the current thread's context is
87 * not set, or the caller is pre Java 2.
88 *
89 * @param name the name of the resource
90 * @return the resulting <code>InputStream</code> object
91 */
92 public static InputStream getResourceAsStream(
93 String name)
94 {
95 return getResourceAsStream(name, null);
96 }
97
98 /**
99 * Loads the class with the specified name. For Java 2 callers, the
100 * current thread's context class loader is preferred, falling back on the
101 * class loader of the caller when the current thread's context is not set,
102 * or the caller is pre Java 2. If the callerClassLoader is null, then
103 * fall back on the system class loader.
104 *
105 * @param name the name of the class
106 * @param callerClassLoader the calling class loader context
107 * @return the resulting <code>Class</code> object
108 * @exception ClassNotFoundException if the class was not found
109 */
110 public static Class<?> loadClass(
111 String name,
112 ClassLoader callerClassLoader) throws ClassNotFoundException
113 {
114 Class<?> clazz = null;
115
116 try
117 {
118 ClassLoader loader = getContextClassLoader();
119
120 if (loader != null)
121 clazz = loader.loadClass(name);
122 }
123 catch (ClassNotFoundException e)
124 {
125 // treat as though loader not set
126 ;
127 }
128
129 if (clazz == null)
130 {
131 if (callerClassLoader != null)
132 clazz = callerClassLoader.loadClass(name);
133 else
134 clazz = Class.forName(name);
135 }
136
137 return clazz;
138 }
139
140 /**
141 * Locates the resource with the specified name. For Java 2 callers, the
142 * current thread's context class loader is preferred, falling back on the
143 * class loader of the caller when the current thread's context is not set,
144 * or the caller is pre Java 2. If the callerClassLoader is null, then
145 * fall back on the system class loader.
146 *
147 * @param name the name of the resource
148 * @param callerClassLoader the calling class loader context
149 * @return the resulting <code>URL</code> object
150 */
151 public static URL getResource(
152 String name,
153 ClassLoader callerClassLoader)
154 {
155 _checkResourceName(name);
156
157 URL url = null;
158
159 ClassLoader loader = getContextClassLoader();
160
161 if (loader != null)
162 url = loader.getResource(name);
163
164 if (url == null)
165 {
166 if (callerClassLoader != null)
167 url = callerClassLoader.getResource(name);
168 else
169 url = ClassLoader.getSystemResource(name);
170 }
171
172 return url;
173 }
174
175 /**
176 * Locates the resource stream with the specified name. For Java 2 callers,
177 * the current thread's context class loader is preferred, falling back on
178 * the class loader of the caller when the current thread's context is not
179 * set, or the caller is pre Java 2. If the callerClassLoader is null, then
180 * fall back on the system class loader.
181 *
182 * @param name the name of the resource
183 * @param callerClassLoader the calling class loader context
184 * @return the resulting <code>InputStream</code> object
185 */
186 public static InputStream getResourceAsStream(
187 String name,
188 ClassLoader callerClassLoader)
189 {
190 _checkResourceName(name);
191
192 InputStream stream = null;
193
194 ClassLoader loader = getContextClassLoader();
195
196 if (loader != null)
197 stream = loader.getResourceAsStream(name);
198
199 if (stream == null)
200 {
201 if (callerClassLoader != null)
202 stream = callerClassLoader.getResourceAsStream(name);
203 else
204 stream = ClassLoader.getSystemResourceAsStream(name);
205 }
206
207 return stream;
208 }
209
210 /**
211 * Dynamically accesses the current context class loader.
212 * Returns null if there is no per-thread context class loader.
213 */
214 public static ClassLoader getContextClassLoader()
215 {
216 return Thread.currentThread().getContextClassLoader();
217 }
218
219 /**
220 * Instantiate a service from a file in /META-INF/services.
221 * <P>
222 * The following is an excerpt from the JAR File specification:
223 * A service provider identifies itself by placing a provider-configuration file
224 * in the resource directory META-INF/services.
225 * The file's name should consist of the fully-qualified name of the abstract service class.
226 * The file should contain a newline-separated list of unique concrete provider-class names.
227 * Space and tab characters, as well as blank lines, are ignored. The comment character is '#' (0x23);
228 * on each line all characters following the first comment character are ignored.
229 * The file must be encoded in UTF-8.
230 *
231 * @param service the classname of the abstract service class.
232 * eg: javax.servlet.Filter
233 */
234 @SuppressWarnings("unchecked")
235 public static <T> List<T> getServices(String service)
236 {
237 String serviceUri ="META-INF/services/" + service;
238 ClassLoader loader = Thread.currentThread().getContextClassLoader();
239 try
240 {
241 Enumeration<URL> urls = loader.getResources(serviceUri);
242 if (urls.hasMoreElements())
243 {
244 List<T> services = new ArrayList<T>(1);
245 Set<String> keys = new HashSet<String>(20);
246 do
247 {
248 URL url = urls.nextElement();
249 _LOG.finest("Processing:{0}", url);
250 try
251 {
252 BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
253 try
254 {
255 while(true)
256 {
257 String line = in.readLine();
258 if (line == null)
259 break;
260
261 String className = _parseLine(line);
262
263 if(className!=null && keys.add(className))
264 {
265 T instance = (T) _getClass(loader, className);
266 services.add(instance);
267 }
268 }
269 }
270 finally
271 {
272 in.close();
273 }
274 }
275 catch (Exception e)
276 {
277 _LOG.warning("ERR_PARSING_URL",url);
278 _LOG.warning(e);
279 }
280 }
281 while(urls.hasMoreElements());
282
283 if (services.size() == 1)
284 return Collections.singletonList(services.get(0));
285
286 return Collections.unmodifiableList(services);
287 }
288 }
289 catch (IOException e)
290 {
291 _LOG.severe("ERR_LOADING_RESROUCE",serviceUri);
292 _LOG.severe(e);
293 }
294
295 return Collections.emptyList();
296 }
297
298 private static String _parseLine(String line)
299 {
300 // Eliminate any comments
301 int hashIndex = line.indexOf('#');
302 if (hashIndex >= 0)
303 line = line.substring(0, hashIndex);
304
305 // and any whitespace
306 line = line.trim();
307 if (line.length() > 0)
308 {
309 return line;
310 }
311
312 return null;
313 }
314
315 private static Object _getClass(ClassLoader loader, String className)
316 throws ClassNotFoundException, InstantiationException,
317 IllegalAccessException
318 {
319 Class<?> clazz = loader.loadClass(className);
320 return clazz.newInstance();
321 }
322
323 private static void _checkResourceName(String name)
324 {
325 if ((name != null) && name.startsWith("/"))
326 {
327 _LOG.warning("RESOURCE_NAME_NOT_PORTABLE", name);
328
329 }
330 }
331
332 private static final TrinidadLogger _LOG =
333 TrinidadLogger.createTrinidadLogger(ClassLoaderUtils.class);
334 }