KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > util > classloader > StickyClassLoader


1 /*BEGIN_COPYRIGHT_BLOCK
2  *
3  * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/
4  * or http://sourceforge.net/projects/drjava/
5  *
6  * DrJava Open Source License
7  *
8  * Copyright (C) 2001-2006 JavaPLT group at Rice University (javaplt@rice.edu). All rights reserved.
9  *
10  * Developed by: Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13  * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
15  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16  *
17  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
18  * following disclaimers.
19  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
20  * following disclaimers in the documentation and/or other materials provided with the distribution.
21  * - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to
22  * endorse or promote products derived from this Software without specific prior written permission.
23  * - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their
24  * names without prior written permission from the JavaPLT group. For permission, write to javaplt@rice.edu.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
27  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * WITH THE SOFTWARE.
31  *
32  *END_COPYRIGHT_BLOCK*/

33
34 package edu.rice.cs.util.classloader;
35
36 import java.util.Arrays JavaDoc;
37 import java.net.URL JavaDoc;
38 import java.io.IOException JavaDoc;
39 import java.io.InputStream JavaDoc;
40
41 import edu.rice.cs.plt.io.IOUtil;
42 import edu.rice.cs.util.FileOps;
43
44 /** A {@link ClassLoader} that works as the union of two classloaders, but always tries to delegate to the first of
45  * these. The purpose for this class is to ensure that classes loaded transitively due to some class's loading are
46  * also loaded with the right classloader. Here's the problem: Say that class A contains a reference to class B, but
47  * the specific B is unknown to clients of class A. Class A is loadable by the standard classloader, but class B needs
48  * to be loaded with a (known) custom classloader.
49  * <P>
50  * If A were loaded using the standard classloader, it would fail because this would cause the transitive loading of
51  * B to be done by the system loader as well. If A were loaded with the custom loader, the same thing would happen --
52  * the custom loader would delegate to the system loader to load A (since it doesn't load non-custom-loader-requiring
53  * classes), but this would associate class A with the system classloader. (Every class is associated with the loader
54  * that called {@link ClassLoader#defineClass} to define it in the JVM.) This association would make B be loaded by
55  * the standard loader!
56  * <P>
57  * To get around this problem, we use this class, which acts mostly as a union of two classloaders. The trick, however,
58  * is that the StickyClassLoader has itself associated with all classes it loads, even though the actual work is done
59  * by the two loaders it delegates to. (It does this by calling {@link ClassLoader#findResource} on the subordinate
60  * loaders to get the class data, but then by calling {@link ClassLoader#defineClass} itself to preserve the
61  * association.
62  *
63  * @version $Id: StickyClassLoader.java 4083 2007-01-23 21:27:44Z dlsmith $
64  */

