View Javadoc

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 final 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 }