View Javadoc
1   package com.atlassian.plugin.util;
2   
3   import java.io.IOException;
4   import java.io.InputStream;
5   import java.net.URL;
6   import java.util.Enumeration;
7   
8   ///CLOVER:OFF
9   
10  /**
11   * This class is extremely useful for loading resources and classes in a fault tolerant manner
12   * that works across different applications servers.
13   * <p>
14   * It has come out of many months of frustrating use of multiple application servers at Atlassian,
15   * please don't change things unless you're sure they're not going to break in one server or another!
16   *
17   * @author $Author: mcannon $
18   * @version $Revision: 1.1 $
19   */
20  public class ClassLoaderUtils {
21      /**
22       * Load a class with a given name.
23       * <p>
24       * It will try to load the class in the following order:
25       * <ul>
26       * <li>From {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()} if non-null
27       * <li>Using the basic {@link Class#forName(java.lang.String) }
28       * <li>From {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
29       * <li>From the {@link Class#getClassLoader() callingClass.getClassLoader() }
30       * </ul>
31       *
32       * @param className    The name of the class to load
33       * @param callingClass The Class object of the calling object
34       * @throws ClassNotFoundException If the class cannot be found anywhere.
35       */
36      public static <T> Class<T> loadClass(final String className, final Class<?> callingClass) throws ClassNotFoundException {
37          final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
38          if (contextClassLoader != null) {
39              try {
40                  return coerce(contextClassLoader.loadClass(className));
41              } catch (final ClassNotFoundException e) {
42                  //fall through
43              }
44          }
45  
46          try {
47              return coerce(Class.forName(className));
48          } catch (final ClassNotFoundException ex) {
49              try {
50                  return coerce(ClassLoaderUtils.class.getClassLoader().loadClass(className));
51              } catch (final ClassNotFoundException exc) {
52                  if ((callingClass != null) && (callingClass.getClassLoader() != null)) {
53                      return coerce(callingClass.getClassLoader().loadClass(className));
54                  } else {
55                      throw exc;
56                  }
57              }
58          }
59      }
60  
61      private static <T> Class<T> coerce(final Class<?> klass) {
62          @SuppressWarnings("unchecked")
63          final Class<T> result = (Class<T>) klass;
64          return result;
65      }
66  
67      /**
68       * Load a given resource.
69       * <p>
70       * This method will try to load the resource using the following methods (in order):
71       * <ul>
72       * <li>From {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
73       * <li>From {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
74       * <li>From the {@link Class#getClassLoader() callingClass.getClassLoader() }
75       * </ul>
76       *
77       * @param resourceName The name of the resource to load
78       * @param callingClass The Class object of the calling object
79       */
80      public static URL getResource(final String resourceName, final Class<?> callingClass) {
81          URL url = null;
82          //JRASERVER-65102 - new tomcat invalidates Thread.currentThread().getContextClassLoader()
83          if (Thread.currentThread().getContextClassLoader() != null) {
84              url = Thread.currentThread().getContextClassLoader().getResource(resourceName);
85          }
86  
87          if (url == null) {
88              url = ClassLoaderUtils.class.getClassLoader().getResource(resourceName);
89          }
90  
91          if (url == null) {
92              url = callingClass.getClassLoader().getResource(resourceName);
93          }
94  
95          return url;
96      }
97  
98      /**
99       * returns all found resources as java.net.URLs.
100      * <p>
101      * This method will try to load the resource using the following methods (in order):
102      * <ul>
103      * <li>From {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
104      * <li>From {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
105      * <li>From the {@link Class#getClassLoader() callingClass.getClassLoader() }
106      * </ul>
107      *
108      * @param resourceName The name of the resource to load
109      * @param callingClass The Class object of the calling object
110      */
111     public static Enumeration<URL> getResources(final String resourceName, final Class<?> callingClass) throws IOException {
112         Enumeration<URL> urls = null;
113         if (Thread.currentThread().getContextClassLoader() != null) {
114             urls = Thread.currentThread().getContextClassLoader().getResources(resourceName);
115         }
116 
117         if (urls == null) {
118             urls = ClassLoaderUtils.class.getClassLoader().getResources(resourceName);
119         }
120 
121         if (urls == null) {
122             urls = callingClass.getClassLoader().getResources(resourceName);
123         }
124 
125         return urls;
126     }
127 
128     /**
129      * This is a convenience method to load a resource as a stream.
130      *
131      * The algorithm used to find the resource is given in getResource()
132      *
133      * @param resourceName The name of the resource to load
134      * @param callingClass The Class object of the calling object
135      */
136     public static InputStream getResourceAsStream(final String resourceName, final Class<?> callingClass) {
137         final URL url = getResource(resourceName, callingClass);
138         try {
139             return url != null ? url.openStream() : null;
140         } catch (final IOException e) {
141             return null;
142         }
143     }
144 
145     /**
146      * Prints the current classloader hierarchy - useful for debugging.
147      */
148     public static void printClassLoader() {
149         System.out.println("ClassLoaderUtils.printClassLoader");
150         printClassLoader(Thread.currentThread().getContextClassLoader());
151     }
152 
153     /**
154      * Prints the classloader hierarchy from a given classloader - useful for debugging.
155      */
156     public static void printClassLoader(final ClassLoader cl) {
157         System.out.println("ClassLoaderUtils.printClassLoader(cl = " + cl + ")");
158         if (cl != null) {
159             printClassLoader(cl.getParent());
160         }
161     }
162 }