65 public class StickyClassLoader extends ClassLoader JavaDoc {
66   private final ClassLoader JavaDoc _newLoader;
67   private final String JavaDoc[] _classesToLoadWithOld;
68
69   /** Creates a sticky class loader with the given primary and secondary loaders to join together. All classes will be
70    * attempted to be loaded with the primary loader, and the secondary will be used as a fallback.
71    * @param newLoader Primary loader
72    * @param oldLoader Secondary loader
73    */

74   public StickyClassLoader(final ClassLoader JavaDoc newLoader, final ClassLoader JavaDoc oldLoader) {
75     this(newLoader, oldLoader, new String JavaDoc[0]);
76   }
77
78   /** Creates a sticky class loader with the given primary and secondary loaders to join together. All classes will be
79    * attempted to be loaded with the primary loader
80    * (except for classes in <code>classesToLoadWithOld</code>),
81    * and the secondary will be used as a fallback.
82    *
83    * @param newLoader Primary loader
84    * @param oldLoader Secondary loader
85    * @param classesToLoadWithOld All class names in this array will be loaded only with the secondary classloader. This
86    * is vital to ensure that only one copy of some classes are loaded, since two differently loaded versions of
87    * a class act totally independently! (That is, they have different, incompatible types.) Often it'll be
88    * necessary to make key interfaces that are used between components get loaded via one standard classloader,
89    * to ensure that things can be cast to that interface.
90    */

91   public StickyClassLoader(final ClassLoader JavaDoc newLoader, final ClassLoader JavaDoc oldLoader,
92                            final String JavaDoc[] classesToLoadWithOld) {
93     super(oldLoader);
94     _newLoader = newLoader; // to be used only in getResource()!
95
_classesToLoadWithOld = new String JavaDoc[classesToLoadWithOld.length];
96     System.arraycopy(classesToLoadWithOld, 0, _classesToLoadWithOld, 0, classesToLoadWithOld.length);
97     Arrays.sort(_classesToLoadWithOld);
98   }
99
100   /** Gets the requested resource, looking first in the new loader and then in the old loader.
101    * @param name Name of resource to find
102    * @return URL of the resource if found/accessible, or null if not.
103    */

104   public URL JavaDoc getResource(String JavaDoc name) {
105     URL JavaDoc resource = _newLoader.getResource(name);
106     if (resource == null) resource = getParent().getResource(name);
107
108     //System.err.println("resource: " + name + " --> " + resource);
109
return resource;
110   }
111
112   /** Loads the given class, delegating first to the new class loader and then second to the old class loader. The
113    * returned Class object will have its ClassLoader ({@link Class#getClassLoader}) set to be this. This is very
114    * important because it causes classes that are loaded due to this class being loaded (ancestor classes/interfaces,
115    * referenced classes, etc) to use the same loader. There are a few exceptions to this explanation:
116    * <OL>
117    * <LI>If the class is in java.* or javax.*, it will be loaded using {@link ClassLoader#getSystemClassLoader}. This
118    * is because only the system loader is allowed to load system classes! Also: sun.*.
119    * </LI>
120    * <LI>If the class name is in the list of classes to load with the old class loader (passed to constructor), the new
121    * loader is not considered when trying to load the class. This is useful to make sure that certain classes (or
122    * interfaces) only have one copy in the system, to ensure that you can cast to that
123    * class/interface regardless of which loader loaded the other class.
124    * </LI>
125    * </OL>
126    */

127   protected Class JavaDoc<?> loadClass(String JavaDoc name, boolean resolve) throws ClassNotFoundException JavaDoc {
128     // check if it's already loaded in the JVM!
129
Class JavaDoc<?> clazz;
130     clazz = findLoadedClass(name);
131     if (clazz != null) return clazz;
132     
133     if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("sun.") ||
134         name.startsWith("com.sun.") || name.startsWith("org.omg.") || name.startsWith("sunw.") ||
135         name.startsWith("org.w3c.dom.") || name.startsWith("org.xml.sax.") || name.startsWith("net.jini.")) {
136       
137       try { clazz = getSystemClassLoader().loadClass(name); }
138       catch (ClassNotFoundException JavaDoc e) {
139         // It might be a non-system class, like javax.mail.*.
140
// Fall back on the secondary loader.
141
clazz = _loadWithSecondary(name);
142       }
143     }
144     else if (Arrays.binarySearch(_classesToLoadWithOld, name) >= 0) {
145       // Don't fall back to secondary if this fails...
146
clazz = getParent().loadClass(name);
147     }
148     else {
149       // Load with the secondary loader
150
clazz = _loadWithSecondary(name);
151       // If this fails don't fall back to loading with oldClassloader (or,
152
// equivalently, getParent()) cuz getResource() (called by _loadWithSecondary())
153
// calls getParent.getResource() if _newLoader.getResource() returns null
154
}
155
156     if (resolve) resolveClass(clazz);
157
158 // System.out.println("Sticky loaded OK: " + name + " " + clazz + " loader=" + clazz.getClassLoader());
159
return clazz;
160   }
161   
162   /** Try to load the class with the given name with the secondary (new) loader. Uses getResource to find the class.
163    * @param name Name of the class to load.
164    */

165   protected Class JavaDoc _loadWithSecondary(String JavaDoc name) throws ClassNotFoundException JavaDoc {
166     // we get the data using getResource because if we just delegate
167
// the call to loadClass on old or new loader, it will use that
168
// loader as the associated class loader for the class. that's bad.
169
// The method does not depend on the newLoader except indirectly
170
// in the call to getResource()
171
try {
172       String JavaDoc fileName = name.replace('.', '/') + ".class";
173       
174       URL JavaDoc resource = getResource(fileName); // only dependency on newLoader!
175
if (resource == null) {
176         throw new ClassNotFoundException JavaDoc("Resource not found: " + fileName);
177       }
178       
179       InputStream JavaDoc in = resource.openStream();
180       try {
181         byte[] data = IOUtil.toByteArray(in);
182         return defineClass(name, data, 0, data.length);
183       }
184       finally { in.close(); }
185     }
186     catch (IOException JavaDoc ioe) {
187       throw new ClassNotFoundException JavaDoc(ioe.toString());
188     }
189   }
190 }
191
Popular Tags