/Users/lyon/j4p/src/javassist/Loader.java
|
1 /*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16 package javassist;
17
18 import java.io.*;
19 import java.util.Hashtable;
20 import java.util.Vector;
21
22 /**
23 * The class loader for Javassist (JDK 1.2 or later only).
24 *
25 * <p>This is a sample class loader using <code>ClassPool</code>.
26 * Unlike a regular class loader, this class loader obtains bytecode
27 * from a <code>ClassPool</code>.
28 *
29 * <p>Note that Javassist can be used without this class loader; programmers
30 * can define their own versions of class loader. They can run
31 * a program even without any user-defined class loader if that program
32 * is statically translated with Javassist.
33 * This class loader is just provided as a utility class.
34 *
35 * <p>Suppose that an instance of <code>MyTranslator</code> implementing
36 * the interface <code>Translator</code> is responsible for modifying
37 * class files.
38 * The startup program of an application using <code>MyTranslator</code>
39 * should be something like this:
40 *
41 * <ul><pre>
42 * import javassist.*;
43 *
44 * public class Main {
45 * public static void main(String[] args) throws Throwable {
46 * MyTranslator myTrans = new MyTranslator();
47 * ClassPool cp = ClassPool.getDefault(myTrans);
48 * Loader cl = new Loader(cp);
49 * cl.run("MyApp", args);
50 * }
51 * }
52 * </pre></ul>
53 *
54 * <p>Class <code>MyApp</code> is the main program of the application.
55 *
56 * <p>This program should be executed as follows:
57 *
58 * <ul><pre>
59 * % java Main <i>arg1</i> <i>arg2</i>...
60 * </pre></ul>
61 *
62 * <p>It modifies the class <code>MyApp</code> with a <code>MyTranslator</code>
63 * object before loading it into the JVM.
64 * Then it calls <code>main()</code> in <code>MyApp</code> with arguments
65 * <i>arg1</i>, <i>arg2</i>, ...
66 *
67 * <p>This program execution is equivalent to:
68 *
69 * <ul><pre>
70 * % java MyApp <i>arg1</i> <i>arg2</i>...
71 * </pre></ul>
72 *
73 * <p>except that classes are translated by <code>MyTranslator</code>
74 * at load time.
75 *
76 * <p>If only a particular class is modified when it is loaded,
77 * a program like the following can be used:
78 *
79 * <ul><pre>ClassPool cp = ClassPool.getDefault();
80 * Loader cl = new Loader(cp);
81 *
82 * CtClass ct = cp.get("test.Rectangle");
83 * ct.setSuperclass(cp.get("test.Point"));
84 *
85 * Class c = cl.loadClass("test.Rectangle");
86 * Object rect = c.newInstance();</pre></ul>
87 *
88 * <p>This program modifies the super class of the <code>Rectangle</code>
89 * class and loads it into the JVM with a class loader <code>cl</code>.
90 *
91 * <p><b>Note 1:</b>
92 *
93 * <p>This class loader does not allow the users to intercept the loading
94 * of <code>java.*</code> and <code>javax.*</code> classes unless
95 * <code>Loader.doDelegation</code> is <code>false</code>. Also see
96 * Note 2.
97 *
98 * <p><b>Note 2:</b>
99 *
100 * <p>If classes are loaded with different class loaders, they belong to
101 * separate name spaces. If class <code>C</code> is loaded by a class
102 * loader <code>CL</code>, all classes that the class <code>C</code>
103 * refers to are also loaded by <code>CL</code>. However, if <code>CL</code>
104 * delegates the loading of the class <code>C</code> to <code>CL'</code>,
105 * then those classes that the class <code>C</code> refers to
106 * are loaded by a parent class loader <code>CL'</code>
107 * instead of <code>CL</code>.
108 *
109 * <p>If an object of class <code>C</code> is assigned
110 * to a variable of class <code>C</code> belonging to a different name
111 * space, then a <code>ClassCastException</code> is thrown.
112 *
113 * <p>Because of the fact above, this loader delegates only the loading of
114 * <code>javassist.Loader</code>
115 * and classes included in package <code>java.*</code> and
116 * <code>javax.*</code> to the parent class
117 * loader. Other classes are directly loaded by this loader.
118 *
119 * <p>For example, suppose that <code>java.lang.String</code> would be loaded
120 * by this loader while <code>java.io.File</code> is loaded by the parent
121 * class loader. If the constructor of <code>java.io.File</code> is called
122 * with an instance of <code>java.lang.String</code>, then it may throw
123 * an exception since it accepts an instance of only the
124 * <code>java.lang.String</code> loaded by the parent class loader.
125 *
126 * @see javassist.ClassPool
127 * @see javassist.Translator
128 */
129 public class Loader extends ClassLoader {
130 private Hashtable notDefinedHere; // must be atomic.
131 private Vector notDefinedPackages; // must be atomic.
132 private ClassPool source;
133
134 /**
135 * Specifies the algorithm of class loading.
136 *
137 * <p>This class loader uses the parent class loader for
138 * <code>java.*</code> and <code>javax.*</code> classes.
139 * If this variable <code>doDelegation</code>
140 * is <code>false</code>, this class loader does not delegate those
141 * classes to the parent class loader.
142 *
143 * <p>The default value is <code>true</code>.
144 */
145 public boolean doDelegation = true;
146
147 /**
148 * Creates a new class loader.
149 */
150 public Loader() {
151 this(null);
152 }
153
154 /**
155 * Creates a new class loader.
156 *
157 * @param cp the source of class files.
158 */
159 public Loader(ClassPool cp) {
160 init(cp);
161 }
162
163 /**
164 * Creates a new class loader
165 * using the specified parent class loader for delegation.
166 *
167 * @param parent the parent class loader.
168 * @param cp the source of class files.
169 */
170 public Loader(ClassLoader parent, ClassPool cp) {
171 super(parent);
172 init(cp);
173 }
174
175 private void init(ClassPool cp) {
176 notDefinedHere = new Hashtable();
177 notDefinedPackages = new Vector();
178 source = cp;
179 delegateLoadingOf("javassist.Loader");
180 }
181
182 /**
183 * Records a class so that the loading of that class is delegated
184 * to the parent class loader.
185 *
186 * <p>If the given class name ends with <code>.</code> (dot), then
187 * that name is interpreted as a package name. All the classes
188 * in that package and the sub packages are delegated.
189 */
190 public void delegateLoadingOf(String classname) {
191 if (classname.endsWith("."))
192 notDefinedPackages.addElement(classname);
193 else
194 notDefinedHere.put(classname, this);
195 }
196
197 /**
198 * Sets the soruce <code>ClassPool</code>.
199 */
200 public void setClassPool(ClassPool cp) {
201 source = cp;
202 }
203
204 /**
205 * Loads a class with an instance of <code>Loader</code>
206 * and calls <code>main()</code> of that class.
207 *
208 * <p>This method calls <code>run()</code>.
209 *
210 * @param args[0] class name to be loaded.
211 * @param args[1-n] parameters passed to <code>main()</code>.
212 *
213 * @see javassist.Loader#run(String[])
214 */
215 public static void main(String[] args) throws Throwable {
216 Loader cl = new Loader();
217 cl.run(args);
218 }
219
220 /**
221 * Loads a class and calls <code>main()</code> in that class.
222 *
223 * @param args[0] the name of the loaded class.
224 * @param args[1-n] parameters passed to <code>main()</code>.
225 */
226 public void run(String[] args) throws Throwable {
227 int n = args.length - 1;
228 if (n >= 0) {
229 String[] args2 = new String[n];
230 for (int i = 0; i < n; ++i)
231 args2[i] = args[i + 1];
232
233 run(args[0], args2);
234 }
235 }
236
237 /**
238 * Loads a class and calls <code>main()</code> in that class.
239 *
240 * @param classname the loaded class.
241 * @param args parameters passed to <code>main()</code>.
242 */
243 public void run(String classname, String[] args) throws Throwable {
244 Class c = loadClass(classname);
245 try {
246 c.getDeclaredMethod("main", new Class[]{String[].class})
247 .invoke(null, new Object[]{args});
248 } catch (java.lang.reflect.InvocationTargetException e) {
249 throw e.getTargetException();
250 }
251 }
252
253 /**
254 * Requests the class loader to load a class.
255 */
256 protected Class loadClass(String name, boolean resolve)
257 throws ClassFormatError, ClassNotFoundException {
258 Class c = findLoadedClass(name);
259 if (c == null)
260 c = loadClassByDelegation(name);
261
262 if (c == null)
263 c = findClass(name);
264
265 if (c == null)
266 c = delegateToParent(name);
267
268 if (resolve)
269 resolveClass(c);
270
271 return c;
272 }
273
274 /**
275 * Finds the specified class using <code>ClassPath</code>.
276 * If the source throws an exception, this returns null.
277 *
278 * <p>This method can be overridden by a subclass of
279 * <code>Loader</code>.
280 */
281 protected Class findClass(String name) {
282 byte[] classfile;
283 try {
284 if (source != null)
285 classfile = source.write(name);
286 else {
287 String jarname = "/" + name.replace('.', '/') + ".class";
288 InputStream in = this.getClass().getResourceAsStream(jarname);
289
290 classfile = ClassPoolTail.readStream(in);
291 }
292 } catch (Exception e) {
293 return null;
294 }
295
296 int i = name.lastIndexOf('.');
297 if (i != -1) {
298 String pname = name.substring(0, i);
299 if (getPackage(pname) == null)
300 try {
301 definePackage(pname, null, null, null,
302 null, null, null, null);
303 } catch (IllegalArgumentException e) {
304 // ignore. maybe the package object for the same
305 // name has been created just right away.
306 }
307 }
308
309 return defineClass(name, classfile, 0, classfile.length);
310 }
311
312 private Class loadClassByDelegation(String name)
313 throws ClassNotFoundException {
314 /* The swing components must be loaded by a system
315 * class loader.
316 * javax.swing.UIManager loads a (concrete) subclass
317 * of LookAndFeel by a system class loader and cast
318 * an instance of the class to LookAndFeel for
319 * (maybe) a security reason. To avoid failure of
320 * type conversion, LookAndFeel must not be loaded
321 * by this class loader.
322 */
323
324 Class c = null;
325 if (doDelegation)
326 if (name.startsWith("java.") || name.startsWith("javax.")
327 || name.startsWith("sun.") || name.startsWith("com.sun.")
328 || notDelegated(name))
329 c = delegateToParent(name);
330
331 return c;
332 }
333
334 private boolean notDelegated(String name) {
335 if (notDefinedHere.get(name) != null)
336 return true;
337
338 int n = notDefinedPackages.size();
339 for (int i = 0; i < n; ++i)
340 if (name.startsWith((String) notDefinedPackages.elementAt(i)))
341 return true;
342
343 return false;
344 }
345
346 private Class delegateToParent(String classname)
347 throws ClassNotFoundException {
348 ClassLoader cl = getParent();
349 if (cl != null)
350 return cl.loadClass(classname);
351 else
352 return findSystemClass(classname);
353 }
354
355 protected Package getPackage(String name) {
356 return super.getPackage(name);
357 }
358 /*
359 // Package p = super.getPackage(name);
360 Package p = null;
361 if (p == null)
362 return definePackage(name, null, null, null,
363 null, null, null, null);
364 else
365 return p;
366 }
367 */
368 }
369