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
16package javassist;
17
18import java.io.*;
19import java.util.Hashtable;
20import java.util.Vector;
21import 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 */
135public 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