/* * 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:
*
*
* import javassist.*; * * public class Main { * public static void main(String[] args) throws Throwable { * MyTranslator myTrans = new MyTranslator(); * ClassPool cp = ClassPool.getDefault(myTrans); * Loader cl = new Loader(cp); * 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 loading it into the JVM.
* 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 is modified when it is loaded, * a program like the following can be used: * *
ClassPool cp = ClassPool.getDefault(); * Loader cl = new Loader(cp); * * CtClass ct = cp.get("test.Rectangle"); * ct.setSuperclass(cp.get("test.Point")); * * Class c = cl.loadClass("test.Rectangle"); * Object rect = c.newInstance();* *
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;
}
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.
}
}
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);
}
protected Package getPackage(String name) {
return super.getPackage(name);
}
/*
// Package p = super.getPackage(name);
Package p = null;
if (p == null)
return definePackage(name, null, null, null,
null, null, null, null);
else
return p;
}
*/
}