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
83 url = Thread.currentThread().getContextClassLoader().getResource(resourceName);
84
85 if (url == null) {
86 url = ClassLoaderUtils.class.getClassLoader().getResource(resourceName);
87 }
88
89 if (url == null) {
90 url = callingClass.getClassLoader().getResource(resourceName);
91 }
92 return url;
93 }
94
95 /**
96 * returns all found resources as java.net.URLs.
97 * <p>
98 * This method will try to load the resource using the following methods (in order):
99 * <ul>
100 * <li>From {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
101 * <li>From {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
102 * <li>From the {@link Class#getClassLoader() callingClass.getClassLoader() }
103 * </ul>
104 *
105 * @param resourceName The name of the resource to load
106 * @param callingClass The Class object of the calling object
107 */
108 public static Enumeration<URL> getResources(final String resourceName, final Class<?> callingClass) throws IOException {
109 Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(resourceName);
110 if (urls == null) {
111 urls = ClassLoaderUtils.class.getClassLoader().getResources(resourceName);
112 if (urls == null) {
113 urls = callingClass.getClassLoader().getResources(resourceName);
114 }
115 }
116
117 return urls;
118 }
119
120 /**
121 * This is a convenience method to load a resource as a stream.
122 *
123 * The algorithm used to find the resource is given in getResource()
124 *
125 * @param resourceName The name of the resource to load
126 * @param callingClass The Class object of the calling object
127 */
128 public static InputStream getResourceAsStream(final String resourceName, final Class<?> callingClass) {
129 final URL url = getResource(resourceName, callingClass);
130 try {
131 return url != null ? url.openStream() : null;
132 } catch (final IOException e) {
133 return null;
134 }
135 }
136
137 /**
138 * Prints the current classloader hierarchy - useful for debugging.
139 */
140 public static void printClassLoader() {
141 System.out.println("ClassLoaderUtils.printClassLoader");
142 printClassLoader(Thread.currentThread().getContextClassLoader());
143 }
144
145 /**
146 * Prints the classloader hierarchy from a given classloader - useful for debugging.
147 */
148 public static void printClassLoader(final ClassLoader cl) {
149 System.out.println("ClassLoaderUtils.printClassLoader(cl = " + cl + ")");
150 if (cl != null) {
151 printClassLoader(cl.getParent());
152 }
153 }
154 }