• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2007 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 import java.security.ProtectionDomain;
22 
23 /**
24  * The class loader for Javassist.
25  *
26  * <p>This is a sample class loader using <code>ClassPool</code>.
27  * Unlike a regular class loader, this class loader obtains bytecode
28  * from a <code>ClassPool</code>.
29  *
30  * <p>Note that Javassist can be used without this class loader; programmers
31  * can define their own versions of class loader.  They can run
32  * a program even without any user-defined class loader if that program
33  * is statically translated with Javassist.
34  * This class loader is just provided as a utility class.
35  *
36  * <p>Suppose that an instance of <code>MyTranslator</code> implementing
37  * the interface <code>Translator</code> is responsible for modifying
38  * class files.
39  * The startup program of an application using <code>MyTranslator</code>
40  * should be something like this:
41  *
42  * <ul><pre>
43  * import javassist.*;
44  *
45  * public class Main {
46  *   public static void main(String[] args) throws Throwable {
47  *     MyTranslator myTrans = new MyTranslator();
48  *     ClassPool cp = ClassPool.getDefault();
49  *     Loader cl = new Loader(cp);
50  *     cl.addTranslator(cp, myTrans);
51  *     cl.run("MyApp", args);
52  *   }
53  * }
54  * </pre></ul>
55  *
56  * <p>Class <code>MyApp</code> is the main program of the application.
57  *
58  * <p>This program should be executed as follows:
59  *
60  * <ul><pre>
61  * % java Main <i>arg1</i> <i>arg2</i>...
62  * </pre></ul>
63  *
64  * <p>It modifies the class <code>MyApp</code> with a <code>MyTranslator</code>
65  * object before the JVM loads it.
66  * Then it calls <code>main()</code> in <code>MyApp</code> with arguments
67  * <i>arg1</i>, <i>arg2</i>, ...
68  *
69  * <p>This program execution is equivalent to:
70  *
71  * <ul><pre>
72  * % java MyApp <i>arg1</i> <i>arg2</i>...
73  * </pre></ul>
74  *
75  * <p>except that classes are translated by <code>MyTranslator</code>
76  * at load time.
77  *
78  * <p>If only a particular class must be modified when it is loaded,
79  * the startup program can be simpler; <code>MyTranslator</code> is
80  * unnecessary.  For example, if only a class <code>test.Rectangle</code>
81  * is modified, the <code>main()</code> method above will be the following:
82  *
83  * <ul><pre>
84  * ClassPool cp = ClassPool.getDefault();
85  * Loader cl = new Loader(cp);
86  * CtClass ct = cp.get("test.Rectangle");
87  * ct.setSuperclass(cp.get("test.Point"));
88  * cl.run("MyApp", args);</pre></ul>
89  *
90  * <p>This program changes the super class of the <code>test.Rectangle</code>
91  * class.
92  *
93  * <p><b>Note 1:</b>
94  *
95  * <p>This class loader does not allow the users to intercept the loading
96  * of <code>java.*</code> and <code>javax.*</code> classes (and
97  * <code>sun.*</code>, <code>org.xml.*</code>, ...) unless
98  * <code>Loader.doDelegation</code> is <code>false</code>.  This is because
99  * 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      */
Loader()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      */
Loader(ClassPool cp)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      */
Loader(ClassLoader parent, ClassPool cp)178     public Loader(ClassLoader parent, ClassPool cp) {
179         super(parent);
180         init(cp);
181     }
182 
init(ClassPool cp)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      */
delegateLoadingOf(String classname)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      */
setDomain(ProtectionDomain d)213     public void setDomain(ProtectionDomain d) {
214         domain = d;
215     }
216 
217     /**
218      * Sets the soruce <code>ClassPool</code>.
219      */
setClassPool(ClassPool cp)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      */
addTranslator(ClassPool cp, Translator t)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      */
main(String[] args)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      */
run(String[] args)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      */
run(String classname, String[] args)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 }).invoke(
291                 null,
292                 new Object[] { args });
293         }
294         catch (java.lang.reflect.InvocationTargetException e) {
295             throw e.getTargetException();
296         }
297     }
298 
299     /**
300      * Requests the class loader to load a class.
301      */
loadClass(String name, boolean resolve)302     protected Class loadClass(String name, boolean resolve)
303         throws ClassFormatError, ClassNotFoundException {
304         name = name.intern();
305         synchronized (name) {
306             Class c = findLoadedClass(name);
307             if (c == null)
308                 c = loadClassByDelegation(name);
309 
310             if (c == null)
311                 c = findClass(name);
312 
313             if (c == null)
314                 c = delegateToParent(name);
315 
316             if (resolve)
317                 resolveClass(c);
318 
319             return c;
320         }
321     }
322 
323     /**
324      * Finds the specified class using <code>ClassPath</code>.
325      * If the source throws an exception, this returns null.
326      *
327      * <p>This method can be overridden by a subclass of
328      * <code>Loader</code>.  Note that the overridden method must not throw
329      * an exception when it just fails to find a class file.
330      *
331      * @return      null if the specified class could not be found.
332      * @throws ClassNotFoundException   if an exception is thrown while
333      *                                  obtaining a class file.
334      */
findClass(String name)335     protected Class findClass(String name) throws ClassNotFoundException {
336         byte[] classfile;
337         try {
338             if (source != null) {
339                 if (translator != null)
340                     translator.onLoad(source, name);
341 
342                 try {
343                     classfile = source.get(name).toBytecode();
344                 }
345                 catch (NotFoundException e) {
346                     return null;
347                 }
348             }
349             else {
350                 String jarname = "/" + name.replace('.', '/') + ".class";
351                 InputStream in = this.getClass().getResourceAsStream(jarname);
352                 if (in == null)
353                     return null;
354 
355                 classfile = ClassPoolTail.readStream(in);
356             }
357         }
358         catch (Exception e) {
359             throw new ClassNotFoundException(
360                 "caught an exception while obtaining a class file for "
361                 + name, e);
362         }
363 
364         int i = name.lastIndexOf('.');
365         if (i != -1) {
366             String pname = name.substring(0, i);
367             if (getPackage(pname) == null)
368                 try {
369                     definePackage(
370                         pname, null, null, null, null, null, null, null);
371                 }
372                 catch (IllegalArgumentException e) {
373                     // ignore.  maybe the package object for the same
374                     // name has been created just right away.
375                 }
376         }
377 
378         if (domain == null)
379             return defineClass(name, classfile, 0, classfile.length);
380         else
381             return defineClass(name, classfile, 0, classfile.length, domain);
382     }
383 
loadClassByDelegation(String name)384     protected Class loadClassByDelegation(String name)
385         throws ClassNotFoundException
386     {
387         /* The swing components must be loaded by a system
388          * class loader.
389          * javax.swing.UIManager loads a (concrete) subclass
390          * of LookAndFeel by a system class loader and cast
391          * an instance of the class to LookAndFeel for
392          * (maybe) a security reason.  To avoid failure of
393          * type conversion, LookAndFeel must not be loaded
394          * by this class loader.
395          */
396 
397         Class c = null;
398         if (doDelegation)
399             if (name.startsWith("java.")
400                 || name.startsWith("javax.")
401                 || name.startsWith("sun.")
402                 || name.startsWith("com.sun.")
403                 || name.startsWith("org.w3c.")
404                 || name.startsWith("org.xml.")
405                 || notDelegated(name))
406                 c = delegateToParent(name);
407 
408         return c;
409     }
410 
notDelegated(String name)411     private boolean notDelegated(String name) {
412         if (notDefinedHere.get(name) != null)
413             return true;
414 
415         int n = notDefinedPackages.size();
416         for (int i = 0; i < n; ++i)
417             if (name.startsWith((String)notDefinedPackages.elementAt(i)))
418                 return true;
419 
420         return false;
421     }
422 
delegateToParent(String classname)423     protected Class delegateToParent(String classname)
424         throws ClassNotFoundException
425     {
426         ClassLoader cl = getParent();
427         if (cl != null)
428             return cl.loadClass(classname);
429         else
430             return findSystemClass(classname);
431     }
432 
getPackage(String name)433     protected Package getPackage(String name) {
434         return super.getPackage(name);
435     }
436     /*
437         // Package p = super.getPackage(name);
438         Package p = null;
439         if (p == null)
440             return definePackage(name, null, null, null,
441                                  null, null, null, null);
442         else
443             return p;
444     }
445     */
446 }
447