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 }