001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist;
017:
018: import java.io.*;
019: import java.util.Hashtable;
020: import java.util.Vector;
021: import java.security.ProtectionDomain;
022:
023: /**
024: * The class loader for Javassist.
025: *
026: * <p>This is a sample class loader using <code>ClassPool</code>.
027: * Unlike a regular class loader, this class loader obtains bytecode
028: * from a <code>ClassPool</code>.
029: *
030: * <p>Note that Javassist can be used without this class loader; programmers
031: * can define their own versions of class loader. They can run
032: * a program even without any user-defined class loader if that program
033: * is statically translated with Javassist.
034: * This class loader is just provided as a utility class.
035: *
036: * <p>Suppose that an instance of <code>MyTranslator</code> implementing
037: * the interface <code>Translator</code> is responsible for modifying
038: * class files.
039: * The startup program of an application using <code>MyTranslator</code>
040: * should be something like this:
041: *
042: * <ul><pre>
043: * import javassist.*;
044: *
045: * public class Main {
046: * public static void main(String[] args) throws Throwable {
047: * MyTranslator myTrans = new MyTranslator();
048: * ClassPool cp = ClassPool.getDefault();
049: * Loader cl = new Loader(cp);
050: * cl.addTranslator(cp, myTrans);
051: * cl.run("MyApp", args);
052: * }
053: * }
054: * </pre></ul>
055: *
056: * <p>Class <code>MyApp</code> is the main program of the application.
057: *
058: * <p>This program should be executed as follows:
059: *
060: * <ul><pre>
061: * % java Main <i>arg1</i> <i>arg2</i>...
062: * </pre></ul>
063: *
064: * <p>It modifies the class <code>MyApp</code> with a <code>MyTranslator</code>
065: * object before the JVM loads it.
066: * Then it calls <code>main()</code> in <code>MyApp</code> with arguments
067: * <i>arg1</i>, <i>arg2</i>, ...
068: *
069: * <p>This program execution is equivalent to:
070: *
071: * <ul><pre>
072: * % java MyApp <i>arg1</i> <i>arg2</i>...
073: * </pre></ul>
074: *
075: * <p>except that classes are translated by <code>MyTranslator</code>
076: * at load time.
077: *
078: * <p>If only a particular class must be modified when it is loaded,
079: * the startup program can be simpler; <code>MyTranslator</code> is
080: * unnecessary. For example, if only a class <code>test.Rectangle</code>
081: * is modified, the <code>main()</code> method above will be the following:
082: *
083: * <ul><pre>
084: * ClassPool cp = ClassPool.getDefault();
085: * Loader cl = new Loader(cp);
086: * CtClass ct = cp.get("test.Rectangle");
087: * ct.setSuperclass(cp.get("test.Point"));
088: * cl.run("MyApp", args);</pre></ul>
089: *
090: * <p>This program changes the super class of the <code>test.Rectangle</code>
091: * class.
092: *
093: * <p><b>Note 1:</b>
094: *
095: * <p>This class loader does not allow the users to intercept the loading
096: * of <code>java.*</code> and <code>javax.*</code> classes (and
097: * <code>sun.*</code>, <code>org.xml.*</code>, ...) unless
098: * <code>Loader.doDelegation</code> is <code>false</code>. This is because
099: * the JVM prohibits a user class loader from loading a system class.
100: * Also see Note 2.
101: * If this behavior is not appropriate, a subclass of <code>Loader</code>
102: * must be defined and <code>loadClassByDelegation()</code> must be overridden.
103: *
104: * <p><b>Note 2:</b>
105: *
106: * <p>If classes are loaded with different class loaders, they belong to
107: * separate name spaces. If class <code>C</code> is loaded by a class
108: * loader <code>CL</code>, all classes that the class <code>C</code>
109: * refers to are also loaded by <code>CL</code>. However, if <code>CL</code>
110: * delegates the loading of the class <code>C</code> to <code>CL'</code>,
111: * then those classes that the class <code>C</code> refers to
112: * are loaded by a parent class loader <code>CL'</code>
113: * instead of <code>CL</code>.
114: *
115: * <p>If an object of class <code>C</code> is assigned
116: * to a variable of class <code>C</code> belonging to a different name
117: * space, then a <code>ClassCastException</code> is thrown.
118: *
119: * <p>Because of the fact above, this loader delegates only the loading of
120: * <code>javassist.Loader</code>
121: * and classes included in package <code>java.*</code> and
122: * <code>javax.*</code> to the parent class
123: * loader. Other classes are directly loaded by this loader.
124: *
125: * <p>For example, suppose that <code>java.lang.String</code> would be loaded
126: * by this loader while <code>java.io.File</code> is loaded by the parent
127: * class loader. If the constructor of <code>java.io.File</code> is called
128: * with an instance of <code>java.lang.String</code>, then it may throw
129: * an exception since it accepts an instance of only the
130: * <code>java.lang.String</code> loaded by the parent class loader.
131: *
132: * @see javassist.ClassPool
133: * @see javassist.Translator
134: */
135: public class Loader extends ClassLoader {
136: private Hashtable notDefinedHere; // must be atomic.
137: private Vector notDefinedPackages; // must be atomic.
138: private ClassPool source;
139: private Translator translator;
140: private ProtectionDomain domain;
141:
142: /**
143: * Specifies the algorithm of class loading.
144: *
145: * <p>This class loader uses the parent class loader for
146: * <code>java.*</code> and <code>javax.*</code> classes.
147: * If this variable <code>doDelegation</code>
148: * is <code>false</code>, this class loader does not delegate those
149: * classes to the parent class loader.
150: *
151: * <p>The default value is <code>true</code>.
152: */
153: public boolean doDelegation = true;
154:
155: /**
156: * Creates a new class loader.
157: */
158: public Loader() {
159: this (null);
160: }
161:
162: /**
163: * Creates a new class loader.
164: *
165: * @param cp the source of class files.
166: */
167: public Loader(ClassPool cp) {
168: init(cp);
169: }
170:
171: /**
172: * Creates a new class loader
173: * using the specified parent class loader for delegation.
174: *
175: * @param parent the parent class loader.
176: * @param cp the source of class files.
177: */
178: public Loader(ClassLoader parent, ClassPool cp) {
179: super (parent);
180: init(cp);
181: }
182:
183: private void init(ClassPool cp) {
184: notDefinedHere = new Hashtable();
185: notDefinedPackages = new Vector();
186: source = cp;
187: translator = null;
188: domain = null;
189: delegateLoadingOf("javassist.Loader");
190: }
191:
192: /**
193: * Records a class so that the loading of that class is delegated
194: * to the parent class loader.
195: *
196: * <p>If the given class name ends with <code>.</code> (dot), then
197: * that name is interpreted as a package name. All the classes
198: * in that package and the sub packages are delegated.
199: */
200: public void delegateLoadingOf(String classname) {
201: if (classname.endsWith("."))
202: notDefinedPackages.addElement(classname);
203: else
204: notDefinedHere.put(classname, this );
205: }
206:
207: /**
208: * Sets the protection domain for the classes handled by this class
209: * loader. Without registering an appropriate protection domain,
210: * the program loaded by this loader will not work with a security
211: * manager or a signed jar file.
212: */
213: public void setDomain(ProtectionDomain d) {
214: domain = d;
215: }
216:
217: /**
218: * Sets the soruce <code>ClassPool</code>.
219: */
220: public void setClassPool(ClassPool cp) {
221: source = cp;
222: }
223:
224: /**
225: * Adds a translator, which is called whenever a class is loaded.
226: *
227: * @param cp the <code>ClassPool</code> object for obtaining
228: * a class file.
229: * @param t a translator.
230: * @throws NotFoundException if <code>t.start()</code> throws an exception.
231: * @throws CannotCompileException if <code>t.start()</code> throws an exception.
232: */
233: public void addTranslator(ClassPool cp, Translator t)
234: throws NotFoundException, CannotCompileException {
235: source = cp;
236: translator = t;
237: t.start(cp);
238: }
239:
240: /**
241: * Loads a class with an instance of <code>Loader</code>
242: * and calls <code>main()</code> of that class.
243: *
244: * <p>This method calls <code>run()</code>.
245: *
246: * @param args command line parameters.
247: * <ul>
248: * <code>args[0]</code> is the class name to be loaded.
249: * <br><code>args[1..n]</code> are parameters passed
250: * to the target <code>main()</code>.
251: * </ul>
252: *
253: * @see javassist.Loader#run(String[])
254: */
255: public static void main(String[] args) throws Throwable {
256: Loader cl = new Loader();
257: cl.run(args);
258: }
259:
260: /**
261: * Loads a class and calls <code>main()</code> in that class.
262: *
263: * @param args command line parameters.
264: * <ul>
265: * <code>args[0]</code> is the class name to be loaded.
266: * <br><code>args[1..n]</code> are parameters passed
267: * to the target <code>main()</code>.
268: * </ul>
269: */
270: public void run(String[] args) throws Throwable {
271: int n = args.length - 1;
272: if (n >= 0) {
273: String[] args2 = new String[n];
274: for (int i = 0; i < n; ++i)
275: args2[i] = args[i + 1];
276:
277: run(args[0], args2);
278: }
279: }
280:
281: /**
282: * Loads a class and calls <code>main()</code> in that class.
283: *
284: * @param classname the loaded class.
285: * @param args parameters passed to <code>main()</code>.
286: */
287: public void run(String classname, String[] args) throws Throwable {
288: Class c = loadClass(classname);
289: try {
290: c.getDeclaredMethod("main", new Class[] { String[].class })
291: .invoke(null, new Object[] { args });
292: } catch (java.lang.reflect.InvocationTargetException e) {
293: throw e.getTargetException();
294: }
295: }
296:
297: /**
298: * Requests the class loader to load a class.
299: */
300: protected Class loadClass(String name, boolean resolve)
301: throws ClassFormatError, ClassNotFoundException {
302: name = name.intern();
303: synchronized (name) {
304: Class c = findLoadedClass(name);
305: if (c == null)
306: c = loadClassByDelegation(name);
307:
308: if (c == null)
309: c = findClass(name);
310:
311: if (c == null)
312: c = delegateToParent(name);
313:
314: if (resolve)
315: resolveClass(c);
316:
317: return c;
318: }
319: }
320:
321: /**
322: * Finds the specified class using <code>ClassPath</code>.
323: * If the source throws an exception, this returns null.
324: *
325: * <p>This method can be overridden by a subclass of
326: * <code>Loader</code>. Note that the overridden method must not throw
327: * an exception when it just fails to find a class file.
328: *
329: * @return null if the specified class could not be found.
330: * @throws ClassNotFoundException if an exception is thrown while
331: * obtaining a class file.
332: */
333: protected Class findClass(String name)
334: throws ClassNotFoundException {
335: byte[] classfile;
336: try {
337: if (source != null) {
338: if (translator != null)
339: translator.onLoad(source, name);
340:
341: try {
342: classfile = source.get(name).toBytecode();
343: } catch (NotFoundException e) {
344: return null;
345: }
346: } else {
347: String jarname = "/" + name.replace('.', '/')
348: + ".class";
349: InputStream in = this .getClass().getResourceAsStream(
350: jarname);
351: if (in == null)
352: return null;
353:
354: classfile = ClassPoolTail.readStream(in);
355: }
356: } catch (Exception e) {
357: throw new ClassNotFoundException(
358: "caught an exception while obtaining a class file for "
359: + name, e);
360: }
361:
362: int i = name.lastIndexOf('.');
363: if (i != -1) {
364: String pname = name.substring(0, i);
365: if (getPackage(pname) == null)
366: try {
367: definePackage(pname, null, null, null, null, null,
368: null, null);
369: } catch (IllegalArgumentException e) {
370: // ignore. maybe the package object for the same
371: // name has been created just right away.
372: }
373: }
374:
375: if (domain == null)
376: return defineClass(name, classfile, 0, classfile.length);
377: else
378: return defineClass(name, classfile, 0, classfile.length,
379: domain);
380: }
381:
382: protected Class loadClassByDelegation(String name)
383: throws ClassNotFoundException {
384: /* The swing components must be loaded by a system
385: * class loader.
386: * javax.swing.UIManager loads a (concrete) subclass
387: * of LookAndFeel by a system class loader and cast
388: * an instance of the class to LookAndFeel for
389: * (maybe) a security reason. To avoid failure of
390: * type conversion, LookAndFeel must not be loaded
391: * by this class loader.
392: */
393:
394: Class c = null;
395: if (doDelegation)
396: if (name.startsWith("java.") || name.startsWith("javax.")
397: || name.startsWith("sun.")
398: || name.startsWith("com.sun.")
399: || name.startsWith("org.w3c.")
400: || name.startsWith("org.xml.")
401: || notDelegated(name))
402: c = delegateToParent(name);
403:
404: return c;
405: }
406:
407: private boolean notDelegated(String name) {
408: if (notDefinedHere.get(name) != null)
409: return true;
410:
411: int n = notDefinedPackages.size();
412: for (int i = 0; i < n; ++i)
413: if (name.startsWith((String) notDefinedPackages
414: .elementAt(i)))
415: return true;
416:
417: return false;
418: }
419:
420: protected Class delegateToParent(String classname)
421: throws ClassNotFoundException {
422: ClassLoader cl = getParent();
423: if (cl != null)
424: return cl.loadClass(classname);
425: else
426: return findSystemClass(classname);
427: }
428:
429: protected Package getPackage(String name) {
430: return super .getPackage(name);
431: }
432: /*
433: // Package p = super.getPackage(name);
434: Package p = null;
435: if (p == null)
436: return definePackage(name, null, null, null,
437: null, null, null, null);
438: else
439: return p;
440: }
441: */
442: }
|