/*
* 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: - ClassPool
- Translator
/**
* 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: - NotFoundException – if
t.start()
throws an exception. - CannotCompileException – if
t.start()
throws an exception.
/**
* 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> {@code args[0]} is the class name to be loaded.
* <br> {@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> {@code args[0]} is the class name to be loaded.
* <br> {@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: - ClassNotFoundException – if an exception is thrown while
obtaining a class file.
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);
}
}