git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@116 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
@@ -255,6 +255,12 @@ see javassist.Dump. | |||
<h2>Changes</h2> | |||
<p>- version 3.0 | |||
<ul> | |||
<li>CtClass.toClass() has been reimplemented. | |||
</ul> | |||
<p>- version 3.0 beta in May 18th, 2004. | |||
<ul> |
@@ -41,7 +41,7 @@ public class CannotCompileException extends Exception { | |||
/** | |||
* Constructs a CannotCompileException with an <code>Exception</code>. | |||
*/ | |||
public CannotCompileException(Exception e) { | |||
public CannotCompileException(Throwable e) { | |||
super("by " + e.toString()); | |||
message = null; | |||
} |
@@ -219,87 +219,6 @@ public class ClassPool { | |||
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> | |||
* object with a new name. |
@@ -843,57 +843,65 @@ public abstract class CtClass { | |||
/** | |||
* Converts this class to a <code>java.lang.Class</code> object. | |||
* 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 | |||
* 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() | |||
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); | |||
} | |||
} | |||
/** | |||
@@ -921,7 +929,7 @@ public abstract class CtClass { | |||
* @return the contents of the class file. | |||
*/ | |||
public byte[] toBytecode() | |||
throws NotFoundException, IOException, CannotCompileException | |||
throws IOException, CannotCompileException | |||
{ | |||
ByteArrayOutputStream barray = new ByteArrayOutputStream(); | |||
DataOutputStream out = new DataOutputStream(barray); |
@@ -0,0 +1,34 @@ | |||
/* | |||
* 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); | |||
} | |||
} |
@@ -66,6 +66,10 @@ public class Reflection implements Translator { | |||
static final String readPrefix = "_r_"; | |||
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 trapRead, trapWrite; | |||
protected CtClass[] readParam; | |||
@@ -189,8 +193,23 @@ public class Reflection implements Translator { | |||
*/ | |||
public boolean makeReflective(CtClass clazz, | |||
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); | |||
return modifyClassfile(clazz, metaobject, metaclass); | |||
} |
@@ -393,39 +393,139 @@ modified at load time. The users of Javassist can define their own | |||
version of class loader but they can also use a class loader provided | |||
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> | |||
<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 | |||
each class loader creates its own name space. | |||
Different class loaders can load different class files with the | |||
same class name. The loaded two classes are regarded as different | |||
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, | |||
the JVM makes two distinct classes with the same name and definition. | |||
The two classes are regarded as different ones. | |||
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 | |||
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. | |||
Each class loader except the bootstrap loader has a | |||
@@ -555,7 +655,7 @@ be helpful: | |||
<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 | |||
<code>javassist.Loader</code>. This class loader uses a | |||
@@ -648,7 +748,7 @@ public class Main2 { | |||
<p>To run this program, do: | |||
<ul><pre> | |||
% java Main <i>arg1</i> <i>arg2</i>... | |||
% java Main2 <i>arg1</i> <i>arg2</i>... | |||
</pre></ul> | |||
<p>The class <code>MyApp</code> and the other application classes | |||
@@ -656,10 +756,10 @@ are translated by <code>MyTranslator</code>. | |||
<p>Note that <em>application</em> classes like <code>MyApp</code> cannot | |||
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 | |||
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 | |||
order from <code>java.lang.ClassLoader</code>. | |||
@@ -692,7 +792,7 @@ make sure whether all the classes using that class have been loaded by | |||
<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: | |||
@@ -763,48 +863,6 @@ Hence, the | |||
<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> | |||
<p>The system classes like <code>java.lang.String</code> cannot be |