/*
 * 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 javassist;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Vector;

import javassist.bytecode.ClassFile;

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:

import javassist.*;
public class Main {
  public static void main(String[] args) throws Throwable {
    MyTranslator myTrans = new MyTranslator();
    ClassPool cp = ClassPool.getDefault();
    Loader cl = new Loader(cp);
    cl.addTranslator(cp, myTrans);
    cl.run("MyApp", args);
  }
}

Class MyApp is the main program of the application.

This program should be executed as follows:

% java Main arg1 arg2...

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:

% java MyApp arg1 arg2...

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:

ClassPool cp = ClassPool.getDefault();
Loader cl = new Loader(cp);
CtClass ct = cp.get("test.Rectangle");
ct.setSuperclass(cp.get("test.Point"));
cl.run("MyApp", args);

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 Also:
/** * The class loader for Javassist. * * <p>This is a sample class loader using <code>ClassPool</code>. * Unlike a regular class loader, this class loader obtains bytecode * from a <code>ClassPool</code>. * * <p>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. * * <p>Suppose that an instance of <code>MyTranslator</code> implementing * the interface <code>Translator</code> is responsible for modifying * class files. * The startup program of an application using <code>MyTranslator</code> * should be something like this: * * <pre> * import javassist.*; * * public class Main { * public static void main(String[] args) throws Throwable { * MyTranslator myTrans = new MyTranslator(); * ClassPool cp = ClassPool.getDefault(); * Loader cl = new Loader(cp); * cl.addTranslator(cp, myTrans); * cl.run("MyApp", args); * } * } * </pre> * * <p>Class <code>MyApp</code> is the main program of the application. * * <p>This program should be executed as follows: * * <pre> * % java Main <i>arg1</i> <i>arg2</i>... * </pre> * * <p>It modifies the class <code>MyApp</code> with a <code>MyTranslator</code> * object before the JVM loads it. * Then it calls <code>main()</code> in <code>MyApp</code> with arguments * <i>arg1</i>, <i>arg2</i>, ... * * <p>This program execution is equivalent to: * * <pre> * % java MyApp <i>arg1</i> <i>arg2</i>... * </pre> * * <p>except that classes are translated by <code>MyTranslator</code> * at load time. * * <p>If only a particular class must be modified when it is loaded, * the startup program can be simpler; <code>MyTranslator</code> is * unnecessary. For example, if only a class <code>test.Rectangle</code> * is modified, the <code>main()</code> method above will be the following: * * <pre> * ClassPool cp = ClassPool.getDefault(); * Loader cl = new Loader(cp); * CtClass ct = cp.get("test.Rectangle"); * ct.setSuperclass(cp.get("test.Point")); * cl.run("MyApp", args);</pre> * * <p>This program changes the super class of the <code>test.Rectangle</code> * class. * * <p><b>Note 1:</b> * * <p>This class loader does not allow the users to intercept the loading * of <code>java.*</code> and <code>javax.*</code> classes (and * <code>sun.*</code>, <code>org.xml.*</code>, ...) unless * <code>Loader.doDelegation</code> is <code>false</code>. 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 <code>Loader</code> * must be defined and <code>loadClassByDelegation()</code> must be overridden. * * <p><b>Note 2:</b> * * <p>If classes are loaded with different class loaders, they belong to * separate name spaces. If class <code>C</code> is loaded by a class * loader <code>CL</code>, all classes that the class <code>C</code> * refers to are also loaded by <code>CL</code>. However, if <code>CL</code> * delegates the loading of the class <code>C</code> to <code>CL'</code>, * then those classes that the class <code>C</code> refers to * are loaded by a parent class loader <code>CL'</code> * instead of <code>CL</code>. * * <p>If an object of class <code>C</code> is assigned * to a variable of class <code>C</code> belonging to a different name * space, then a <code>ClassCastException</code> is thrown. * * <p>Because of the fact above, this loader delegates only the loading of * <code>javassist.Loader</code> * and classes included in package <code>java.*</code> and * <code>javax.*</code> to the parent class * loader. Other classes are directly loaded by this loader. * * <p>For example, suppose that <code>java.lang.String</code> would be loaded * by this loader while <code>java.io.File</code> is loaded by the parent * class loader. If the constructor of <code>java.io.File</code> is called * with an instance of <code>java.lang.String</code>, then it may throw * an exception since it accepts an instance of only the * <code>java.lang.String</code> loaded by the parent class loader. * * @see javassist.ClassPool * @see javassist.Translator */
public class Loader extends ClassLoader {
A simpler class loader. This is a class loader that exposes the protected defineClass() method declared in java.lang.ClassLoader. It provides a method similar to CtClass#toClass().

When loading a class, this class loader delegates the work to the parent class loader unless the loaded classes are explicitly given by invokeDefineClass(CtClass). Note that a class Foo loaded by this class loader is different from the class with the same name Foo but loaded by another class loader. This is Java's naming rule.

Since:3.24
/** * A simpler class loader. * This is a class loader that exposes the protected {@code defineClass()} method * declared in {@code java.lang.ClassLoader}. It provides a method similar to * {@code CtClass#toClass()}. * * <p>When loading a class, this class loader delegates the work to the * parent class loader unless the loaded classes are explicitly given * by {@link #invokeDefineClass(CtClass)}. * Note that a class {@code Foo} loaded by this class loader is * different from the class with the same name {@code Foo} but loaded by * another class loader. This is Java's naming rule. * </p> * * @since 3.24 */
public static class Simple extends ClassLoader {
Constructs a class loader.
/** * Constructs a class loader. */
public Simple() {}
Constructs a class loader.
Params:
  • parent – the parent class loader.
/** * Constructs a class loader. * @param parent the parent class loader. */
public Simple(ClassLoader parent) { super(parent); }
Invokes the protected defineClass() in ClassLoader. It converts the given CtClass object into a java.lang.Class object.
/** * Invokes the protected {@code defineClass()} in {@code ClassLoader}. * It converts the given {@link CtClass} object into a {@code java.lang.Class} object. */
public Class<?> invokeDefineClass(CtClass cc) throws IOException, CannotCompileException { byte[] code = cc.toBytecode(); return defineClass(cc.getName(), code, 0, code.length); } } private HashMap<String,ClassLoader> notDefinedHere; // must be atomic. private Vector<String> notDefinedPackages; // must be atomic. private 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.

/** * Specifies the algorithm of class loading. * * <p>This class loader uses the parent class loader for * <code>java.*</code> and <code>javax.*</code> classes. * If this variable <code>doDelegation</code> * is <code>false</code>, this class loader does not delegate those * classes to the parent class loader. * * <p>The default value is <code>true</code>. */
public boolean doDelegation = true;
Creates a new class loader.
/** * Creates a new class loader. */
public Loader() { this(null); }
Creates a new class loader.
Params:
  • cp – the source of class files.
/** * 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.
Params:
  • parent – the parent class loader.
  • cp – the source of class files.
/** * 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 HashMap<String,ClassLoader>(); notDefinedPackages = new Vector<String>(); 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.

/** * Records a class so that the loading of that class is delegated * to the parent class loader. * * <p>If the given class name ends with <code>.</code> (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.
/** * 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.
/** * Sets the soruce <code>ClassPool</code>. */
public void setClassPool(ClassPool cp) { source = cp; }
Adds a translator, which is called whenever a class is loaded.
Params:
  • cp – the ClassPool object for obtaining a class file.
  • t – a translator.
Throws:
/** * Adds a translator, which is called whenever a class is loaded. * * @param cp the <code>ClassPool</code> object for obtaining * a class file. * @param t a translator. * @throws NotFoundException if <code>t.start()</code> throws an exception. * @throws CannotCompileException if <code>t.start()</code> 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().

Params:
  • args – command line parameters.
    args[0] is the class name to be loaded.
    args[1..n] are parameters passed to the target main().
See Also:
/** * Loads a class with an instance of <code>Loader</code> * and calls <code>main()</code> of that class. * * <p>This method calls <code>run()</code>. * * @param args command line parameters. * <br>&nbsp;&nbsp;{@code args[0]} is the class name to be loaded. * <br>&nbsp;&nbsp;{@code args[1..n]} are parameters passed * to the target {@code 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.
Params:
  • args – command line parameters.
    args[0] is the class name to be loaded.
    args[1..n] are parameters passed to the target main().
/** * Loads a class and calls <code>main()</code> in that class. * * @param args command line parameters. * * <br>&nbsp;&nbsp;{@code args[0]} is the class name to be loaded. * <br>&nbsp;&nbsp;{@code args[1..n]} are parameters passed * to the target {@code main()}. */
public void run(String[] args) throws Throwable { if (args.length >= 1) run(args[0], Arrays.copyOfRange(args, 1, args.length)); }
Loads a class and calls main() in that class.
Params:
  • classname – the loaded class.
  • args – parameters passed to main().
/** * Loads a class and calls <code>main()</code> in that class. * * @param classname the loaded class. * @param args parameters passed to <code>main()</code>. */
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 (InvocationTargetException e) { throw e.getTargetException(); } }
Requests the class loader to load a class.
/** * Requests the class loader to load a class. */
@Override 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.

Throws:
Returns: null if the specified class could not be found.
/** * Finds the specified class using <code>ClassPath</code>. * If the source throws an exception, this returns null. * * <p>This method can be overridden by a subclass of * <code>Loader</code>. 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. */
@Override 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 = 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 (isDefinedPackage(pname)) 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); return defineClass(name, classfile, 0, classfile.length, domain); } private boolean isDefinedPackage(String name) { if (ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9) return getDefinedPackage(name) == null; else return getPackage(name) == null; } 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.containsKey(name)) return true; for (String pack : notDefinedPackages) if (name.startsWith(pack)) return true; return false; } protected Class<?> delegateToParent(String classname) throws ClassNotFoundException { ClassLoader cl = getParent(); if (cl != null) return cl.loadClass(classname); return findSystemClass(classname); } }