git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@116 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
<h2>Changes</h2> | <h2>Changes</h2> | ||||
<p>- version 3.0 | |||||
<ul> | |||||
<li>CtClass.toClass() has been reimplemented. | |||||
</ul> | |||||
<p>- version 3.0 beta in May 18th, 2004. | <p>- version 3.0 beta in May 18th, 2004. | ||||
<ul> | <ul> |
/** | /** | ||||
* Constructs a CannotCompileException with an <code>Exception</code>. | * Constructs a CannotCompileException with an <code>Exception</code>. | ||||
*/ | */ | ||||
public CannotCompileException(Exception e) { | |||||
public CannotCompileException(Throwable e) { | |||||
super("by " + e.toString()); | super("by " + e.toString()); | ||||
message = null; | message = null; | ||||
} | } |
return (Object[])cflow.get(name); | return (Object[])cflow.get(name); | ||||
} | } | ||||
/** | |||||
* A simple class loader used by <code>toClass()</code> | |||||
* in <code>CtClass</code>. | |||||
* This class loader is provided for convenience. If you need more | |||||
* complex functionality, you should write your own class loader. | |||||
* | |||||
* @see CtClass#forName(String) | |||||
* @see CtClass#toClass() | |||||
* @see ClassPool#toClass(CtClass) | |||||
* @see ClassPool#forName(String) | |||||
*/ | |||||
public static class SimpleLoader extends ClassLoader { | |||||
/** | |||||
* Loads a class. | |||||
* | |||||
* @param name the fully qualified class name. | |||||
* @param classfile the class file. | |||||
* @throws ClassFormatError if the class file is wrong. | |||||
*/ | |||||
public Class loadClass(String name, byte[] classfile) | |||||
throws ClassFormatError | |||||
{ | |||||
Class c = defineClass(name, classfile, 0, classfile.length); | |||||
resolveClass(c); | |||||
return c; | |||||
} | |||||
}; | |||||
private static SimpleLoader classLoader = null; | |||||
/** | |||||
* Returns a <code>java.lang.Class</code> object that has been loaded | |||||
* by <code>toClass()</code> in <code>CtClass</code> | |||||
* or <code>ClassPool</code>. Such an object cannot be | |||||
* obtained by <code>java.lang.Class.forName()</code> because it has | |||||
* been loaded by an internal class loader of Javassist. | |||||
* | |||||
* @param name the fully-qualified class name. | |||||
* @see #toClass(CtClass) | |||||
*/ | |||||
public Class forName(String name) throws ClassNotFoundException { | |||||
if (classLoader == null) | |||||
classLoader = new SimpleLoader(); | |||||
return classLoader.loadClass(name); | |||||
} | |||||
/** | |||||
* Loads the class represented by a <code>CtClass</code> object. | |||||
* <code>toClass()</code> in <code>CtClass</code> calls this method | |||||
* to load a class. | |||||
* | |||||
* <p>This method can be overridden to change the class loader. | |||||
* In the default implementation, | |||||
* the class loader is a <code>SimpleLoader</code>. | |||||
* If this method is overridden, <code>forName()</code> must be also | |||||
* overridden for extending the behavior. | |||||
* | |||||
* @param cc the loaded <code>CtClass</code>. | |||||
* @return the <code>java.lang.Class</code> object representing | |||||
* the loaded class. | |||||
* @see #forName(String) | |||||
*/ | |||||
public Class toClass(CtClass cc) | |||||
throws NotFoundException, CannotCompileException, IOException | |||||
{ | |||||
if (cc.getClassPool() != this) | |||||
throw new CannotCompileException( | |||||
"not contained in this class pool: " + cc.getName()); | |||||
try { | |||||
if (classLoader == null) | |||||
classLoader = new SimpleLoader(); | |||||
return classLoader.loadClass(cc.getName(), cc.toBytecode()); | |||||
} | |||||
catch (ClassFormatError e) { | |||||
throw new CannotCompileException(e, cc.getName()); | |||||
} | |||||
} | |||||
/** | /** | ||||
* Reads a class file and constructs a <code>CtClass</code> | * Reads a class file and constructs a <code>CtClass</code> | ||||
* object with a new name. | * object with a new name. |
/** | /** | ||||
* Converts this class to a <code>java.lang.Class</code> object. | * Converts this class to a <code>java.lang.Class</code> object. | ||||
* Once this method is called, further modifications are not | * Once this method is called, further modifications are not | ||||
* possible any more. | |||||
* allowed any more. | |||||
* To load the class, this method uses the context class loader | |||||
* of the current thread. If the program is running on some application | |||||
* server, the context class loader might be inappropriate to load the | |||||
* class. | |||||
* | * | ||||
* <p>This method is provided for convenience. If you need more | * <p>This method is provided for convenience. If you need more | ||||
* complex functionality, you should write your own class loader. | * complex functionality, you should write your own class loader. | ||||
* | * | ||||
* <p>To load a class file, this method uses an internal class loader, | |||||
* which is an instance of <code>ClassPool.SimpleLoader</code>. | |||||
* Thus, that class file is not loaded by the system class loader, | |||||
* which should have loaded this <code>CtClass</code> class. | |||||
* The internal class loader | |||||
* loads only the classes explicitly specified by this method | |||||
* <code>toClass()</code>. The other classes are loaded | |||||
* by the parent class loader (usually the sytem class loader) | |||||
* by delegation. | |||||
* | |||||
* <p>For example, | |||||
* | |||||
* <ul><pre>class Line { Point p1, p2; }</pre></ul> | |||||
* | |||||
* <p>If the class <code>Line</code> is loaded by the internal class | |||||
* loader and the class <code>Point</code> has not been loaded yet, | |||||
* then the class <code>Point</code> that the class <code>Line</code> | |||||
* refers to is loaded by the parent class loader. There is no | |||||
* chance of modifying the definition of <code>Point</code> with | |||||
* Javassist. | |||||
* | |||||
* <p>The internal class loader is shared among all the instances | |||||
* of <code>ClassPool</code>. | |||||
* | |||||
* @return the <code>Class</code> object representing the loaded class. | |||||
* @see CtClass#forName(String) | |||||
* @see ClassPool.SimpleLoader | |||||
* @see Loader | |||||
* @see #toClass(java.lang.ClassLoader) | |||||
*/ | */ | ||||
public Class toClass() | public Class toClass() | ||||
throws NotFoundException, IOException, CannotCompileException | |||||
throws CannotCompileException | |||||
{ | { | ||||
return getClassPool().toClass(this); | |||||
return toClass(Thread.currentThread().getContextClassLoader()); | |||||
} | } | ||||
/** | /** | ||||
* Returns a <code>java.lang.Class</code> object that has been loaded | |||||
* by <code>toClass()</code>. Such an object cannot be | |||||
* obtained by <code>java.lang.Class.forName()</code> because it has | |||||
* been loaded by an internal class loader of Javassist. | |||||
* Converts this class to a <code>java.lang.Class</code> object. | |||||
* Once this method is called, further modifications are not allowed | |||||
* any more. | |||||
* | |||||
* <p>The class file represented by this <code>CtClass</code> is | |||||
* loaded by the given class loader to construct a | |||||
* <code>java.lang.Class</code> object. Since a private method | |||||
* on the class loader is invoked through the reflection API, | |||||
* the caller must have permissions to do that. | |||||
* | * | ||||
* @see CtClass#toClass() | |||||
* @see ClassPool.SimpleLoader | |||||
* <p>This method is provided for convenience. If you need more | |||||
* complex functionality, you should write your own class loader. | |||||
* | |||||
* @param loader the class loader used to load this class. | |||||
*/ | */ | ||||
public Class forName(String name) throws ClassNotFoundException { | |||||
return getClassPool().forName(name); | |||||
public Class toClass(ClassLoader loader) | |||||
throws CannotCompileException | |||||
{ | |||||
try { | |||||
byte[] b = toBytecode(); | |||||
Class cl = Class.forName("java.lang.ClassLoader"); | |||||
java.lang.reflect.Method method | |||||
= cl.getDeclaredMethod("defineClass", | |||||
new Class[] { String.class, byte[].class, | |||||
int.class, int.class }); | |||||
method.setAccessible(true); | |||||
Object[] args = new Object[] { getName(), b, new Integer(0), | |||||
new Integer(b.length)}; | |||||
Class clazz = (Class)method.invoke(loader, args); | |||||
method.setAccessible(false); | |||||
return clazz; | |||||
} | |||||
catch (RuntimeException e) { | |||||
throw e; | |||||
} | |||||
catch (java.lang.reflect.InvocationTargetException e) { | |||||
throw new CannotCompileException(e.getTargetException()); | |||||
} | |||||
catch (Exception e) { | |||||
throw new CannotCompileException(e); | |||||
} | |||||
} | } | ||||
/** | /** | ||||
* @return the contents of the class file. | * @return the contents of the class file. | ||||
*/ | */ | ||||
public byte[] toBytecode() | public byte[] toBytecode() | ||||
throws NotFoundException, IOException, CannotCompileException | |||||
throws IOException, CannotCompileException | |||||
{ | { | ||||
ByteArrayOutputStream barray = new ByteArrayOutputStream(); | ByteArrayOutputStream barray = new ByteArrayOutputStream(); | ||||
DataOutputStream out = new DataOutputStream(barray); | DataOutputStream out = new DataOutputStream(barray); |
/* | |||||
* Javassist, a Java-bytecode translator toolkit. | |||||
* Copyright (C) 1999-2004 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.reflect; | |||||
import javassist.CannotCompileException; | |||||
/** | |||||
* Thrown by <code>makeReflective()</code> in <code>Reflection</code> | |||||
* when there is an attempt to reflect | |||||
* a class that is either an interface or a subclass of | |||||
* either ClassMetaobject or Metaobject. | |||||
* | |||||
* @author Brett Randall | |||||
* @see javassist.reflect.Reflection#makeReflective(CtClass,CtClass,CtClass) | |||||
* @see javassist.CannotCompileException | |||||
*/ | |||||
public class CannotReflectException extends CannotCompileException { | |||||
public CannotReflectException(String msg) { | |||||
super(msg); | |||||
} | |||||
} |
static final String readPrefix = "_r_"; | static final String readPrefix = "_r_"; | ||||
static final String writePrefix = "_w_"; | static final String writePrefix = "_w_"; | ||||
static final String metaobjectClassName = "javassist.reflect.Metaobject"; | |||||
static final String classMetaobjectClassName | |||||
= "javassist.reflect.ClassMetaobject"; | |||||
protected CtMethod trapMethod, trapStaticMethod; | protected CtMethod trapMethod, trapStaticMethod; | ||||
protected CtMethod trapRead, trapWrite; | protected CtMethod trapRead, trapWrite; | ||||
protected CtClass[] readParam; | protected CtClass[] readParam; | ||||
*/ | */ | ||||
public boolean makeReflective(CtClass clazz, | public boolean makeReflective(CtClass clazz, | ||||
CtClass metaobject, CtClass metaclass) | CtClass metaobject, CtClass metaclass) | ||||
throws CannotCompileException, NotFoundException | |||||
throws CannotCompileException, CannotReflectException, | |||||
NotFoundException | |||||
{ | { | ||||
if (clazz.isInterface()) | |||||
throw new CannotReflectException( | |||||
"Cannot reflect an interface: " + clazz.getName()); | |||||
if (clazz.subclassOf(classPool.get(classMetaobjectClassName))) | |||||
throw new CannotReflectException( | |||||
"Cannot reflect a subclass of ClassMetaobject: " | |||||
+ clazz.getName()); | |||||
if (clazz.subclassOf(classPool.get(metaobjectClassName))) | |||||
throw new CannotReflectException( | |||||
"Cannot reflect a subclass of Metaobject: " | |||||
+ clazz.getName()); | |||||
registerReflectiveClass(clazz); | registerReflectiveClass(clazz); | ||||
return modifyClassfile(clazz, metaobject, metaclass); | return modifyClassfile(clazz, metaobject, metaclass); | ||||
} | } |
version of class loader but they can also use a class loader provided | version of class loader but they can also use a class loader provided | ||||
by Javassist. | by Javassist. | ||||
<p>Using a class loader is not easy. In particular, if you are a | |||||
beginner, you should separate your program into an application program | |||||
and an instrumentation program. Then you should load only the former | |||||
program by a user-defined class loader. The latter one, as well as | |||||
the program of the user-defined class loader, should be loaded by the | |||||
system class loader. | |||||
<ul> | |||||
<b>Note:</b> The JVM does not allow dynamically reloading a class. | |||||
Once a class loader loads a class, it cannot reload a modified | |||||
version of that class during runtime. Thus, you cannot alter | |||||
the definition of a class after the JVM loads it. | |||||
However, the JPDA (Java Platform Debugger Architecture) provides | |||||
limited ability for reloading a class. See "HotSwap" of JPDA for details. | |||||
</ul> | |||||
<p><br> | |||||
<h3>4.1 The <code>toClass</code> method in <code>CtClass</code></h3> | |||||
<p>The <code>CtClass</code> provides a convenience method | |||||
<code>toClass()</code>, which requests the context class loader for | |||||
the current thread to load the class represented by the <code>CtClass</code> | |||||
object. To call this method, the caller must have appropriate permission; | |||||
otherwise, a <code>SecurityException</code> may be thrown. | |||||
<p>The following program shows how to use <code>toClass()</code>: | |||||
<ul><pre> | |||||
public class Hello { | |||||
public void say() { | |||||
System.out.println("Hello"); | |||||
} | |||||
} | |||||
public class Test { | |||||
public static void main(String[] args) throws Exception { | |||||
ClassPool cp = ClassPool.getDefault(); | |||||
CtClass cc = cp.get("Hello"); | |||||
CtMethod m = cc.getDeclaredMethod("say"); | |||||
m.insertBefore("{ System.out.println(\"Hello.say():\"); }"); | |||||
Class c = cc.toClass(); | |||||
Hello h = (Hello)c.newInstance(); | |||||
h.say(); | |||||
} | |||||
} | |||||
</pre></ul> | |||||
<p><code>Test.main()</code> inserts a call to <code>println()</code> | |||||
in the method body of <code>say()</code> in <code>Hello</code>. Then | |||||
it constructs an instance of the modified <code>Hello</code> class | |||||
and calls <code>say()</code> on that instance. | |||||
<p>Note that the program above depends on the fact that the | |||||
<code>Hello</code> class is never loaded before <code>toClass()</code> | |||||
is invoked. If not, the JVM would load the original | |||||
<code>Hello</code> class before <code>toClass()</code> request to load | |||||
the modified <code>Hello</code> class. Hence loading the modified | |||||
<code>Hello</code> class would be failed. | |||||
For example, if | |||||
<code>main()</code> in <code>Test</code> is something like this: | |||||
<ul><pre> | |||||
public static void main(String[] args) throws Exception { | |||||
Hello orig = new Hello(); | |||||
ClassPool cp = ClassPool.getDefault(); | |||||
CtClass cc = cp.get("Hello"); | |||||
: | |||||
} | |||||
</pre></ul> | |||||
<p>then the original <code>Hello</code> class is loaded at the first | |||||
line of <code>main</code> and the call to <code>toClass()</code> | |||||
throws an exception since the class loader cannot load two different | |||||
versions of the <code>Hello</code> class at the same time. | |||||
<p><em>If the program is running on some application server such as | |||||
JBoss,</em> the context class loader used by <code>toClass()</code> | |||||
might be inappropriate. In this case, you would see an unexpected | |||||
<code>ClassCastException</code>. To avoid this exception, you must | |||||
explicitly give an appropriate class loader to <code>toClass()</code>. | |||||
For example, if <code>bean</code> is your session bean object, then the | |||||
following code: | |||||
<ul><pre>CtClass cc = ...; | |||||
Class c = cc.toClass(bean.getClass().getClassLoader()); | |||||
</pre></ul> | |||||
<p>would work. You should give <code>toClass()</code> the class loader | |||||
that has loaded your program (in the above example, the class of | |||||
the <code>bean</code> object). | |||||
<p><code>toClass()</code> is provided for convenience. If you need | |||||
more complex functionality, you should write your own class loader. | |||||
<p><br> | <p><br> | ||||
<h3>4.1 Class loading in Java</h3> | |||||
<h3>4.2 Class loading in Java</h3> | |||||
<p>In Java, multiple class loaders can coexist and | <p>In Java, multiple class loaders can coexist and | ||||
each class loader creates its own name space. | each class loader creates its own name space. | ||||
Different class loaders can load different class files with the | Different class loaders can load different class files with the | ||||
same class name. The loaded two classes are regarded as different | same class name. The loaded two classes are regarded as different | ||||
ones. This feature enables us to run multiple application programs | ones. This feature enables us to run multiple application programs | ||||
on a single JVM. | |||||
on a single JVM even if these programs include different classes | |||||
with the same name. | |||||
<ul> | |||||
<b>Note:</b> The JVM does not allow dynamically reloading a class. | |||||
Once a class loader loads a class, it cannot reload a modified | |||||
version of that class during runtime. Thus, you cannot alter | |||||
the definition of a class after the JVM loads it. | |||||
However, the JPDA (Java Platform Debugger Architecture) provides | |||||
limited ability for reloading a class. See "HotSwap" of JPDA for details. | |||||
</ul> | |||||
<p>If the same class file is loaded by two distinct class loaders, | <p>If the same class file is loaded by two distinct class loaders, | ||||
the JVM makes two distinct classes with the same name and definition. | the JVM makes two distinct classes with the same name and definition. | ||||
The two classes are regarded as different ones. | The two classes are regarded as different ones. | ||||
Since the two classes are not identical, an instance of one class is | Since the two classes are not identical, an instance of one class is | ||||
not assignable to a variable of the other class. The cast operation | not assignable to a variable of the other class. The cast operation | ||||
between the two classes fails and throws a <code>ClassCastException</code>. | |||||
between the two classes fails | |||||
and throws a <em><code>ClassCastException</code></em>. | |||||
<p>For example, the following code snippet throws an exception: | |||||
<ul><pre> | |||||
MyClassLoader myLoader = new MyClassLoader(); | |||||
Class clazz = myLoader.loadClass("Box"); | |||||
Object obj = clazz.newInstance(); | |||||
Box b = (Box)obj; // this always throws ClassCastException. | |||||
</pre></ul> | |||||
<p> | |||||
The <code>Box</code> class is loaded by two class loaders. | |||||
Suppose that a class loader CL loads a class including this code snippet. | |||||
Since this code snippet refers to <code>MyClassLoader</code>, | |||||
<code>Class</code>, <code>Object</code>, and <code>Box</code>, | |||||
CL also loads these classes (unless it delegates to another class loader). | |||||
Hence the type of the variable <code>b</code> is the <code>Box</code> | |||||
class loaded by CL. | |||||
On the other hand, <code>myLoader</code> also loads the <code>Box</code> | |||||
class. The object <code>obj</code> is an instance of | |||||
the <code>Box</code> class loaded by <code>myLoader</code>. | |||||
Therefore, the last statement always throws a | |||||
<code>ClassCastException</code> since the class of <code>obj</code> is | |||||
a different verison of the <code>Box</code> class from one used as the | |||||
type of the variable <code>b</code>. | |||||
<p>Multiple class loaders form a tree structure. | <p>Multiple class loaders form a tree structure. | ||||
Each class loader except the bootstrap loader has a | Each class loader except the bootstrap loader has a | ||||
<p><br> | <p><br> | ||||
<h3>4.2 Using <code>javassist.Loader</code></h3> | |||||
<h3>4.3 Using <code>javassist.Loader</code></h3> | |||||
<p>Javassist provides a class loader | <p>Javassist provides a class loader | ||||
<code>javassist.Loader</code>. This class loader uses a | <code>javassist.Loader</code>. This class loader uses a | ||||
<p>To run this program, do: | <p>To run this program, do: | ||||
<ul><pre> | <ul><pre> | ||||
% java Main <i>arg1</i> <i>arg2</i>... | |||||
% java Main2 <i>arg1</i> <i>arg2</i>... | |||||
</pre></ul> | </pre></ul> | ||||
<p>The class <code>MyApp</code> and the other application classes | <p>The class <code>MyApp</code> and the other application classes | ||||
<p>Note that <em>application</em> classes like <code>MyApp</code> cannot | <p>Note that <em>application</em> classes like <code>MyApp</code> cannot | ||||
access the <em>loader</em> classes such as <code>Main2</code>, | access the <em>loader</em> classes such as <code>Main2</code>, | ||||
<code>MyTranslator</code> and <code>ClassPool</code> because they | |||||
<code>MyTranslator</code>, and <code>ClassPool</code> because they | |||||
are loaded by different loaders. The application classes are loaded | are loaded by different loaders. The application classes are loaded | ||||
by <code>javassist.Loader</code> whereas the loader classes such as | by <code>javassist.Loader</code> whereas the loader classes such as | ||||
<code>Main</code> are by the default Java class loader. | |||||
<code>Main2</code> are by the default Java class loader. | |||||
<p><code>javassist.Loader</code> searches for classes in a different | <p><code>javassist.Loader</code> searches for classes in a different | ||||
order from <code>java.lang.ClassLoader</code>. | order from <code>java.lang.ClassLoader</code>. | ||||
<p><br> | <p><br> | ||||
<h3>4.3 Writing a class loader</h3> | |||||
<h3>4.4 Writing a class loader</h3> | |||||
<p>A simple class loader using Javassist is as follows: | <p>A simple class loader using Javassist is as follows: | ||||
<p><br> | <p><br> | ||||
<h3>4.4 The <code>toClass</code> method in <code>CtClass</code></h3> | |||||
<p>The <code>CtClass</code> provides a convenience method | |||||
<code>toClass</code>, which loads the class by an internal class | |||||
loader of Javassist. This method first obtains the class file | |||||
representing the modified class and loads it by an instance of | |||||
<code>javassist.ClassPool.SimpleLoader</code>. | |||||
The following code is the definition of this class loader: | |||||
<ul><pre> | |||||
public class SimpleLoader extends ClassLoader { | |||||
public Class loadClass(String classname, byte[] classfile) | |||||
throws ClassFormatError | |||||
{ | |||||
Class c = defineClass(classname, classfile, 0, classfile.length); | |||||
resolveClass(c); | |||||
return c; | |||||
} | |||||
}; | |||||
</pre></ul> | |||||
<p><code>loadClass()</code> loads the class specified by | |||||
<code>classfile</code>. | |||||
Thus, <code>toClass()</code> is equivalent to the following code: | |||||
<ul><pre> | |||||
CtClass cc = ... ; | |||||
ClassPool.SimpleLoader cl = new ClassPool.SimpleLoader(); | |||||
Class c = cl.loadClass(cc.getName(), cc.toBytecode()); | |||||
</pre></ul> | |||||
<p>Note that this class loader might be too simple for realistic use. | |||||
It delegates to the parent class loader unless the class is explicitly | |||||
loaded by <code>loadClass()</code> (or <code>toClass()</code> in | |||||
<code>CtClass</code>). If you encounter an unexpected | |||||
<code>ClassCastException</code>, you should check the class loader of | |||||
the object. Call <code>getClass().getClassLoader()</code> on the | |||||
object and make sure that the destination class and the source class | |||||
of the cast operation have been loaded by the same class loader. | |||||
<p><br> | |||||
<h3>4.5 Modifying a system class</h3> | <h3>4.5 Modifying a system class</h3> | ||||
<p>The system classes like <code>java.lang.String</code> cannot be | <p>The system classes like <code>java.lang.String</code> cannot be |