Home | History | Annotate | Download | only in javassist
      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      */
    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 }).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      */
    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      */
    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 
    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 
    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 
    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 
    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