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