KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > geronimo > kernel > config > MultiParentClassLoader


1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. 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 JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.ObjectInputStream JavaDoc;
22 import java.io.ObjectOutputStream JavaDoc;
23 import java.io.ObjectStreamClass JavaDoc;
24 import java.lang.reflect.Field JavaDoc;
25 import java.net.URL JavaDoc;
26 import java.net.URLClassLoader JavaDoc;
27 import java.net.URLStreamHandlerFactory JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.Collection JavaDoc;
30 import java.util.Collections JavaDoc;
31 import java.util.Enumeration JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
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: 476049 $ $Date: 2006-11-16 23:35:17 -0500 (Thu, 16 Nov 2006) $
46  */

47 public class MultiParentClassLoader extends URLClassLoader JavaDoc {
48     private final Artifact id;
49     private final ClassLoader JavaDoc[] parents;
50     private final boolean inverseClassLoading;
51     private final String JavaDoc[] hiddenClasses;
52     private final String JavaDoc[] nonOverridableClasses;
53     private final String JavaDoc[] hiddenResources;
54     private final String JavaDoc[] 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 JavaDoc[] urls) {
64         super(urls);
65         this.id = id;
66         parents = new ClassLoader JavaDoc[]{ClassLoader.getSystemClassLoader()};
67         inverseClassLoading = false;
68         hiddenClasses = new String JavaDoc[0];
69         nonOverridableClasses = new String JavaDoc[0];
70         hiddenResources = new String JavaDoc[0];
71         nonOverridableResources = new String JavaDoc[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 JavaDoc[] urls, ClassLoader JavaDoc parent) {
83         this(id, urls, new ClassLoader JavaDoc[]{parent});
84     }
85
86     public MultiParentClassLoader(Artifact id, URL JavaDoc[] urls, ClassLoader JavaDoc parent, boolean inverseClassLoading, String JavaDoc[] hiddenClasses, String JavaDoc[] nonOverridableClasses) {
87         this(id, urls, new ClassLoader JavaDoc[]{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 JavaDoc[] urls, ClassLoader JavaDoc parent, URLStreamHandlerFactory JavaDoc factory) {
100         this(id, urls, new ClassLoader JavaDoc[]{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 JavaDoc[] urls, ClassLoader JavaDoc[] parents) {
111         super(urls);
112         this.id = id;
113         this.parents = copyParents(parents);
114         inverseClassLoading = false;
115         hiddenClasses = new String JavaDoc[0];
116         nonOverridableClasses = new String JavaDoc[0];
117         hiddenResources = new String JavaDoc[0];
118         nonOverridableResources = new String JavaDoc[0];
119     }
120
121     public MultiParentClassLoader(Artifact id, URL JavaDoc[] urls, ClassLoader JavaDoc[] parents, boolean inverseClassLoading, Collection JavaDoc hiddenClasses, Collection JavaDoc nonOverridableClasses) {
122         this(id, urls, parents, inverseClassLoading, (String JavaDoc[]) hiddenClasses.toArray(new String JavaDoc[hiddenClasses.size()]), (String JavaDoc[]) nonOverridableClasses.toArray(new String JavaDoc[nonOverridableClasses.size()]));
123     }
124
125     public MultiParentClassLoader(Artifact id, URL JavaDoc[] urls, ClassLoader JavaDoc[] parents, boolean inverseClassLoading, String JavaDoc[] hiddenClasses, String JavaDoc[] 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 JavaDoc copy(ClassLoader JavaDoc source) {
141         if (source instanceof MultiParentClassLoader) {
142             return new MultiParentClassLoader((MultiParentClassLoader) source);
143         } else if (source instanceof URLClassLoader JavaDoc) {
144             return new URLClassLoader JavaDoc(((URLClassLoader JavaDoc)source).getURLs(), source.getParent());
145         } else {
146             return new URLClassLoader JavaDoc(new URL JavaDoc[0], source);
147         }
148     }
149
150     ClassLoader JavaDoc copy() {
151         return MultiParentClassLoader.copy(this);
152     }
153
154     private String JavaDoc[] toResources(String JavaDoc[] classes) {
155         String JavaDoc[] resources = new String JavaDoc[classes.length];
156         for (int i = 0; i < classes.length; i++) {
157             String JavaDoc 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 JavaDoc[] urls, ClassLoader JavaDoc[] parents, URLStreamHandlerFactory JavaDoc factory) {
173         super(urls, null, factory);
174         this.id = id;
175         this.parents = copyParents(parents);
176         inverseClassLoading = false;
177         hiddenClasses = new String JavaDoc[0];
178         nonOverridableClasses = new String JavaDoc[0];
179         hiddenResources = new String JavaDoc[0];
180         nonOverridableResources = new String JavaDoc[0];
181     }
182
183     private static ClassLoader JavaDoc[] copyParents(ClassLoader JavaDoc[] parents) {
184         ClassLoader JavaDoc[] newParentsArray = new ClassLoader JavaDoc[parents.length];
185         for (int i = 0; i < parents.length; i++) {
186             ClassLoader JavaDoc parent = parents[i];
187             if (parent == null) {
188                 throw new NullPointerException JavaDoc("parent[" + i + "] is null");
189             }
190             newParentsArray[i] = parent;
191         }
192         return newParentsArray;
193     }
194
195     private static ClassLoader JavaDoc[] deepCopyParents(ClassLoader JavaDoc[] parents) {
196         ClassLoader JavaDoc[] newParentsArray = new ClassLoader JavaDoc[parents.length];
197         for (int i = 0; i < parents.length; i++) {
198             ClassLoader JavaDoc parent = parents[i];
199             if (parent == null) {
200                 throw new NullPointerException JavaDoc("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 JavaDoc[] getParents() {
225         return parents;
226     }
227
228     public void addURL(URL JavaDoc url) {
229         // todo this needs a security check
230
super.addURL(url);
231     }
232
233     protected synchronized Class JavaDoc loadClass(String JavaDoc name, boolean resolve) throws ClassNotFoundException JavaDoc {
234         //
235
// Check if class is in the loaded classes cache
236
//
237
Class JavaDoc cachedClass = findLoadedClass(name);
238         if (cachedClass != null) {
239             return resolveClass(cachedClass, resolve);
240         }
241         
242         // This is a reasonable hack. We can add some classes to the list below.
243
// Since we know these classes are in the system class loader let's not waste our
244
// time going through the hierarchy.
245
//
246
// The order is based on profiling the server. It may not be optimal for all
247
// workloads.
248

249         if ( name.startsWith("java.") ||
250              name.equals("boolean") ||
251              name.equals("int") ||
252              name.equals("double") ||
253              name.equals("long")) {
254             Class JavaDoc clazz = ClassLoader.getSystemClassLoader().loadClass(name);
255             return resolveClass(clazz, resolve);
256         }
257         
258         //
259
// if we are using inverse class loading, check local urls first
260
//
261
if (inverseClassLoading && !isDestroyed() && !isNonOverridableClass(name)) {
262             try {
263                 Class JavaDoc clazz = findClass(name);
264                 return resolveClass(clazz, resolve);
265             } catch (ClassNotFoundException JavaDoc ignored) {
266             }
267         }
268
269         //
270
// Check parent class loaders
271
//
272
if (!isHiddenClass(name)) {
273             for (int i = 0; i < parents.length; i++) {
274                 ClassLoader JavaDoc parent = parents[i];
275                 try {
276                     Class JavaDoc clazz = parent.loadClass(name);
277                     return resolveClass(clazz, resolve);
278                 } catch (ClassNotFoundException JavaDoc ignored) {
279                     // this parent didn't have the class; try the next one
280
}
281             }
282         }
283
284         //
285
// if we are not using inverse class loading, check local urls now
286
//
287
// don't worry about excluding non-overridable classes here... we
288
// have alredy checked he parent and the parent didn't have the
289
// class, so we can override now
290
if (!isDestroyed()) {
291             try {
292                 Class JavaDoc clazz = findClass(name);
293                 return resolveClass(clazz, resolve);
294             } catch (ClassNotFoundException JavaDoc ignored) {
295             }
296         }
297
298         throw new ClassNotFoundException JavaDoc(name + " in classloader " + id);
299     }
300
301     private boolean isNonOverridableClass(String JavaDoc name) {
302         for (int i = 0; i < nonOverridableClasses.length; i++) {
303             if (name.startsWith(nonOverridableClasses[i])) {
304                 return true;
305             }
306         }
307         return false;
308     }
309
310     private boolean isHiddenClass(String JavaDoc name) {
311         for (int i = 0; i < hiddenClasses.length; i++) {
312             if (name.startsWith(hiddenClasses[i])) {
313                 return true;
314             }
315         }
316         return false;
317     }
318
319     private Class JavaDoc resolveClass(Class JavaDoc clazz, boolean resolve) {
320         if (resolve) {
321             resolveClass(clazz);
322         }
323         return clazz;
324     }
325
326     public URL JavaDoc getResource(String JavaDoc name) {
327         if (isDestroyed()) {
328             return null;
329         }
330
331         //
332
// if we are using inverse class loading, check local urls first
333
//
334
if (inverseClassLoading && !isDestroyed() && !isNonOverridableResource(name)) {
335             URL JavaDoc url = findResource(name);
336             if (url != null) {
337                 return url;
338             }
339         }
340
341         //
342
// Check parent class loaders
343
//
344
if (!isHiddenResource(name)) {
345             for (int i = 0; i < parents.length; i++) {
346                 ClassLoader JavaDoc parent = parents[i];
347                 URL JavaDoc url = parent.getResource(name);
348                 if (url != null) {
349                     return url;
350                 }
351             }
352         }
353
354         //
355
// if we are not using inverse class loading, check local urls now
356
//
357
// don't worry about excluding non-overridable resources here... we
358
// have alredy checked he parent and the parent didn't have the
359
// resource, so we can override now
360
if (!isDestroyed()) {
361             // parents didn't have the resource; attempt to load it from my urls
362
return findResource(name);
363         }
364
365         return null;
366     }
367
368     public Enumeration JavaDoc findResources(String JavaDoc name) throws IOException JavaDoc {
369         if (isDestroyed()) {
370             return Collections.enumeration(Collections.EMPTY_SET);
371         }
372
373         List JavaDoc resources = new ArrayList JavaDoc();
374
375         //
376
// if we are using inverse class loading, add the resources from local urls first
377
//
378
if (inverseClassLoading && !isDestroyed()) {
379             List JavaDoc myResources = Collections.list(super.findResources(name));
380             resources.addAll(myResources);
381         }
382
383         //
384
// Add parent resources
385
//
386
for (int i = 0; i < parents.length; i++) {
387             ClassLoader JavaDoc parent = parents[i];
388             List JavaDoc parentResources = Collections.list(parent.getResources(name));
389             resources.addAll(parentResources);
390         }
391
392         //
393
// if we are not using inverse class loading, add the resources from local urls now
394
//
395
if (!inverseClassLoading && !isDestroyed()) {
396             List JavaDoc myResources = Collections.list(super.findResources(name));
397             resources.addAll(myResources);
398         }
399
400         return Collections.enumeration(resources);
401     }
402
403     private boolean isNonOverridableResource(String JavaDoc name) {
404         for (int i = 0; i < nonOverridableResources.length; i++) {
405             if (name.startsWith(nonOverridableResources[i])) {
406                 return true;
407             }
408         }
409         return false;
410     }
411
412     private boolean isHiddenResource(String JavaDoc name) {
413         for (int i = 0; i < hiddenResources.length; i++) {
414             if (name.startsWith(hiddenResources[i])) {
415                 return true;
416             }
417         }
418         return false;
419     }
420
421     public String JavaDoc toString() {
422         return "[" + getClass().getName() + " id=" + id + "]";
423     }
424
425     public synchronized boolean isDestroyed() {
426         return destroyed;
427     }
428
429     public void destroy() {
430         synchronized(this) {
431             if (destroyed) return;
432             destroyed = true;
433         }
434
435         LogFactory.release(this);
436         clearSoftCache(ObjectInputStream JavaDoc.class, "subclassAudits");
437         clearSoftCache(ObjectOutputStream JavaDoc.class, "subclassAudits");
438         clearSoftCache(ObjectStreamClass JavaDoc.class, "localDescs");
439         clearSoftCache(ObjectStreamClass JavaDoc.class, "reflectors");
440
441         // The beanInfoCache in java.beans.Introspector will hold on to Classes which
442
// it has introspected. If we don't flush the cache, we may run out of
443
// Permanent Generation space.
444
Introspector.flushCaches();
445     }
446
447     private static final Object JavaDoc lock = new Object JavaDoc();
448     private static boolean clearSoftCacheFailed = false;
449
450     private static void clearSoftCache(Class JavaDoc clazz, String JavaDoc fieldName) {
451         Map JavaDoc cache = null;
452         try {
453             Field JavaDoc f = clazz.getDeclaredField(fieldName);
454             f.setAccessible(true);
455             cache = (Map JavaDoc) f.get(null);
456         } catch (Throwable JavaDoc e) {
457             synchronized (lock) {
458                 if (!clearSoftCacheFailed) {
459                     clearSoftCacheFailed = true;
460                     LogFactory.getLog(MultiParentClassLoader.class).debug("Unable to clear SoftCache field " + fieldName + " in class " + clazz);
461                 }
462             }
463         }
464
465         if (cache != null) {
466             synchronized (cache) {
467                 cache.clear();
468             }
469         }
470     }
471
472 }
473
Popular Tags