]> source.dussan.org Git - javassist.git/commitdiff
Changed the implementation of CtClass#toClass() and fixed [Bugs item #993105] Reflect...
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Mon, 26 Jul 2004 08:55:35 +0000 (08:55 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Mon, 26 Jul 2004 08:55:35 +0000 (08:55 +0000)
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@116 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

Readme.html
src/main/javassist/CannotCompileException.java
src/main/javassist/ClassPool.java
src/main/javassist/CtClass.java
src/main/javassist/reflect/CannotReflectException.java [new file with mode: 0644]
src/main/javassist/reflect/Reflection.java
tutorial/tutorial.html

index 1d89d316f1871ce98d6a8e804376f59d18d261c9..b968d9d8f962a06e6d5f3755f531903f247c090d 100644 (file)
@@ -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>
index dfc5f79f08bc3dfcedecb0f779a41c5c0886dd07..0a575577530ef454d7ee7d34cde88b84a7915960 100644 (file)
@@ -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;
     }
index ce64c84710eb376d795994db5cd5435b8b8ba374..6ddcf31da3d25f09d8a2f6429cd8fbeba567c09d 100644 (file)
@@ -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.
index bf5a28ee6d7b0e2c11fa634ea2b72c54375ed0e8..0ef8491d0aa1987eaaa2f0316c466c2b0a5734df 100644 (file)
@@ -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);
diff --git a/src/main/javassist/reflect/CannotReflectException.java b/src/main/javassist/reflect/CannotReflectException.java
new file mode 100644 (file)
index 0000000..3147e08
--- /dev/null
@@ -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);
+    }
+}
index 1405d9b3eda00b0359616da952ec13fec9ca891a..9cb14425de051fbd828f3693d1bfcee3b9f103b1 100644 (file)
@@ -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);
     }
index 52a68e1c13eabd0b9fa96da9cb497a0bee110a98..6dc7ad1461d4779a793ef28ce977ad9da944d80a 100644 (file)
@@ -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