/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999- 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, * or the Apache License Version 2.0. * * 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 com.fr.third.javassist; import java.io.*; import java.util.Hashtable; import java.util.Vector; import java.security.ProtectionDomain; /** * The class loader for Javassist. * *

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 the JVM loads it. * 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 must be modified when it is loaded, * the startup program can be simpler; MyTranslator is * unnecessary. For example, if only a class test.Rectangle * is modified, the main() method above will be the following: * *

* *

This program changes the super class of the test.Rectangle * class. * *

Note 1: * *

This class loader does not allow the users to intercept the loading * of java.* and javax.* classes (and * sun.*, org.xml.*, ...) unless * Loader.doDelegation is false. This is because * the JVM prohibits a user class loader from loading a system class. * Also see Note 2. * If this behavior is not appropriate, a subclass of Loader * must be defined and loadClassByDelegation() must be overridden. * *

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 com.fr.third.javassist.ClassPool * @see Translator */ public class Loader extends ClassLoader { private Hashtable notDefinedHere; // must be atomic. private Vector notDefinedPackages; // must be atomic. private com.fr.third.javassist.ClassPool source; private Translator translator; private ProtectionDomain domain; /** * 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(com.fr.third.javassist.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, com.fr.third.javassist.ClassPool cp) { super(parent); init(cp); } private void init(com.fr.third.javassist.ClassPool cp) { notDefinedHere = new Hashtable(); notDefinedPackages = new Vector(); source = cp; translator = null; domain = null; 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 protection domain for the classes handled by this class * loader. Without registering an appropriate protection domain, * the program loaded by this loader will not work with a security * manager or a signed jar file. */ public void setDomain(ProtectionDomain d) { domain = d; } /** * Sets the soruce ClassPool. */ public void setClassPool(com.fr.third.javassist.ClassPool cp) { source = cp; } /** * Adds a translator, which is called whenever a class is loaded. * * @param cp the ClassPool object for obtaining * a class file. * @param t a translator. * @throws NotFoundException if t.start() throws an exception. * @throws com.fr.third.javassist.CannotCompileException if t.start() throws an exception. */ public void addTranslator(ClassPool cp, Translator t) throws NotFoundException, CannotCompileException { source = cp; translator = t; t.start(cp); } /** * Loads a class with an instance of Loader * and calls main() of that class. * *

This method calls run(). * * @param args command line parameters. *

* * @see 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 command line parameters. * */ 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 { name = name.intern(); synchronized (name) { 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. Note that the overridden method must not throw * an exception when it just fails to find a class file. * * @return null if the specified class could not be found. * @throws ClassNotFoundException if an exception is thrown while * obtaining a class file. */ protected Class findClass(String name) throws ClassNotFoundException { byte[] classfile; try { if (source != null) { if (translator != null) translator.onLoad(source, name); try { classfile = source.get(name).toBytecode(); } catch (NotFoundException e) { return null; } } else { String jarname = "/" + name.replace('.', '/') + ".class"; InputStream in = this.getClass().getResourceAsStream(jarname); if (in == null) return null; classfile = com.fr.third.javassist.ClassPoolTail.readStream(in); } } catch (Exception e) { throw new ClassNotFoundException( "caught an exception while obtaining a class file for " + name, e); } int i = name.lastIndexOf('.'); if (i != -1) { String pname = name.substring(0, i); if (getPackage(pname) == null) try { definePackage( pname, null, null, null, null, null, null, null); } catch (IllegalArgumentException e) { // ignore. maybe the package object for the same // name has been created just right away. } } if (domain == null) return defineClass(name, classfile, 0, classfile.length); else return defineClass(name, classfile, 0, classfile.length, domain); } protected 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.") || name.startsWith("org.w3c.") || name.startsWith("org.xml.") || 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; } protected Class delegateToParent(String classname) throws ClassNotFoundException { ClassLoader cl = getParent(); if (cl != null) return cl.loadClass(classname); else return findSystemClass(classname); } }