/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import java.io.*; import java.util.Hashtable; import java.util.Vector; /** * The class loader for Javassist (JDK 1.2 or later only). * *

This is a sample class loader using ClassPool. * Unlike a regular class loader, this class loader obtains bytecode * from a ClassPool. * *

Note that Javassist can be used without this class loader; programmers * can define their own versions of class loader. They can run * a program even without any user-defined class loader if that program * is statically translated with Javassist. * This class loader is just provided as a utility class. * *

Suppose that an instance of MyTranslator implementing * the interface Translator is responsible for modifying * class files. * The startup program of an application using MyTranslator * should be something like this: * *

* *

Class MyApp is the main program of the application. * *

This program should be executed as follows: * *

* *

It modifies the class MyApp with a MyTranslator * object before loading it into the JVM. * Then it calls main() in MyApp with arguments * arg1, arg2, ... * *

This program execution is equivalent to: * *

* *

except that classes are translated by MyTranslator * at load time. * *

If only a particular class is modified when it is loaded, * a program like the following can be used: * *

* *

This program modifies the super class of the Rectangle * class and loads it into the JVM with a class loader cl. * *

Note 1: * *

This class loader does not allow the users to intercept the loading * of java.* and javax.* classes unless * Loader.doDelegation is false. Also see * Note 2. * *

Note 2: * *

If classes are loaded with different class loaders, they belong to * separate name spaces. If class C is loaded by a class * loader CL, all classes that the class C * refers to are also loaded by CL. However, if CL * delegates the loading of the class C to CL', * then those classes that the class C refers to * are loaded by a parent class loader CL' * instead of CL. * *

If an object of class C is assigned * to a variable of class C belonging to a different name * space, then a ClassCastException is thrown. * *

Because of the fact above, this loader delegates only the loading of * javassist.Loader * and classes included in package java.* and * javax.* to the parent class * loader. Other classes are directly loaded by this loader. * *

For example, suppose that java.lang.String would be loaded * by this loader while java.io.File is loaded by the parent * class loader. If the constructor of java.io.File is called * with an instance of java.lang.String, then it may throw * an exception since it accepts an instance of only the * java.lang.String loaded by the parent class loader. * * @see javassist.ClassPool * @see javassist.Translator */ public class Loader extends ClassLoader { private Hashtable notDefinedHere; // must be atomic. private Vector notDefinedPackages; // must be atomic. private ClassPool source; /** * Specifies the algorithm of class loading. * *

This class loader uses the parent class loader for * java.* and javax.* classes. * If this variable doDelegation * is false, this class loader does not delegate those * classes to the parent class loader. * *

The default value is true. */ public boolean doDelegation = true; /** * Creates a new class loader. */ public Loader() { this(null); } /** * Creates a new class loader. * * @param cp the source of class files. */ public Loader(ClassPool cp) { init(cp); } /** * Creates a new class loader * using the specified parent class loader for delegation. * * @param parent the parent class loader. * @param cp the source of class files. */ public Loader(ClassLoader parent, ClassPool cp) { super(parent); init(cp); } private void init(ClassPool cp) { notDefinedHere = new Hashtable(); notDefinedPackages = new Vector(); source = cp; delegateLoadingOf("javassist.Loader"); } /** * Records a class so that the loading of that class is delegated * to the parent class loader. * *

If the given class name ends with . (dot), then * that name is interpreted as a package name. All the classes * in that package and the sub packages are delegated. */ public void delegateLoadingOf(String classname) { if (classname.endsWith(".")) notDefinedPackages.addElement(classname); else notDefinedHere.put(classname, this); } /** * Sets the soruce ClassPool. */ public void setClassPool(ClassPool cp) { source = cp; } /** * Loads a class with an instance of Loader * and calls main() of that class. * *

This method calls run(). * * @param args[0] class name to be loaded. * @param args[1-n] parameters passed to main(). * * @see javassist.Loader#run(String[]) */ public static void main(String[] args) throws Throwable { Loader cl = new Loader(); cl.run(args); } /** * Loads a class and calls main() in that class. * * @param args[0] the name of the loaded class. * @param args[1-n] parameters passed to main(). */ public void run(String[] args) throws Throwable { int n = args.length - 1; if (n >= 0) { String[] args2 = new String[n]; for (int i = 0; i < n; ++i) args2[i] = args[i + 1]; run(args[0], args2); } } /** * Loads a class and calls main() in that class. * * @param classname the loaded class. * @param args parameters passed to main(). */ public void run(String classname, String[] args) throws Throwable { Class c = loadClass(classname); try { c.getDeclaredMethod("main", new Class[] { String[].class }) .invoke(null, new Object[] { args }); } catch (java.lang.reflect.InvocationTargetException e) { throw e.getTargetException(); } } /** * Requests the class loader to load a class. */ protected Class loadClass(String name, boolean resolve) throws ClassFormatError, ClassNotFoundException { Class c = findLoadedClass(name); if (c == null) c = loadClassByDelegation(name); if (c == null) c = findClass(name); if (c == null) c = delegateToParent(name); if (resolve) resolveClass(c); return c; } /** * Finds the specified class using ClassPath. * If the source throws an exception, this returns null. * *

This method can be overridden by a subclass of * Loader. */ protected Class findClass(String name) { byte[] classfile; try { if (source != null) classfile = source.write(name); else { String jarname = "/" + name.replace('.', '/') + ".class"; InputStream in = this.getClass().getResourceAsStream(jarname); classfile = ClassPoolTail.readStream(in); } } catch (Exception e) { return null; } return defineClass(name, classfile, 0, classfile.length); } private Class loadClassByDelegation(String name) throws ClassNotFoundException { /* The swing components must be loaded by a system * class loader. * javax.swing.UIManager loads a (concrete) subclass * of LookAndFeel by a system class loader and cast * an instance of the class to LookAndFeel for * (maybe) a security reason. To avoid failure of * type conversion, LookAndFeel must not be loaded * by this class loader. */ Class c = null; if (doDelegation) if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("sun.") || name.startsWith("com.sun.") || notDelegated(name)) c = delegateToParent(name); return c; } private boolean notDelegated(String name) { if (notDefinedHere.get(name) != null) return true; int n = notDefinedPackages.size(); for (int i = 0; i < n; ++i) if (name.startsWith((String)notDefinedPackages.elementAt(i))) return true; return false; } private Class delegateToParent(String classname) throws ClassNotFoundException { ClassLoader cl = getParent(); if (cl != null) return cl.loadClass(classname); else return findSystemClass(classname); } }