1 /*
2 * Copyright (c) 2002-2003 by OpenSymphony
3 * All rights reserved.
4 */
5 package com.opensymphony.xwork2.util;
6
7 import java.io.IOException;
8 import java.io.InputStream;
9
10 import java.net.URL;
11 import java.util;
12
13
14 /**
15 * This class is extremely useful for loading resources and classes in a fault tolerant manner
16 * that works across different applications servers.
17 *
18 * It has come out of many months of frustrating use of multiple application servers at Atlassian,
19 * please don't change things unless you're sure they're not going to break in one server or another!
20 *
21 * It was brought in from oscore trunk revision 147.
22 *
23 * @author $Author: hani $
24 * @version $Revision: 117 $
25 */
26 public class ClassLoaderUtil {
27 //~ Methods ////////////////////////////////////////////////////////////////
28
29 /**
30 * Load all resources with a given name, potentially aggregating all results
31 * from the searched classloaders. If no results are found, the resource name
32 * is prepended by '/' and tried again.
33 *
34 * This method will try to load the resources using the following methods (in order):
35 * <ul>
36 * <li>From Thread.currentThread().getContextClassLoader()
37 * <li>From ClassLoaderUtil.class.getClassLoader()
38 * <li>callingClass.getClassLoader()
39 * </ul>
40 *
41 * @param resourceName The name of the resources to load
42 * @param callingClass The Class object of the calling object
43 */
44 public static Iterator<URL> getResources(String resourceName, Class callingClass, boolean aggregate) throws IOException {
45
46 AggregateIterator<URL> iterator = new AggregateIterator<URL>();
47
48 iterator.addEnumeration(Thread.currentThread().getContextClassLoader().getResources(resourceName));
49
50 if (!iterator.hasNext() || aggregate) {
51 iterator.addEnumeration(ClassLoaderUtil.class.getClassLoader().getResources(resourceName));
52 }
53
54 if (!iterator.hasNext() || aggregate) {
55 ClassLoader cl = callingClass.getClassLoader();
56
57 if (cl != null) {
58 iterator.addEnumeration(cl.getResources(resourceName));
59 }
60 }
61
62 if (!iterator.hasNext() && (resourceName != null) && (resourceName.charAt(0) != '/')) {
63 return getResources('/' + resourceName, callingClass, aggregate);
64 }
65
66 return iterator;
67 }
68
69 /**
70 * Load a given resource.
71 *
72 * This method will try to load the resource using the following methods (in order):
73 * <ul>
74 * <li>From Thread.currentThread().getContextClassLoader()
75 * <li>From ClassLoaderUtil.class.getClassLoader()
76 * <li>callingClass.getClassLoader()
77 * </ul>
78 *
79 * @param resourceName The name IllegalStateException("Unable to call ")of the resource to load
80 * @param callingClass The Class object of the calling object
81 */
82 public static URL getResource(String resourceName, Class callingClass) {
83 URL url = Thread.currentThread().getContextClassLoader().getResource(resourceName);
84
85 if (url == null) {
86 url = ClassLoaderUtil.class.getClassLoader().getResource(resourceName);
87 }
88
89 if (url == null) {
90 ClassLoader cl = callingClass.getClassLoader();
91
92 if (cl != null) {
93 url = cl.getResource(resourceName);
94 }
95 }
96
97 if ((url == null) && (resourceName != null) && (resourceName.charAt(0) != '/')) {
98 return getResource('/' + resourceName, callingClass);
99 }
100
101 return url;
102 }
103
104 /**
105 * This is a convenience method to load a resource as a stream.
106 *
107 * The algorithm used to find the resource is given in getResource()
108 *
109 * @param resourceName The name of the resource to load
110 * @param callingClass The Class object of the calling object
111 */
112 public static InputStream getResourceAsStream(String resourceName, Class callingClass) {
113 URL url = getResource(resourceName, callingClass);
114
115 try {
116 return (url != null) ? url.openStream() : null;
117 } catch (IOException e) {
118 return null;
119 }
120 }
121
122 /**
123 * Load a class with a given name.
124 *
125 * It will try to load the class in the following order:
126 * <ul>
127 * <li>From Thread.currentThread().getContextClassLoader()
128 * <li>Using the basic Class.forName()
129 * <li>From ClassLoaderUtil.class.getClassLoader()
130 * <li>From the callingClass.getClassLoader()
131 * </ul>
132 *
133 * @param className The name of the class to load
134 * @param callingClass The Class object of the calling object
135 * @throws ClassNotFoundException If the class cannot be found anywhere.
136 */
137 public static Class loadClass(String className, Class callingClass) throws ClassNotFoundException {
138 try {
139 return Thread.currentThread().getContextClassLoader().loadClass(className);
140 } catch (ClassNotFoundException e) {
141 try {
142 return Class.forName(className);
143 } catch (ClassNotFoundException ex) {
144 try {
145 return ClassLoaderUtil.class.getClassLoader().loadClass(className);
146 } catch (ClassNotFoundException exc) {
147 return callingClass.getClassLoader().loadClass(className);
148 }
149 }
150 }
151 }
152
153 /**
154 * Aggregates Enumeration instances into one iterator and filters out duplicates. Always keeps one
155 * ahead of the enumerator to protect against returning duplicates.
156 */
157 protected static class AggregateIterator<E> implements Iterator<E> {
158
159 LinkedList<Enumeration<E>> enums = new LinkedList<Enumeration<E>>();
160 Enumeration<E> cur = null;
161 E next = null;
162 Set<E> loaded = new HashSet<E>();
163
164 public AggregateIterator addEnumeration(Enumeration<E> e) {
165 if (e.hasMoreElements()) {
166 if (cur == null) {
167 cur = e;
168 next = e.nextElement();
169 loaded.add(next);
170 } else {
171 enums.add(e);
172 }
173 }
174 return this;
175 }
176
177 public boolean hasNext() {
178 return (next != null);
179 }
180
181 public E next() {
182 if (next != null) {
183 E prev = next;
184 next = loadNext();
185 return prev;
186 } else {
187 throw new NoSuchElementException();
188 }
189 }
190
191 private Enumeration<E> determineCurrentEnumeration() {
192 if (cur != null && !cur.hasMoreElements()) {
193 if (enums.size() > 0) {
194 cur = enums.removeLast();
195 } else {
196 cur = null;
197 }
198 }
199 return cur;
200 }
201
202 private E loadNext() {
203 if (determineCurrentEnumeration() != null) {
204 E tmp = cur.nextElement();
205 while (loaded.contains(tmp)) {
206 tmp = loadNext();
207 if (tmp == null) {
208 break;
209 }
210 }
211 if (tmp != null) {
212 loaded.add(tmp);
213 }
214 return tmp;
215 }
216 return null;
217
218 }
219
220 public void remove() {
221 throw new UnsupportedOperationException();
222 }
223 }
224 }