1 /**
2 *
3 * Copyright 2005 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.geronimo.kernel.config;
18
19 import java.beans.Introspector;
20 import java.io.IOException;
21 import java.io.ObjectInputStream;
22 import java.io.ObjectOutputStream;
23 import java.io.ObjectStreamClass;
24 import java.lang.reflect.Field;
25 import java.net.URL;
26 import java.net.URLClassLoader;
27 import java.net.URLStreamHandlerFactory;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.Enumeration;
32 import java.util.List;
33 import java.util.Map;
34
35 import org.apache.commons.logging.LogFactory;
36 import org.apache.geronimo.kernel.repository.Artifact;
37
38 /**
39 * A MultiParentClassLoader is a simple extension of the URLClassLoader that simply changes the single parent class
40 * loader model to support a list of parent class loaders. Each operation that accesses a parent, has been replaced
41 * with a operation that checks each parent in order. This getParent method of this class will always return null,
42 * which may be interpreted by the calling code to mean that this class loader is a direct child of the system class
43 * loader.
44 *
45 * @version $Rev: 471382 $ $Date: 2006-11-05 01:01:39 -0800 (Sun, 05 Nov 2006) $
46 */
47 public class MultiParentClassLoader extends URLClassLoader {
48 private final Artifact id;
49 private final ClassLoader[] parents;
50 private final boolean inverseClassLoading;
51 private final String[] hiddenClasses;
52 private final String[] nonOverridableClasses;
53 private final String[] hiddenResources;
54 private final String[] nonOverridableResources;
55 private boolean destroyed = false;
56
57 /**
58 * Creates a named class loader with no parents.
59 *
60 * @param id the id of this class loader
61 * @param urls the urls from which this class loader will classes and resources
62 */
63 public MultiParentClassLoader(Artifact id, URL[] urls) {
64 super(urls);
65 this.id = id;
66 parents = new ClassLoader[]{ClassLoader.getSystemClassLoader()};
67 inverseClassLoading = false;
68 hiddenClasses = new String[0];
69 nonOverridableClasses = new String[0];
70 hiddenResources = new String[0];
71 nonOverridableResources = new String[0];
72 }
73
74
75 /**
76 * Creates a named class loader as a child of the specified parent.
77 *
78 * @param id the id of this class loader
79 * @param urls the urls from which this class loader will classes and resources
80 * @param parent the parent of this class loader
81 */
82 public MultiParentClassLoader(Artifact id, URL[] urls, ClassLoader parent) {
83 this(id, urls, new ClassLoader[]{parent});
84 }
85
86 public MultiParentClassLoader(Artifact id, URL[] urls, ClassLoader parent, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) {
87 this(id, urls, new ClassLoader[]{parent}, inverseClassLoading, hiddenClasses, nonOverridableClasses);
88 }
89
90 /**
91 * Creates a named class loader as a child of the specified parent and using the specified URLStreamHandlerFactory
92 * for accessing the urls..
93 *
94 * @param id the id of this class loader
95 * @param urls the urls from which this class loader will classes and resources
96 * @param parent the parent of this class loader
97 * @param factory the URLStreamHandlerFactory used to access the urls
98 */
99 public MultiParentClassLoader(Artifact id, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
100 this(id, urls, new ClassLoader[]{parent}, factory);
101 }
102
103 /**
104 * Creates a named class loader as a child of the specified parents.
105 *
106 * @param id the id of this class loader
107 * @param urls the urls from which this class loader will classes and resources
108 * @param parents the parents of this class loader
109 */
110 public MultiParentClassLoader(Artifact id, URL[] urls, ClassLoader[] parents) {
111 super(urls);
112 this.id = id;
113 this.parents = copyParents(parents);
114 inverseClassLoading = false;
115 hiddenClasses = new String[0];
116 nonOverridableClasses = new String[0];
117 hiddenResources = new String[0];
118 nonOverridableResources = new String[0];
119 }
120
121 public MultiParentClassLoader(Artifact id, URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, Collection hiddenClasses, Collection nonOverridableClasses) {
122 this(id, urls, parents, inverseClassLoading, (String[]) hiddenClasses.toArray(new String[hiddenClasses.size()]), (String[]) nonOverridableClasses.toArray(new String[nonOverridableClasses.size()]));
123 }
124
125 public MultiParentClassLoader(Artifact id, URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) {
126 super(urls);
127 this.id = id;
128 this.parents = copyParents(parents);
129 this.inverseClassLoading = inverseClassLoading;
130 this.hiddenClasses = hiddenClasses;
131 this.nonOverridableClasses = nonOverridableClasses;
132 hiddenResources = toResources(hiddenClasses);
133 nonOverridableResources = toResources(nonOverridableClasses);
134 }
135
136 public MultiParentClassLoader(MultiParentClassLoader source) {
137 this(source.id, source.getURLs(), deepCopyParents(source.parents), source.inverseClassLoading, source.hiddenClasses, source.nonOverridableClasses);
138 }
139
140 static ClassLoader copy(ClassLoader source) {
141 if (source instanceof MultiParentClassLoader) {
142 return new MultiParentClassLoader((MultiParentClassLoader) source);
143 } else if (source instanceof URLClassLoader) {
144 return new URLClassLoader(((URLClassLoader)source).getURLs(), source.getParent());
145 } else {
146 return new URLClassLoader(new URL[0], source);
147 }
148 }
149
150 ClassLoader copy() {
151 return MultiParentClassLoader.copy(this);
152 }
153
154 private String[] toResources(String[] classes) {
155 String[] resources = new String[classes.length];
156 for (int i = 0; i < classes.length; i++) {
157 String className = classes[i];
158 resources[i] = className.replace('.', '/');
159 }
160 return resources;
161 }
162
163 /**
164 * Creates a named class loader as a child of the specified parents and using the specified URLStreamHandlerFactory
165 * for accessing the urls..
166 *
167 * @param id the id of this class loader
168 * @param urls the urls from which this class loader will classes and resources
169 * @param parents the parents of this class loader
170 * @param factory the URLStreamHandlerFactory used to access the urls
171 */
172 public MultiParentClassLoader(Artifact id, URL[] urls, ClassLoader[] parents, URLStreamHandlerFactory factory) {
173 super(urls, null, factory);
174 this.id = id;
175 this.parents = copyParents(parents);
176 inverseClassLoading = false;
177 hiddenClasses = new String[0];
178 nonOverridableClasses = new String[0];
179 hiddenResources = new String[0];
180 nonOverridableResources = new String[0];
181 }
182
183 private static ClassLoader[] copyParents(ClassLoader[] parents) {
184 ClassLoader[] newParentsArray = new ClassLoader[parents.length];
185 for (int i = 0; i < parents.length; i++) {
186 ClassLoader parent = parents[i];
187 if (parent == null) {
188 throw new NullPointerException("parent[" + i + "] is null");
189 }
190 newParentsArray[i] = parent;
191 }
192 return newParentsArray;
193 }
194
195 private static ClassLoader[] deepCopyParents(ClassLoader[] parents) {
196 ClassLoader[] newParentsArray = new ClassLoader[parents.length];
197 for (int i = 0; i < parents.length; i++) {
198 ClassLoader parent = parents[i];
199 if (parent == null) {
200 throw new NullPointerException("parent[" + i + "] is null");
201 }
202 if (parent instanceof MultiParentClassLoader) {
203 parent = ((MultiParentClassLoader)parent).copy();
204 }
205 newParentsArray[i] = parent;
206 }
207 return newParentsArray;
208 }
209
210 /**
211 * Gets the id of this class loader.
212 *
213 * @return the id of this class loader
214 */
215 public Artifact getId() {
216 return id;
217 }
218
219 /**
220 * Gets the parents of this class loader.
221 *
222 * @return the parents of this class loader
223 */
224 public ClassLoader[] getParents() {
225 return parents;
226 }
227
228 public void addURL(URL url) {
229
230 super.addURL(url);
231 }
232
233 protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
234
235
236
237 Class cachedClass = findLoadedClass(name);
238 if (cachedClass != null) {
239 return resolveClass(cachedClass, resolve);
240 }
241
242
243
244
245 if (inverseClassLoading && !isDestroyed() && !isNonOverridableClass(name)) {
246 try {
247 Class clazz = findClass(name);
248 return resolveClass(clazz, resolve);
249 } catch (ClassNotFoundException ignored) {
250 }
251 }
252
253
254
255
256 if (!isHiddenClass(name)) {
257 for (int i = 0; i < parents.length; i++) {
258 ClassLoader parent = parents[i];
259 try {
260 Class clazz = parent.loadClass(name);
261 return resolveClass(clazz, resolve);
262 } catch (ClassNotFoundException ignored) {
263
264 }
265 }
266 }
267
268
269
270
271
272
273
274 if (!isDestroyed()) {
275 try {
276 Class clazz = findClass(name);
277 return resolveClass(clazz, resolve);
278 } catch (ClassNotFoundException ignored) {
279 }
280 }
281
282 throw new ClassNotFoundException(name + " in classloader " + id);
283 }
284
285 private boolean isNonOverridableClass(String name) {
286 for (int i = 0; i < nonOverridableClasses.length; i++) {
287 if (name.startsWith(nonOverridableClasses[i])) {
288 return true;
289 }
290 }
291 return false;
292 }
293
294 private boolean isHiddenClass(String name) {
295 for (int i = 0; i < hiddenClasses.length; i++) {
296 if (name.startsWith(hiddenClasses[i])) {
297 return true;
298 }
299 }
300 return false;
301 }
302
303 private Class resolveClass(Class clazz, boolean resolve) {
304 if (resolve) {
305 resolveClass(clazz);
306 }
307 return clazz;
308 }
309
310 public URL getResource(String name) {
311 if (isDestroyed()) {
312 return null;
313 }
314
315
316
317
318 if (inverseClassLoading && !isDestroyed() && !isNonOverridableResource(name)) {
319 URL url = findResource(name);
320 if (url != null) {
321 return url;
322 }
323 }
324
325
326
327
328 if (!isHiddenResource(name)) {
329 for (int i = 0; i < parents.length; i++) {
330 ClassLoader parent = parents[i];
331 URL url = parent.getResource(name);
332 if (url != null) {
333 return url;
334 }
335 }
336 }
337
338
339
340
341
342
343
344 if (!isDestroyed()) {
345
346 return findResource(name);
347 }
348
349 return null;
350 }
351
352 public Enumeration findResources(String name) throws IOException {
353 if (isDestroyed()) {
354 return Collections.enumeration(Collections.EMPTY_SET);
355 }
356
357 List resources = new ArrayList();
358
359
360
361
362 if (inverseClassLoading && !isDestroyed()) {
363 List myResources = Collections.list(super.findResources(name));
364 resources.addAll(myResources);
365 }
366
367
368
369
370 for (int i = 0; i < parents.length; i++) {
371 ClassLoader parent = parents[i];
372 List parentResources = Collections.list(parent.getResources(name));
373 resources.addAll(parentResources);
374 }
375
376
377
378
379 if (!inverseClassLoading && !isDestroyed()) {
380 List myResources = Collections.list(super.findResources(name));
381 resources.addAll(myResources);
382 }
383
384 return Collections.enumeration(resources);
385 }
386
387 private boolean isNonOverridableResource(String name) {
388 for (int i = 0; i < nonOverridableResources.length; i++) {
389 if (name.startsWith(nonOverridableResources[i])) {
390 return true;
391 }
392 }
393 return false;
394 }
395
396 private boolean isHiddenResource(String name) {
397 for (int i = 0; i < hiddenResources.length; i++) {
398 if (name.startsWith(hiddenResources[i])) {
399 return true;
400 }
401 }
402 return false;
403 }
404
405 public String toString() {
406 return "[" + getClass().getName() + " id=" + id + "]";
407 }
408
409 public synchronized boolean isDestroyed() {
410 return destroyed;
411 }
412
413 public void destroy() {
414 synchronized(this) {
415 if (destroyed) return;
416 destroyed = true;
417 }
418
419 LogFactory.release(this);
420 clearSoftCache(ObjectInputStream.class, "subclassAudits");
421 clearSoftCache(ObjectOutputStream.class, "subclassAudits");
422 clearSoftCache(ObjectStreamClass.class, "localDescs");
423 clearSoftCache(ObjectStreamClass.class, "reflectors");
424
425
426
427
428 Introspector.flushCaches();
429 }
430
431 private static final Object lock = new Object();
432 private static boolean clearSoftCacheFailed = false;
433
434 private static void clearSoftCache(Class clazz, String fieldName) {
435 Map cache = null;
436 try {
437 Field f = clazz.getDeclaredField(fieldName);
438 f.setAccessible(true);
439 cache = (Map) f.get(null);
440 } catch (Throwable e) {
441 synchronized (lock) {
442 if (!clearSoftCacheFailed) {
443 clearSoftCacheFailed = true;
444 LogFactory.getLog(MultiParentClassLoader.class).debug("Unable to clear SoftCache field " + fieldName + " in class " + clazz);
445 }
446 }
447 }
448
449 if (cache != null) {
450 synchronized (cache) {
451 cache.clear();
452 }
453 }
454 }
455
456 }