From: chiba Date: Wed, 11 Jan 2006 06:06:30 +0000 (+0000) Subject: restructred sub packages X-Git-Tag: rel_3_17_1_ga~394 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=52a455616e4077b5b61823cb4765c02fc6866f9a;p=javassist.git restructred sub packages git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@233 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- diff --git a/Readme.html b/Readme.html index 4699d715..9c88637d 100644 --- a/Readme.html +++ b/Readme.html @@ -137,7 +137,7 @@ set CLASSPATH=.;javassist.jar

Compare this result with that of the regular execution without reflection: @@ -151,7 +151,7 @@ set CLASSPATH=.;javassist.jar To do this, type the commands:

Then, @@ -292,14 +292,18 @@ see javassist.Dump.

  • The return type of CtClass.stopPruning() was changed from void to boolean.
  • toMethod() in javassist.CtConstructor has been implemented. -
  • javassist.preproc package was elminated and the source was - moved to the sample directory. - - -

    - version 3.1 - -

    - version 3.1 RC2 in September 7, 2005 diff --git a/build.xml b/build.xml index b7562364..1f78b28a 100644 --- a/build.xml +++ b/build.xml @@ -109,18 +109,18 @@ to ${build.classes.dir}. excludepackagenames="javassist.compiler.*,javassist.convert.*" sourcepath="src/main" defaultexcludes="yes" + locale="en_US" + charset="iso-8859-1" destdir="html" author="true" version="true" use="true" - Locale="en_US" - charset="iso-8859-1" - Public="true" + public="true" nohelp="true" windowtitle="Javassist API"> Javassist]]> Javassist, a Java-bytecode translator toolkit. -Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved.]]> +Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.]]> @@ -151,7 +151,8 @@ Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved.]]> - ** please run sample-rmi and sample-evolve separately ** + ** please run sample-rmi, sample-evolve, and + sample-hotswap (or -hotswap5) separately ** @@ -161,7 +162,7 @@ Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved.]]> - + @@ -212,6 +213,7 @@ Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved.]]> ** JAVA_HOME/lib/tools.jar must be included in CLASS_PATH + ** for JDK 1.4 @@ -221,6 +223,7 @@ Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved.]]> ** JAVA_HOME/lib/tools.jar must be included in CLASS_PATH + ** for JDK 1.5 or later diff --git a/sample/duplicate/DuplicatedObject.java b/sample/duplicate/DuplicatedObject.java index 5995abcc..7161493c 100644 --- a/sample/duplicate/DuplicatedObject.java +++ b/sample/duplicate/DuplicatedObject.java @@ -1,6 +1,6 @@ package sample.duplicate; -import javassist.reflect.*; +import javassist.tools.reflect.*; public class DuplicatedObject extends Metaobject { private DuplicatedObject backup; diff --git a/sample/duplicate/Main.java b/sample/duplicate/Main.java index e152a23e..064f13cd 100644 --- a/sample/duplicate/Main.java +++ b/sample/duplicate/Main.java @@ -3,7 +3,7 @@ package sample.duplicate; /* Runtime metaobject (JDK 1.2 or later only). - With the javassist.reflect package, the users can attach a metaobject + With the javassist.tools.reflect package, the users can attach a metaobject to an object. The metaobject can control the behavior of the object. For example, you can implement fault tolerancy with this ability. One of the implementation techniques of fault tolernacy is to make a copy @@ -30,15 +30,15 @@ package sample.duplicate; % java sample.duplicate.Main You would see two balls in a window. This is because - sample.duplicate.Viewer is loaded by javassist.reflect.Loader so that + sample.duplicate.Viewer is loaded by javassist.tools.reflect.Loader so that a metaobject would be attached. */ public class Main { public static void main(String[] args) throws Throwable { - javassist.reflect.Loader cl = new javassist.reflect.Loader(); + javassist.tools.reflect.Loader cl = new javassist.tools.reflect.Loader(); cl.makeReflective("sample.duplicate.Ball", "sample.duplicate.DuplicatedObject", - "javassist.reflect.ClassMetaobject"); + "javassist.tools.reflect.ClassMetaobject"); cl.run("sample.duplicate.Viewer", args); } } diff --git a/sample/evolve/DemoServer.java b/sample/evolve/DemoServer.java index dd64c550..b334bccb 100644 --- a/sample/evolve/DemoServer.java +++ b/sample/evolve/DemoServer.java @@ -1,6 +1,6 @@ package sample.evolve; -import javassist.web.*; +import javassist.tools.web.*; import java.io.*; /** diff --git a/sample/evolve/Evolution.java b/sample/evolve/Evolution.java index ecae0921..e804ff46 100644 --- a/sample/evolve/Evolution.java +++ b/sample/evolve/Evolution.java @@ -60,9 +60,9 @@ public class Evolution implements Translator { private void onLoadUpdatable(String classname) throws NotFoundException, CannotCompileException { // if the class is a concrete class, - // classname is $. + // classname is $$. - int i = classname.lastIndexOf('$'); + int i = classname.lastIndexOf("$$"); if (i <= 0) return; @@ -72,7 +72,7 @@ public class Evolution implements Translator { int version; try { - version = Integer.parseInt(classname.substring(i + 1)); + version = Integer.parseInt(classname.substring(i + 2)); } catch (NumberFormatException e) { throw new NotFoundException(classname, e); diff --git a/sample/evolve/VersionManager.java b/sample/evolve/VersionManager.java index 184397fd..efecee19 100644 --- a/sample/evolve/VersionManager.java +++ b/sample/evolve/VersionManager.java @@ -41,7 +41,7 @@ public class VersionManager { else version = ((Integer)found).intValue() + 1; - Class c = Class.forName(qualifiedClassname + '$' + version); + Class c = Class.forName(qualifiedClassname + "$$" + version); versionNo.put(qualifiedClassname, new Integer(version)); return c; } diff --git a/sample/hotswap/Test.java b/sample/hotswap/Test.java index e15afc9a..651d2184 100644 --- a/sample/hotswap/Test.java +++ b/sample/hotswap/Test.java @@ -1,5 +1,5 @@ import java.io.*; -import javassist.tools.HotSwapper; +import javassist.util.HotSwapper; public class Test { public static void main(String[] args) throws Exception { diff --git a/sample/reflect/Main.java b/sample/reflect/Main.java index d9733abc..972e8964 100644 --- a/sample/reflect/Main.java +++ b/sample/reflect/Main.java @@ -1,6 +1,6 @@ package sample.reflect; -import javassist.reflect.Loader; +import javassist.tools.reflect.Loader; /* The "verbose metaobject" example (JDK 1.2 or later only). @@ -14,7 +14,7 @@ import javassist.reflect.Loader; To run, - % java javassist.reflect.Loader sample.reflect.Main Joe + % java javassist.tools.reflect.Loader sample.reflect.Main Joe Compare this result with that of the regular execution without reflection: @@ -25,7 +25,7 @@ public class Main { Loader cl = (Loader)Main.class.getClassLoader(); cl.makeReflective("sample.reflect.Person", "sample.reflect.VerboseMetaobj", - "javassist.reflect.ClassMetaobject"); + "javassist.tools.reflect.ClassMetaobject"); cl.run("sample.reflect.Person", args); } diff --git a/sample/reflect/Person.java b/sample/reflect/Person.java index 445d3807..90ccb18f 100644 --- a/sample/reflect/Person.java +++ b/sample/reflect/Person.java @@ -4,8 +4,8 @@ package sample.reflect; -import javassist.reflect.Metalevel; -import javassist.reflect.Metaobject; +import javassist.tools.reflect.Metalevel; +import javassist.tools.reflect.Metaobject; public class Person { public String name; diff --git a/sample/reflect/VerboseMetaobj.java b/sample/reflect/VerboseMetaobj.java index 91dba579..cc47999c 100644 --- a/sample/reflect/VerboseMetaobj.java +++ b/sample/reflect/VerboseMetaobj.java @@ -1,6 +1,6 @@ package sample.reflect; -import javassist.reflect.*; +import javassist.tools.reflect.*; public class VerboseMetaobj extends Metaobject { public VerboseMetaobj(Object self, Object[] args) { diff --git a/sample/rmi/CountApplet.java b/sample/rmi/CountApplet.java index 0bebdaf9..e4ee0ee2 100644 --- a/sample/rmi/CountApplet.java +++ b/sample/rmi/CountApplet.java @@ -3,9 +3,9 @@ package sample.rmi; import java.applet.*; import java.awt.*; import java.awt.event.*; -import javassist.rmi.ObjectImporter; -import javassist.rmi.ObjectNotFoundException; -import javassist.web.Viewer; +import javassist.tools.rmi.ObjectImporter; +import javassist.tools.rmi.ObjectNotFoundException; +import javassist.tools.web.Viewer; public class CountApplet extends Applet implements ActionListener { private Font font; diff --git a/sample/rmi/Counter.java b/sample/rmi/Counter.java index f8a0fcf5..0920ca73 100644 --- a/sample/rmi/Counter.java +++ b/sample/rmi/Counter.java @@ -1,6 +1,6 @@ package sample.rmi; -import javassist.rmi.AppletServer; +import javassist.tools.rmi.AppletServer; import java.io.IOException; import javassist.CannotCompileException; import javassist.NotFoundException; diff --git a/sample/rmi/start.html b/sample/rmi/start.html index 696b629c..33321ad1 100644 --- a/sample/rmi/start.html +++ b/sample/rmi/start.html @@ -12,4 +12,4 @@ Start!

    If you don't want to use a web browser, do as follows: -

    + diff --git a/sample/rmi/webdemo.html b/sample/rmi/webdemo.html index d313ec3c..a2b595ce 100644 --- a/sample/rmi/webdemo.html +++ b/sample/rmi/webdemo.html @@ -6,7 +6,7 @@ local object. The applet can communicate through a socket with the host that executes the web server distributing that applet. However, the applet cannot directly call a method on an object if the object is -on a remote host. The javassist.rmi package provides +on a remote host. The javassist.tools.rmi package provides a mechanism for the applet to transparently access the remote object. The rules that the applet must be subject to are simpler than the standard Java RMI. @@ -36,7 +36,7 @@ Look at the lines shown with red:

    Figure 1: Applet

    -import javassist.rmi.ObjectImporter;
    +import javassist.tools.rmi.ObjectImporter;
     
     public class CountApplet extends Applet implements ActionListener {
       private Font font;
    @@ -106,7 +106,7 @@ public class Counter {
     }
     
    -

    Note that the javassist.rmi package does not require +

    Note that the javassist.tools.rmi package does not require the Counter class to be an interface unlike the Java RMI, with which Counter must be an interface and it must be implemented by another class. @@ -167,7 +167,7 @@ following features:

    With the Java RMI or Voyager, the applet programmer must define an interface for every remote object class and access the remote object through that interface. -On the other hand, the javassist.rmi package does not +On the other hand, the javassist.tools.rmi package does not require the programmer to follow that programming convention. It is suitable for writing simple distributed programs like applets. diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index 74ab0d3e..d4178e7f 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -703,7 +703,7 @@ public abstract class CtClass { * * @param name method name * @param desc method descriptor - * @see CtBehavior.getSignature() + * @see CtBehavior#getSignature() * @see javassist.bytecode.Descriptor */ public CtMethod getMethod(String name, String desc) diff --git a/src/main/javassist/CtMethod.java b/src/main/javassist/CtMethod.java index 043c8d3b..abb42b88 100644 --- a/src/main/javassist/CtMethod.java +++ b/src/main/javassist/CtMethod.java @@ -118,7 +118,7 @@ public final class CtMethod extends CtBehavior { * * @param src the source text. * @param declaring the class to which the created method is added. - * @see CtNewMethod.make(String, CtClass) + * @see CtNewMethod#make(String, CtClass) */ public static CtMethod make(String src, CtClass declaring) throws CannotCompileException diff --git a/src/main/javassist/bytecode/Bytecode.java b/src/main/javassist/bytecode/Bytecode.java index a1f74c6d..b2a0ee23 100644 --- a/src/main/javassist/bytecode/Bytecode.java +++ b/src/main/javassist/bytecode/Bytecode.java @@ -1339,7 +1339,7 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode { * Appends PUTSTATIC. * * @param classname the fully-qualified name of the target class. - * @param filedName the field name. + * @param fieldName the field name. * @param desc the descriptor of the field type. */ public void addPutstatic(String classname, String fieldName, String desc) { diff --git a/src/main/javassist/bytecode/Descriptor.java b/src/main/javassist/bytecode/Descriptor.java index ef194b7f..39577ce2 100644 --- a/src/main/javassist/bytecode/Descriptor.java +++ b/src/main/javassist/bytecode/Descriptor.java @@ -717,7 +717,6 @@ public class Descriptor { /** * Returns the first character of the current element. - * @return */ public char currentChar() { return desc.charAt(curPos); } diff --git a/src/main/javassist/bytecode/annotation/package.html b/src/main/javassist/bytecode/annotation/package.html index 6cf7e538..d0656db6 100644 --- a/src/main/javassist/bytecode/annotation/package.html +++ b/src/main/javassist/bytecode/annotation/package.html @@ -1,6 +1,6 @@ -Annotations API. +Bytecode-level Annotations API.

    This package provides low-level API for editing annotations attributes. diff --git a/src/main/javassist/expr/MethodCall.java b/src/main/javassist/expr/MethodCall.java index 7bc2c388..9fd29e2a 100644 --- a/src/main/javassist/expr/MethodCall.java +++ b/src/main/javassist/expr/MethodCall.java @@ -120,7 +120,7 @@ public class MethodCall extends Expr { * The method signature is represented by a character string * called method descriptor, which is defined in the JVM specification. * - * @see javassist.CtBehavior.getSignature() + * @see javassist.CtBehavior#getSignature() * @see javassist.bytecode.Descriptor * @since 3.1 */ diff --git a/src/main/javassist/reflect/CannotCreateException.java b/src/main/javassist/reflect/CannotCreateException.java deleted file mode 100644 index a0933830..00000000 --- a/src/main/javassist/reflect/CannotCreateException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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; - -/** - * Signals that ClassMetaobject.newInstance() fails. - */ -public class CannotCreateException extends Exception { - public CannotCreateException(String s) { - super(s); - } - - public CannotCreateException(Exception e) { - super("by " + e.toString()); - } -} diff --git a/src/main/javassist/reflect/CannotInvokeException.java b/src/main/javassist/reflect/CannotInvokeException.java deleted file mode 100644 index c778e89d..00000000 --- a/src/main/javassist/reflect/CannotInvokeException.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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 java.lang.reflect.InvocationTargetException; -import java.lang.IllegalAccessException; - -/** - * Thrown when method invocation using the reflection API has thrown - * an exception. - * - * @see javassist.reflect.Metaobject#trapMethodcall(int, Object[]) - * @see javassist.reflect.ClassMetaobject#trapMethodcall(int, Object[]) - * @see javassist.reflect.ClassMetaobject#invoke(Object, int, Object[]) - */ -public class CannotInvokeException extends RuntimeException { - - private Throwable err = null; - - /** - * Returns the cause of this exception. It may return null. - */ - public Throwable getReason() { return err; } - - /** - * Constructs a CannotInvokeException with an error message. - */ - public CannotInvokeException(String reason) { - super(reason); - } - - /** - * Constructs a CannotInvokeException with an InvocationTargetException. - */ - public CannotInvokeException(InvocationTargetException e) { - super("by " + e.getTargetException().toString()); - err = e.getTargetException(); - } - - /** - * Constructs a CannotInvokeException with an IllegalAccessException. - */ - public CannotInvokeException(IllegalAccessException e) { - super("by " + e.toString()); - err = e; - } - - /** - * Constructs a CannotInvokeException with an ClassNotFoundException. - */ - public CannotInvokeException(ClassNotFoundException e) { - super("by " + e.toString()); - err = e; - } -} diff --git a/src/main/javassist/reflect/CannotReflectException.java b/src/main/javassist/reflect/CannotReflectException.java deleted file mode 100644 index 036ebf49..00000000 --- a/src/main/javassist/reflect/CannotReflectException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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 makeReflective() in Reflection - * 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); - } -} diff --git a/src/main/javassist/reflect/ClassMetaobject.java b/src/main/javassist/reflect/ClassMetaobject.java deleted file mode 100644 index 61328452..00000000 --- a/src/main/javassist/reflect/ClassMetaobject.java +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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 java.lang.reflect.*; -import java.util.Arrays; -import java.io.Serializable; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -/** - * A runtime class metaobject. - * - *

    A ClassMetaobject is created for every - * class of reflective objects. It can be used to hold values - * shared among the reflective objects of the same class. - * - *

    To obtain a class metaobject, calls _getClass() - * on a reflective object. For example, - * - *

    - * - * @see javassist.reflect.Metaobject - * @see javassist.reflect.Metalevel - */ -public class ClassMetaobject implements Serializable { - /** - * The base-level methods controlled by a metaobject - * are renamed so that they begin with - * methodPrefix "_m_". - */ - static final String methodPrefix = "_m_"; - static final int methodPrefixLen = 3; - - private Class javaClass; - private Constructor[] constructors; - private Method[] methods; - - /** - * Specifies how a java.lang.Class object is loaded. - * - *

    If true, it is loaded by: - *

    - *

    If false, it is loaded by Class.forName(). - * The default value is false. - */ - public static boolean useContextClassLoader = false; - - /** - * Constructs a ClassMetaobject. - * - * @param params params[0] is the name of the class - * of the reflective objects. - */ - public ClassMetaobject(String[] params) - { - try { - javaClass = getClassObject(params[0]); - } - catch (ClassNotFoundException e) { - javaClass = null; - } - - constructors = javaClass.getConstructors(); - methods = null; - } - - private void writeObject(ObjectOutputStream out) throws IOException { - out.writeUTF(javaClass.getName()); - } - - private void readObject(ObjectInputStream in) - throws IOException, ClassNotFoundException - { - javaClass = getClassObject(in.readUTF()); - constructors = javaClass.getConstructors(); - methods = null; - } - - private Class getClassObject(String name) throws ClassNotFoundException { - if (useContextClassLoader) - return Thread.currentThread().getContextClassLoader() - .loadClass(name); - else - return Class.forName(name); - } - - /** - * Obtains the java.lang.Class representing this class. - */ - public final Class getJavaClass() { - return javaClass; - } - - /** - * Obtains the name of this class. - */ - public final String getName() { - return javaClass.getName(); - } - - /** - * Returns true if obj is an instance of this class. - */ - public final boolean isInstance(Object obj) { - return javaClass.isInstance(obj); - } - - /** - * Creates a new instance of the class. - * - * @param args the arguments passed to the constructor. - */ - public final Object newInstance(Object[] args) - throws CannotCreateException - { - int n = constructors.length; - for (int i = 0; i < n; ++i) { - try { - return constructors[i].newInstance(args); - } - catch (IllegalArgumentException e) { - // try again - } - catch (InstantiationException e) { - throw new CannotCreateException(e); - } - catch (IllegalAccessException e) { - throw new CannotCreateException(e); - } - catch (InvocationTargetException e) { - throw new CannotCreateException(e); - } - } - - throw new CannotCreateException("no constructor matches"); - } - - /** - * Is invoked when static fields of the base-level - * class are read and the runtime system intercepts it. - * This method simply returns the value of the field. - * - *

    Every subclass of this class should redefine this method. - */ - public Object trapFieldRead(String name) { - Class jc = getJavaClass(); - try { - return jc.getField(name).get(null); - } - catch (NoSuchFieldException e) { - throw new RuntimeException(e.toString()); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e.toString()); - } - } - - /** - * Is invoked when static fields of the base-level - * class are modified and the runtime system intercepts it. - * This method simply sets the field to the given value. - * - *

    Every subclass of this class should redefine this method. - */ - public void trapFieldWrite(String name, Object value) { - Class jc = getJavaClass(); - try { - jc.getField(name).set(null, value); - } - catch (NoSuchFieldException e) { - throw new RuntimeException(e.toString()); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e.toString()); - } - } - - /** - * Invokes a method whose name begins with - * methodPrefix "_m_" and the identifier. - * - * @exception CannotInvokeException if the invocation fails. - */ - static public Object invoke(Object target, int identifier, Object[] args) - throws Throwable - { - Method[] allmethods = target.getClass().getMethods(); - int n = allmethods.length; - String head = methodPrefix + identifier; - for (int i = 0; i < n; ++i) - if (allmethods[i].getName().startsWith(head)) { - try { - return allmethods[i].invoke(target, args); - } catch (java.lang.reflect.InvocationTargetException e) { - throw e.getTargetException(); - } catch (java.lang.IllegalAccessException e) { - throw new CannotInvokeException(e); - } - } - - throw new CannotInvokeException("cannot find a method"); - } - - /** - * Is invoked when static methods of the base-level - * class are called and the runtime system intercepts it. - * This method simply executes the intercepted method invocation - * with the original parameters and returns the resulting value. - * - *

    Every subclass of this class should redefine this method. - */ - public Object trapMethodcall(int identifier, Object[] args) - throws Throwable - { - try { - Method[] m = getReflectiveMethods(); - return m[identifier].invoke(null, args); - } - catch (java.lang.reflect.InvocationTargetException e) { - throw e.getTargetException(); - } - catch (java.lang.IllegalAccessException e) { - throw new CannotInvokeException(e); - } - } - - /** - * Returns an array of the methods defined on the given reflective - * object. This method is for the internal use only. - */ - public final Method[] getReflectiveMethods() { - if (methods != null) - return methods; - - Class baseclass = getJavaClass(); - Method[] allmethods = baseclass.getDeclaredMethods(); - int n = allmethods.length; - int[] index = new int[n]; - int max = 0; - for (int i = 0; i < n; ++i) { - Method m = allmethods[i]; - String mname = m.getName(); - if (mname.startsWith(methodPrefix)) { - int k = 0; - for (int j = methodPrefixLen;; ++j) { - char c = mname.charAt(j); - if ('0' <= c && c <= '9') - k = k * 10 + c - '0'; - else - break; - } - - index[i] = ++k; - if (k > max) - max = k; - } - } - - methods = new Method[max]; - for (int i = 0; i < n; ++i) - if (index[i] > 0) - methods[index[i] - 1] = allmethods[i]; - - return methods; - } - - /** - * Returns the java.lang.reflect.Method object representing - * the method specified by identifier. - * - *

    Note that the actual method returned will be have an altered, - * reflective name i.e. _m_2_... - * - * @param identifier the identifier index - * given to trapMethodcall() etc. - * @see Metaobject#trapMethodcall(int,Object[]) - * @see #trapMethodcall(int,Object[]) - */ - public final Method getMethod(int identifier) { - return getReflectiveMethods()[identifier]; - } - - /** - * Returns the name of the method specified - * by identifier. - */ - public final String getMethodName(int identifier) { - String mname = getReflectiveMethods()[identifier].getName(); - int j = ClassMetaobject.methodPrefixLen; - for (;;) { - char c = mname.charAt(j++); - if (c < '0' || '9' < c) - break; - } - - return mname.substring(j); - } - - /** - * Returns an array of Class objects representing the - * formal parameter types of the method specified - * by identifier. - */ - public final Class[] getParameterTypes(int identifier) { - return getReflectiveMethods()[identifier].getParameterTypes(); - } - - /** - * Returns a Class objects representing the - * return type of the method specified by identifier. - */ - public final Class getReturnType(int identifier) { - return getReflectiveMethods()[identifier].getReturnType(); - } - - /** - * Returns the identifier index of the method, as identified by its - * original name. - * - *

    This method is useful, in conjuction with - * ClassMetaobject#getMethod(), to obtain a quick reference - * to the original method in the reflected class (i.e. not the proxy - * method), using the original name of the method. - * - *

    Written by Brett Randall and Shigeru Chiba. - * - * @param originalName The original name of the reflected method - * @param argTypes array of Class specifying the method signature - * @return the identifier index of the original method - * @throws NoSuchMethodException if the method does not exist - * - * @see ClassMetaobject#getMethod(int) - */ - public final int getMethodIndex(String originalName, Class[] argTypes) - throws NoSuchMethodException - { - Method[] mthds = getReflectiveMethods(); - for (int i = 0; i < mthds.length; i++) { - if (mthds[i] == null) - continue; - - // check name and parameter types match - if (getMethodName(i).equals(originalName) - && Arrays.equals(argTypes, mthds[i].getParameterTypes())) - return i; - } - - throw new NoSuchMethodException("Method " + originalName - + " not found"); - } -} diff --git a/src/main/javassist/reflect/Compiler.java b/src/main/javassist/reflect/Compiler.java deleted file mode 100644 index 734f598b..00000000 --- a/src/main/javassist/reflect/Compiler.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.CtClass; -import javassist.ClassPool; -import java.io.PrintStream; - -class CompiledClass { - public String classname; - public String metaobject; - public String classobject; -} - -/** - * A bytecode translator for reflection. - * - *

    This translator directly modifies class files on a local disk so that - * the classes represented by those class files are reflective. - * After the modification, the class files can be run with the standard JVM - * without javassist.reflect.Loader - * or any other user-defined class loader. - * - *

    The modified class files are given as the command-line parameters, - * which are a sequence of fully-qualified class names followed by options: - * - *

    -m classname : specifies the class of the - * metaobjects associated with instances of the class followed by - * this option. The default is javassit.reflect.Metaobject. - * - *

    -c classname : specifies the class of the - * class metaobjects associated with instances of the class followed by - * this option. The default is javassit.reflect.ClassMetaobject. - * - *

    If a class name is not followed by any options, the class indicated - * by that class name is not reflective. - * - *

    For example, - *

    - * - *

    modifies class files Dog.class, Cat.class, - * and Cow.class. - * The metaobject of a Dog object is a MetaDog object and the class - * metaobject is a CMetaDog object. - * The metaobject of a Cat object is a MetaCat object but - * the class metaobject is a default one. - * Cow objects are not reflective. - * - *

    Note that if the super class is also made reflective, it must be done - * before the sub class. - * - * @see javassist.reflect.Metaobject - * @see javassist.reflect.ClassMetaobject - * @see javassist.reflect.Reflection - */ -public class Compiler { - - public static void main(String[] args) throws Exception { - if (args.length == 0) { - help(System.err); - return; - } - - CompiledClass[] entries = new CompiledClass[args.length]; - int n = parse(args, entries); - - if (n < 1) { - System.err.println("bad parameter."); - return; - } - - processClasses(entries, n); - } - - private static void processClasses(CompiledClass[] entries, int n) - throws Exception - { - Reflection implementor = new Reflection(); - ClassPool pool = ClassPool.getDefault(); - implementor.start(pool); - - for (int i = 0; i < n; ++i) { - CtClass c = pool.get(entries[i].classname); - if (entries[i].metaobject != null - || entries[i].classobject != null) { - String metaobj, classobj; - - if (entries[i].metaobject == null) - metaobj = "javassist.reflect.Metaobject"; - else - metaobj = entries[i].metaobject; - - if (entries[i].classobject == null) - classobj = "javassist.reflect.ClassMetaobject"; - else - classobj = entries[i].classobject; - - if (!implementor.makeReflective(c, pool.get(metaobj), - pool.get(classobj))) - System.err.println("Warning: " + c.getName() - + " is reflective. It was not changed."); - - System.err.println(c.getName() + ": " + metaobj + ", " - + classobj); - } - else - System.err.println(c.getName() + ": not reflective"); - } - - for (int i = 0; i < n; ++i) { - implementor.onLoad(pool, entries[i].classname); - pool.get(entries[i].classname).writeFile(); - } - } - - private static int parse(String[] args, CompiledClass[] result) { - int n = -1; - for (int i = 0; i < args.length; ++i) { - String a = args[i]; - if (a.equals("-m")) - if (n < 0 || i + 1 > args.length) - return -1; - else - result[n].metaobject = args[++i]; - else if (a.equals("-c")) - if (n < 0 || i + 1 > args.length) - return -1; - else - result[n].classobject = args[++i]; - else if (a.charAt(0) == '-') - return -1; - else { - CompiledClass cc = new CompiledClass(); - cc.classname = a; - cc.metaobject = null; - cc.classobject = null; - result[++n] = cc; - } - } - - return n + 1; - } - - private static void help(PrintStream out) { - out.println("Usage: java javassist.reflect.Compiler"); - out.println(" ( [-m ] [-c ])+"); - } -} diff --git a/src/main/javassist/reflect/Loader.java b/src/main/javassist/reflect/Loader.java deleted file mode 100644 index 76dce4d6..00000000 --- a/src/main/javassist/reflect/Loader.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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; -import javassist.NotFoundException; -import javassist.ClassPool; - -/** - * A class loader for reflection. - * - *

    To run a program, say MyApp, - * including a reflective class, - * you must write a start-up program as follows: - * - *

    - * - *

    Then run this program as follows: - * - *

    - * - *

    This command runs Main.main() with arg1, ... - * and Main.main() runs MyApp.main() with - * arg1, ... - * The Person class is modified - * to be a reflective class. Method calls on a Person - * object are intercepted by an instance of MyMetaobject. - * - *

    Also, you can run MyApp in a slightly different way: - * - *

    - * - *

    This program is run as follows: - * - *

    - * - *

    The difference from the former one is that the class Main - * is loaded by javassist.reflect.Loader whereas the class - * Main2 is not. Thus, Main belongs - * to the same name space (security domain) as MyApp - * whereas Main2 does not; Main2 belongs - * to the same name space as javassist.reflect.Loader. - * For more details, - * see the notes in the manual page of javassist.Loader. - * - *

    The class Main2 is equivalent to this class: - * - *

    - * - *

    Note: - * - *

    javassist.reflect.Loader does not make a class reflective - * if that class is in a java.* or - * javax.* pacakge because of the specifications - * on the class loading algorithm of Java. The JVM does not allow to - * load such a system class with a user class loader. - * - *

    To avoid this limitation, those classes should be statically - * modified with javassist.reflect.Compiler and the original - * class files should be replaced. - * - * @see javassist.reflect.Reflection - * @see javassist.reflect.Compiler - * @see javassist.Loader - */ -public class Loader extends javassist.Loader { - protected Reflection reflection; - - /** - * Loads a class with an instance of Loader - * and calls main() in that class. - * - * @param args command line parameters. - *

    - */ - public static void main(String[] args) throws Throwable { - Loader cl = new Loader(); - cl.run(args); - } - - /** - * Constructs a new class loader. - */ - public Loader() throws CannotCompileException, NotFoundException { - super(); - delegateLoadingOf("javassist.reflect.Loader"); - - reflection = new Reflection(); - ClassPool pool = ClassPool.getDefault(); - addTranslator(pool, reflection); - } - - /** - * Produces a reflective class. - * If the super class is also made reflective, it must be done - * before the sub class. - * - * @param clazz the reflective class. - * @param metaobject the class of metaobjects. - * It must be a subclass of - * Metaobject. - * @param metaclass the class of the class metaobject. - * It must be a subclass of - * ClassMetaobject. - * @return false if the class is already reflective. - * - * @see javassist.reflect.Metaobject - * @see javassist.reflect.ClassMetaobject - */ - public boolean makeReflective(String clazz, - String metaobject, String metaclass) - throws CannotCompileException, NotFoundException - { - return reflection.makeReflective(clazz, metaobject, metaclass); - } -} diff --git a/src/main/javassist/reflect/Metalevel.java b/src/main/javassist/reflect/Metalevel.java deleted file mode 100644 index 8192267a..00000000 --- a/src/main/javassist/reflect/Metalevel.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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; - -/** - * An interface to access a metaobject and a class metaobject. - * This interface is implicitly implemented by the reflective - * class. - */ -public interface Metalevel { - /** - * Obtains the class metaobject associated with this object. - */ - ClassMetaobject _getClass(); - - /** - * Obtains the metaobject associated with this object. - */ - Metaobject _getMetaobject(); - - /** - * Changes the metaobject associated with this object. - */ - void _setMetaobject(Metaobject m); -} diff --git a/src/main/javassist/reflect/Metaobject.java b/src/main/javassist/reflect/Metaobject.java deleted file mode 100644 index 8c3749aa..00000000 --- a/src/main/javassist/reflect/Metaobject.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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 java.lang.reflect.Method; -import java.io.Serializable; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -/** - * A runtime metaobject. - * - *

    A Metaobject is created for - * every object at the base level. A different reflective object is - * associated with a different metaobject. - * - *

    The metaobject intercepts method calls - * on the reflective object at the base-level. To change the behavior - * of the method calls, a subclass of Metaobject - * should be defined. - * - *

    To obtain a metaobject, calls _getMetaobject() - * on a reflective object. For example, - * - *

    - * - * @see javassist.reflect.ClassMetaobject - * @see javassist.reflect.Metalevel - */ -public class Metaobject implements Serializable { - protected ClassMetaobject classmetaobject; - protected Metalevel baseobject; - protected Method[] methods; - - /** - * Constructs a Metaobject. The metaobject is - * constructed before the constructor is called on the base-level - * object. - * - * @param self the object that this metaobject is associated with. - * @param args the parameters passed to the constructor of - * self. - */ - public Metaobject(Object self, Object[] args) { - baseobject = (Metalevel)self; - classmetaobject = baseobject._getClass(); - methods = classmetaobject.getReflectiveMethods(); - } - - /** - * Constructs a Metaobject without initialization. - * If calling this constructor, a subclass should be responsible - * for initialization. - */ - protected Metaobject() { - baseobject = null; - classmetaobject = null; - methods = null; - } - - private void writeObject(ObjectOutputStream out) throws IOException { - out.writeObject(baseobject); - } - - private void readObject(ObjectInputStream in) - throws IOException, ClassNotFoundException - { - baseobject = (Metalevel)in.readObject(); - classmetaobject = baseobject._getClass(); - methods = classmetaobject.getReflectiveMethods(); - } - - /** - * Obtains the class metaobject associated with this metaobject. - * - * @see javassist.reflect.ClassMetaobject - */ - public final ClassMetaobject getClassMetaobject() { - return classmetaobject; - } - - /** - * Obtains the object controlled by this metaobject. - */ - public final Object getObject() { - return baseobject; - } - - /** - * Changes the object controlled by this metaobject. - * - * @param self the object - */ - public final void setObject(Object self) { - baseobject = (Metalevel)self; - classmetaobject = baseobject._getClass(); - methods = classmetaobject.getReflectiveMethods(); - - // call _setMetaobject() after the metaobject is settled. - baseobject._setMetaobject(this); - } - - /** - * Returns the name of the method specified - * by identifier. - */ - public final String getMethodName(int identifier) { - String mname = methods[identifier].getName(); - int j = ClassMetaobject.methodPrefixLen; - for (;;) { - char c = mname.charAt(j++); - if (c < '0' || '9' < c) - break; - } - - return mname.substring(j); - } - - /** - * Returns an array of Class objects representing the - * formal parameter types of the method specified - * by identifier. - */ - public final Class[] getParameterTypes(int identifier) { - return methods[identifier].getParameterTypes(); - } - - /** - * Returns a Class objects representing the - * return type of the method specified by identifier. - */ - public final Class getReturnType(int identifier) { - return methods[identifier].getReturnType(); - } - - /** - * Is invoked when public fields of the base-level - * class are read and the runtime system intercepts it. - * This method simply returns the value of the field. - * - *

    Every subclass of this class should redefine this method. - */ - public Object trapFieldRead(String name) { - Class jc = getClassMetaobject().getJavaClass(); - try { - return jc.getField(name).get(getObject()); - } - catch (NoSuchFieldException e) { - throw new RuntimeException(e.toString()); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e.toString()); - } - } - - /** - * Is invoked when public fields of the base-level - * class are modified and the runtime system intercepts it. - * This method simply sets the field to the given value. - * - *

    Every subclass of this class should redefine this method. - */ - public void trapFieldWrite(String name, Object value) { - Class jc = getClassMetaobject().getJavaClass(); - try { - jc.getField(name).set(getObject(), value); - } - catch (NoSuchFieldException e) { - throw new RuntimeException(e.toString()); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e.toString()); - } - } - - /** - * Is invoked when base-level method invocation is intercepted. - * This method simply executes the intercepted method invocation - * with the original parameters and returns the resulting value. - * - *

    Every subclass of this class should redefine this method. - * - *

    Note: this method is not invoked if the base-level method - * is invoked by a constructor in the super class. For example, - * - *

    - * - *

    if an instance of B is created, - * the invocation of initialize() in B is intercepted only once. - * The first invocation by the constructor in A is not intercepted. - * This is because the link between a base-level object and a - * metaobject is not created until the execution of a - * constructor of the super class finishes. - */ - public Object trapMethodcall(int identifier, Object[] args) - throws Throwable - { - try { - return methods[identifier].invoke(getObject(), args); - } - catch (java.lang.reflect.InvocationTargetException e) { - throw e.getTargetException(); - } - catch (java.lang.IllegalAccessException e) { - throw new CannotInvokeException(e); - } - } -} diff --git a/src/main/javassist/reflect/Reflection.java b/src/main/javassist/reflect/Reflection.java deleted file mode 100644 index cd4f441c..00000000 --- a/src/main/javassist/reflect/Reflection.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.*; -import javassist.CtMethod.ConstParameter; - -/** - * The class implementing the behavioral reflection mechanism. - * - *

    If a class is reflective, - * then all the method invocations on every - * instance of that class are intercepted by the runtime - * metaobject controlling that instance. The methods inherited from the - * super classes are also intercepted except final methods. To intercept - * a final method in a super class, that super class must be also reflective. - * - *

    To do this, the original class file representing a reflective class: - * - *

    - * - *

    is modified so that it represents a class: - * - *

    - * - * @see javassist.reflect.ClassMetaobject - * @see javassist.reflect.Metaobject - * @see javassist.reflect.Loader - * @see javassist.reflect.Compiler - */ -public class Reflection implements Translator { - - static final String classobjectField = "_classobject"; - static final String classobjectAccessor = "_getClass"; - static final String metaobjectField = "_metaobject"; - static final String metaobjectGetter = "_getMetaobject"; - static final String metaobjectSetter = "_setMetaobject"; - 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; - - protected ClassPool classPool; - protected CodeConverter converter; - - private boolean isExcluded(String name) { - return name.startsWith(ClassMetaobject.methodPrefix) - || name.equals(classobjectAccessor) - || name.equals(metaobjectSetter) - || name.equals(metaobjectGetter) - || name.startsWith(readPrefix) - || name.startsWith(writePrefix); - } - - /** - * Constructs a new Reflection object. - */ - public Reflection() { - classPool = null; - converter = new CodeConverter(); - } - - /** - * Initializes the object. - */ - public void start(ClassPool pool) throws NotFoundException { - classPool = pool; - final String msg - = "javassist.reflect.Sample is not found or broken."; - try { - CtClass c = classPool.get("javassist.reflect.Sample"); - trapMethod = c.getDeclaredMethod("trap"); - trapStaticMethod = c.getDeclaredMethod("trapStatic"); - trapRead = c.getDeclaredMethod("trapRead"); - trapWrite = c.getDeclaredMethod("trapWrite"); - readParam - = new CtClass[] { classPool.get("java.lang.Object") }; - } - catch (NotFoundException e) { - throw new RuntimeException(msg); - } - } - - /** - * Inserts hooks for intercepting accesses to the fields declared - * in reflective classes. - */ - public void onLoad(ClassPool pool, String classname) - throws CannotCompileException, NotFoundException - { - CtClass clazz = pool.get(classname); - clazz.instrument(converter); - } - - /** - * Produces a reflective class. - * If the super class is also made reflective, it must be done - * before the sub class. - * - * @param classname the name of the reflective class - * @param metaobject the class name of metaobjects. - * @param metaclass the class name of the class metaobject. - * @return false if the class is already reflective. - * - * @see javassist.reflect.Metaobject - * @see javassist.reflect.ClassMetaobject - */ - public boolean makeReflective(String classname, - String metaobject, String metaclass) - throws CannotCompileException, NotFoundException - { - return makeReflective(classPool.get(classname), - classPool.get(metaobject), - classPool.get(metaclass)); - } - - /** - * Produces a reflective class. - * If the super class is also made reflective, it must be done - * before the sub class. - * - * @param clazz the reflective class. - * @param metaobject the class of metaobjects. - * It must be a subclass of - * Metaobject. - * @param metaclass the class of the class metaobject. - * It must be a subclass of - * ClassMetaobject. - * @return false if the class is already reflective. - * - * @see javassist.reflect.Metaobject - * @see javassist.reflect.ClassMetaobject - */ - public boolean makeReflective(Class clazz, - Class metaobject, Class metaclass) - throws CannotCompileException, NotFoundException - { - return makeReflective(clazz.getName(), metaobject.getName(), - metaclass.getName()); - } - - /** - * Produces a reflective class. It modifies the given - * CtClass object and makes it reflective. - * If the super class is also made reflective, it must be done - * before the sub class. - * - * @param clazz the reflective class. - * @param metaobject the class of metaobjects. - * It must be a subclass of - * Metaobject. - * @param metaclass the class of the class metaobject. - * It must be a subclass of - * ClassMetaobject. - * @return false if the class is already reflective. - * - * @see javassist.reflect.Metaobject - * @see javassist.reflect.ClassMetaobject - */ - public boolean makeReflective(CtClass clazz, - CtClass metaobject, CtClass metaclass) - 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); - } - - /** - * Registers a reflective class. The field accesses to the instances - * of this class are instrumented. - */ - private void registerReflectiveClass(CtClass clazz) { - CtField[] fs = clazz.getDeclaredFields(); - for (int i = 0; i < fs.length; ++i) { - CtField f = fs[i]; - int mod = f.getModifiers(); - if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) { - String name = f.getName(); - converter.replaceFieldRead(f, clazz, readPrefix + name); - converter.replaceFieldWrite(f, clazz, writePrefix + name); - } - } - } - - private boolean modifyClassfile(CtClass clazz, CtClass metaobject, - CtClass metaclass) - throws CannotCompileException, NotFoundException - { - if (clazz.getAttribute("Reflective") != null) - return false; // this is already reflective. - else - clazz.setAttribute("Reflective", new byte[0]); - - CtClass mlevel = classPool.get("javassist.reflect.Metalevel"); - boolean addMeta = !clazz.subtypeOf(mlevel); - if (addMeta) - clazz.addInterface(mlevel); - - processMethods(clazz, addMeta); - processFields(clazz); - - CtField f; - if (addMeta) { - f = new CtField(classPool.get("javassist.reflect.Metaobject"), - metaobjectField, clazz); - f.setModifiers(Modifier.PROTECTED); - clazz.addField(f, CtField.Initializer.byNewWithParams(metaobject)); - - clazz.addMethod(CtNewMethod.getter(metaobjectGetter, f)); - clazz.addMethod(CtNewMethod.setter(metaobjectSetter, f)); - } - - f = new CtField(classPool.get("javassist.reflect.ClassMetaobject"), - classobjectField, clazz); - f.setModifiers(Modifier.PRIVATE | Modifier.STATIC); - clazz.addField(f, CtField.Initializer.byNew(metaclass, - new String[] { clazz.getName() })); - - clazz.addMethod(CtNewMethod.getter(classobjectAccessor, f)); - return true; - } - - private void processMethods(CtClass clazz, boolean dontSearch) - throws CannotCompileException, NotFoundException - { - CtMethod[] ms = clazz.getMethods(); - for (int i = 0; i < ms.length; ++i) { - CtMethod m = ms[i]; - int mod = m.getModifiers(); - if (Modifier.isPublic(mod) && !Modifier.isAbstract(mod)) - processMethods0(mod, clazz, m, i, dontSearch); - } - } - - private void processMethods0(int mod, CtClass clazz, - CtMethod m, int identifier, boolean dontSearch) - throws CannotCompileException, NotFoundException - { - CtMethod body; - String name = m.getName(); - - if (isExcluded(name)) // internally-used method inherited - return; // from a reflective class. - - CtMethod m2; - if (m.getDeclaringClass() == clazz) { - if (Modifier.isNative(mod)) - return; - - m2 = m; - if (Modifier.isFinal(mod)) { - mod &= ~Modifier.FINAL; - m2.setModifiers(mod); - } - } - else { - if (Modifier.isFinal(mod)) - return; - - mod &= ~Modifier.NATIVE; - m2 = CtNewMethod.delegator(findOriginal(m, dontSearch), clazz); - m2.setModifiers(mod); - clazz.addMethod(m2); - } - - m2.setName(ClassMetaobject.methodPrefix + identifier - + "_" + name); - - if (Modifier.isStatic(mod)) - body = trapStaticMethod; - else - body = trapMethod; - - CtMethod wmethod - = CtNewMethod.wrapped(m.getReturnType(), name, - m.getParameterTypes(), m.getExceptionTypes(), - body, ConstParameter.integer(identifier), - clazz); - wmethod.setModifiers(mod); - clazz.addMethod(wmethod); - } - - private CtMethod findOriginal(CtMethod m, boolean dontSearch) - throws NotFoundException - { - if (dontSearch) - return m; - - String name = m.getName(); - CtMethod[] ms = m.getDeclaringClass().getDeclaredMethods(); - for (int i = 0; i < ms.length; ++i) { - String orgName = ms[i].getName(); - if (orgName.endsWith(name) - && orgName.startsWith(ClassMetaobject.methodPrefix) - && ms[i].getSignature().equals(m.getSignature())) - return ms[i]; - } - - return m; - } - - private void processFields(CtClass clazz) - throws CannotCompileException, NotFoundException - { - CtField[] fs = clazz.getDeclaredFields(); - for (int i = 0; i < fs.length; ++i) { - CtField f = fs[i]; - int mod = f.getModifiers(); - if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) { - mod |= Modifier.STATIC; - String name = f.getName(); - CtClass ftype = f.getType(); - CtMethod wmethod - = CtNewMethod.wrapped(ftype, readPrefix + name, - readParam, null, trapRead, - ConstParameter.string(name), - clazz); - wmethod.setModifiers(mod); - clazz.addMethod(wmethod); - CtClass[] writeParam = new CtClass[2]; - writeParam[0] = classPool.get("java.lang.Object"); - writeParam[1] = ftype; - wmethod = CtNewMethod.wrapped(CtClass.voidType, - writePrefix + name, - writeParam, null, trapWrite, - ConstParameter.string(name), clazz); - wmethod.setModifiers(mod); - clazz.addMethod(wmethod); - } - } - } -} diff --git a/src/main/javassist/reflect/Sample.java b/src/main/javassist/reflect/Sample.java deleted file mode 100644 index 81bad691..00000000 --- a/src/main/javassist/reflect/Sample.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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; - -/** - * A template used for defining a reflective class. - */ -public class Sample { - private Metaobject _metaobject; - private static ClassMetaobject _classobject; - - public Object trap(Object[] args, int identifier) throws Throwable { - Metaobject mobj; - mobj = _metaobject; - if (mobj == null) - return ClassMetaobject.invoke(this, identifier, args); - else - return mobj.trapMethodcall(identifier, args); - } - - public static Object trapStatic(Object[] args, int identifier) - throws Throwable - { - return _classobject.trapMethodcall(identifier, args); - } - - public static Object trapRead(Object[] args, String name) { - if (args[0] == null) - return _classobject.trapFieldRead(name); - else - return ((Metalevel)args[0])._getMetaobject().trapFieldRead(name); - } - - public static Object trapWrite(Object[] args, String name) { - Metalevel base = (Metalevel)args[0]; - if (base == null) - _classobject.trapFieldWrite(name, args[1]); - else - base._getMetaobject().trapFieldWrite(name, args[1]); - - return null; - } -} diff --git a/src/main/javassist/reflect/package.html b/src/main/javassist/reflect/package.html deleted file mode 100644 index 10a41964..00000000 --- a/src/main/javassist/reflect/package.html +++ /dev/null @@ -1,35 +0,0 @@ - - -Runtime Behavioral Reflection. - -

    (also recently known as interceptors or AOP?) - -

    This package enables a metaobject to trap method calls and field -accesses on a regular Java object. It provides a class -Reflection, which is a main module for implementing -runtime behavioral reflection. -It also provides -a class Loader and Compiler -as utilities for dynamically or statically -translating a regular class into a reflective class. - -

    An instance of the reflective class is associated with -a runtime metaobject and a runtime class metaobject, which control -the behavior of that instance. -The runtime -metaobject is created for every (base-level) instance but the -runtime class metaobject is created for every (base-level) class. -Metaobject is the root class of the runtime -metaobject and ClassMetaobject is the root class -of the runtime class metaobject. - -

    This package is provided as a sample implementation of the -reflection mechanism with Javassist. All the programs in this package -uses only the regular Javassist API; they never call any hidden -methods. - -

    The most significant class in this package is Reflection. -See the description of this class first. - - - diff --git a/src/main/javassist/rmi/AppletServer.java b/src/main/javassist/rmi/AppletServer.java deleted file mode 100644 index 89cc92ab..00000000 --- a/src/main/javassist/rmi/AppletServer.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.rmi; - -import java.io.*; -import javassist.web.*; -import javassist.CannotCompileException; -import javassist.NotFoundException; -import javassist.ClassPool; -import java.lang.reflect.Method; -import java.util.Hashtable; -import java.util.Vector; - -/** - * An AppletServer object is a web server that an ObjectImporter - * communicates with. It makes the objects specified by - * exportObject() remotely accessible from applets. - * If the classes of the exported objects are requested by the client-side - * JVM, this web server sends proxy classes for the requested classes. - * - * @see javassist.rmi.ObjectImporter - */ -public class AppletServer extends Webserver { - private StubGenerator stubGen; - private Hashtable exportedNames; - private Vector exportedObjects; - - private static final byte[] okHeader - = "HTTP/1.0 200 OK\r\n\r\n".getBytes(); - - /** - * Constructs a web server. - * - * @param port port number - */ - public AppletServer(String port) - throws IOException, NotFoundException, CannotCompileException - { - this(Integer.parseInt(port)); - } - - /** - * Constructs a web server. - * - * @param port port number - */ - public AppletServer(int port) - throws IOException, NotFoundException, CannotCompileException - { - this(ClassPool.getDefault(), new StubGenerator(), port); - } - - /** - * Constructs a web server. - * - * @param port port number - * @param src the source of classs files. - */ - public AppletServer(int port, ClassPool src) - throws IOException, NotFoundException, CannotCompileException - { - this(new ClassPool(src), new StubGenerator(), port); - } - - private AppletServer(ClassPool loader, StubGenerator gen, int port) - throws IOException, NotFoundException, CannotCompileException - { - super(port); - exportedNames = new Hashtable(); - exportedObjects = new Vector(); - stubGen = gen; - addTranslator(loader, gen); - } - - /** - * Begins the HTTP service. - */ - public void run() { - super.run(); - } - - /** - * Exports an object. - * This method produces the bytecode of the proxy class used - * to access the exported object. A remote applet can load - * the proxy class and call a method on the exported object. - * - * @param name the name used for looking the object up. - * @param obj the exported object. - * @return the object identifier - * - * @see javassist.rmi.ObjectImporter#lookupObject(String) - */ - public synchronized int exportObject(String name, Object obj) - throws CannotCompileException - { - Class clazz = obj.getClass(); - ExportedObject eo = new ExportedObject(); - eo.object = obj; - eo.methods = clazz.getMethods(); - exportedObjects.addElement(eo); - eo.identifier = exportedObjects.size() - 1; - if (name != null) - exportedNames.put(name, eo); - - try { - stubGen.makeProxyClass(clazz); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - - return eo.identifier; - } - - /** - * Processes a request from a web browser (an ObjectImporter). - */ - public void doReply(InputStream in, OutputStream out, String cmd) - throws IOException, BadHttpRequest - { - if (cmd.startsWith("POST /rmi ")) - processRMI(in, out); - else if (cmd.startsWith("POST /lookup ")) - lookupName(cmd, in, out); - else - super.doReply(in, out, cmd); - } - - private void processRMI(InputStream ins, OutputStream outs) - throws IOException - { - ObjectInputStream in = new ObjectInputStream(ins); - - int objectId = in.readInt(); - int methodId = in.readInt(); - Exception err = null; - Object rvalue = null; - try { - ExportedObject eo - = (ExportedObject)exportedObjects.elementAt(objectId); - Object[] args = readParameters(in); - rvalue = convertRvalue(eo.methods[methodId].invoke(eo.object, - args)); - } - catch(Exception e) { - err = e; - logging2(e.toString()); - } - - outs.write(okHeader); - ObjectOutputStream out = new ObjectOutputStream(outs); - if (err != null) { - out.writeBoolean(false); - out.writeUTF(err.toString()); - } - else - try { - out.writeBoolean(true); - out.writeObject(rvalue); - } - catch (NotSerializableException e) { - logging2(e.toString()); - } - catch (InvalidClassException e) { - logging2(e.toString()); - } - - out.flush(); - out.close(); - in.close(); - } - - private Object[] readParameters(ObjectInputStream in) - throws IOException, ClassNotFoundException - { - int n = in.readInt(); - Object[] args = new Object[n]; - for (int i = 0; i < n; ++i) { - Object a = in.readObject(); - if (a instanceof RemoteRef) { - RemoteRef ref = (RemoteRef)a; - ExportedObject eo - = (ExportedObject)exportedObjects.elementAt(ref.oid); - a = eo.object; - } - - args[i] = a; - } - - return args; - } - - private Object convertRvalue(Object rvalue) - throws CannotCompileException - { - if (rvalue == null) - return null; // the return type is void. - - String classname = rvalue.getClass().getName(); - if (stubGen.isProxyClass(classname)) - return new RemoteRef(exportObject(null, rvalue), classname); - else - return rvalue; - } - - private void lookupName(String cmd, InputStream ins, OutputStream outs) - throws IOException - { - ObjectInputStream in = new ObjectInputStream(ins); - String name = DataInputStream.readUTF(in); - ExportedObject found = (ExportedObject)exportedNames.get(name); - outs.write(okHeader); - ObjectOutputStream out = new ObjectOutputStream(outs); - if (found == null) { - logging2(name + "not found."); - out.writeInt(-1); // error code - out.writeUTF("error"); - } - else { - logging2(name); - out.writeInt(found.identifier); - out.writeUTF(found.object.getClass().getName()); - } - - out.flush(); - out.close(); - in.close(); - } -} - -class ExportedObject { - public int identifier; - public Object object; - public Method[] methods; -} diff --git a/src/main/javassist/rmi/ObjectImporter.java b/src/main/javassist/rmi/ObjectImporter.java deleted file mode 100644 index adad468d..00000000 --- a/src/main/javassist/rmi/ObjectImporter.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.rmi; - -import java.io.*; -import java.net.*; -import java.applet.Applet; -import java.lang.reflect.*; - -/** - * The object importer enables applets to call a method on a remote - * object running on the Webserver. - * - *

    To access the remote - * object, the applet first calls lookupObject() and - * obtains a proxy object, which is a reference to that object. - * The class name of the proxy object is identical to that of - * the remote object. - * The proxy object provides the same set of methods as the remote object. - * If one of the methods is invoked on the proxy object, - * the invocation is delegated to the remote object. - * From the viewpoint of the applet, therefore, the two objects are - * identical. The applet can access the object on the server - * with the regular Java syntax without concern about the actual - * location. - * - *

    The methods remotely called by the applet must be public. - * This is true even if the applet's class and the remote object's classs - * belong to the same package. - * - *

    If class X is a class of remote objects, a subclass of X must be - * also a class of remote objects. On the other hand, this restriction - * is not applied to the superclass of X. The class X does not have to - * contain a constructor taking no arguments. - * - *

    The parameters to a remote method is passed in the call-by-value - * manner. Thus all the parameter classes must implement - * java.io.Serializable. However, if the parameter is the - * proxy object, the reference to the remote object instead of a copy of - * the object is passed to the method. - * - *

    Because of the limitations of the current implementation, - *

    - * - *

    All the exceptions thrown by the remote object are converted - * into RemoteException. Since this exception is a subclass - * of RuntimeException, the caller method does not need - * to catch the exception. However, good programs should catch - * the RuntimeException. - * - * @see javassist.rmi.AppletServer - * @see javassist.rmi.RemoteException - * @see javassist.web.Viewer - */ -public class ObjectImporter implements java.io.Serializable { - private final byte[] endofline = { 0x0d, 0x0a }; - private String servername, orgServername; - private int port, orgPort; - - protected byte[] lookupCommand = "POST /lookup HTTP/1.0".getBytes(); - protected byte[] rmiCommand = "POST /rmi HTTP/1.0".getBytes(); - - /** - * Constructs an object importer. - * - *

    Remote objects are imported from the web server that the given - * applet has been loaded from. - * - * @param applet the applet loaded from the Webserver. - */ - public ObjectImporter(Applet applet) { - URL codebase = applet.getCodeBase(); - orgServername = servername = codebase.getHost(); - orgPort = port = codebase.getPort(); - } - - /** - * Constructs an object importer. - * - *

    If you run a program with javassist.web.Viewer, - * you can construct an object importer as follows: - * - *

    - * - * @see javassist.web.Viewer - */ - public ObjectImporter(String servername, int port) { - this.orgServername = this.servername = servername; - this.orgPort = this.port = port; - } - - /** - * Finds the object exported by a server with the specified name. - * If the object is not found, this method returns null. - * - * @param name the name of the exported object. - * @return the proxy object or null. - */ - public Object getObject(String name) { - try { - return lookupObject(name); - } - catch (ObjectNotFoundException e) { - return null; - } - } - - /** - * Sets an http proxy server. After this method is called, the object - * importer connects a server through the http proxy server. - */ - public void setHttpProxy(String host, int port) { - String proxyHeader = "POST http://" + orgServername + ":" + orgPort; - String cmd = proxyHeader + "/lookup HTTP/1.0"; - lookupCommand = cmd.getBytes(); - cmd = proxyHeader + "/rmi HTTP/1.0"; - rmiCommand = cmd.getBytes(); - this.servername = host; - this.port = port; - } - - /** - * Finds the object exported by the server with the specified name. - * It sends a POST request to the server (via an http proxy server - * if needed). - * - * @param name the name of the exported object. - * @return the proxy object. - */ - public Object lookupObject(String name) throws ObjectNotFoundException - { - try { - Socket sock = new Socket(servername, port); - OutputStream out = sock.getOutputStream(); - out.write(lookupCommand); - out.write(endofline); - out.write(endofline); - - ObjectOutputStream dout = new ObjectOutputStream(out); - dout.writeUTF(name); - dout.flush(); - - InputStream in = new BufferedInputStream(sock.getInputStream()); - skipHeader(in); - ObjectInputStream din = new ObjectInputStream(in); - int n = din.readInt(); - String classname = din.readUTF(); - din.close(); - dout.close(); - sock.close(); - - if (n >= 0) - return createProxy(n, classname); - } - catch (Exception e) { - e.printStackTrace(); - throw new ObjectNotFoundException(name, e); - } - - throw new ObjectNotFoundException(name); - } - - private static final Class[] proxyConstructorParamTypes - = new Class[] { ObjectImporter.class, int.class }; - - private Object createProxy(int oid, String classname) throws Exception { - Class c = Class.forName(classname); - Constructor cons = c.getConstructor(proxyConstructorParamTypes); - return cons.newInstance(new Object[] { this, new Integer(oid) }); - } - - /** - * Calls a method on a remote object. - * It sends a POST request to the server (via an http proxy server - * if needed). - * - *

    This method is called by only proxy objects. - */ - public Object call(int objectid, int methodid, Object[] args) - throws RemoteException - { - boolean result; - Object rvalue; - String errmsg; - - try { - /* This method establishes a raw tcp connection for sending - * a POST message. Thus the object cannot communicate a - * remote object beyond a fire wall. To avoid this problem, - * the connection should be established with a mechanism - * collaborating a proxy server. Unfortunately, java.lang.URL - * does not seem to provide such a mechanism. - * - * You might think that using HttpURLConnection is a better - * way than constructing a raw tcp connection. Unfortunately, - * URL.openConnection() does not return an HttpURLConnection - * object in Netscape's JVM. It returns a - * netscape.net.URLConnection object. - * - * lookupObject() has the same problem. - */ - Socket sock = new Socket(servername, port); - OutputStream out = new BufferedOutputStream( - sock.getOutputStream()); - out.write(rmiCommand); - out.write(endofline); - out.write(endofline); - - ObjectOutputStream dout = new ObjectOutputStream(out); - dout.writeInt(objectid); - dout.writeInt(methodid); - writeParameters(dout, args); - dout.flush(); - - InputStream ins = new BufferedInputStream(sock.getInputStream()); - skipHeader(ins); - ObjectInputStream din = new ObjectInputStream(ins); - result = din.readBoolean(); - rvalue = null; - errmsg = null; - if (result) - rvalue = din.readObject(); - else - errmsg = din.readUTF(); - - din.close(); - dout.close(); - sock.close(); - - if (rvalue instanceof RemoteRef) { - RemoteRef ref = (RemoteRef)rvalue; - rvalue = createProxy(ref.oid, ref.classname); - } - } - catch (ClassNotFoundException e) { - throw new RemoteException(e); - } - catch (IOException e) { - throw new RemoteException(e); - } - catch (Exception e) { - throw new RemoteException(e); - } - - if (result) - return rvalue; - else - throw new RemoteException(errmsg); - } - - private void skipHeader(InputStream in) throws IOException { - int len; - do { - int c; - len = 0; - while ((c = in.read()) >= 0 && c != 0x0d) - ++len; - - in.read(); /* skip 0x0a (LF) */ - } while (len > 0); - } - - private void writeParameters(ObjectOutputStream dout, Object[] params) - throws IOException - { - int n = params.length; - dout.writeInt(n); - for (int i = 0; i < n; ++i) - if (params[i] instanceof Proxy) { - Proxy p = (Proxy)params[i]; - dout.writeObject(new RemoteRef(p._getObjectId())); - } - else - dout.writeObject(params[i]); - } -} diff --git a/src/main/javassist/rmi/ObjectNotFoundException.java b/src/main/javassist/rmi/ObjectNotFoundException.java deleted file mode 100644 index 85bc316e..00000000 --- a/src/main/javassist/rmi/ObjectNotFoundException.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.rmi; - -public class ObjectNotFoundException extends Exception { - public ObjectNotFoundException(String name) { - super(name + " is not exported"); - } - - public ObjectNotFoundException(String name, Exception e) { - super(name + " because of " + e.toString()); - } -} diff --git a/src/main/javassist/rmi/Proxy.java b/src/main/javassist/rmi/Proxy.java deleted file mode 100644 index f627ac63..00000000 --- a/src/main/javassist/rmi/Proxy.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.rmi; - -/** - * An interface implemented by proxy classes. - * - * @see javassist.rmi.StubGenerator - */ -public interface Proxy { - int _getObjectId(); -} diff --git a/src/main/javassist/rmi/RemoteException.java b/src/main/javassist/rmi/RemoteException.java deleted file mode 100644 index ceb04f9f..00000000 --- a/src/main/javassist/rmi/RemoteException.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.rmi; - -/** - * RemoteException represents any exception thrown - * during remote method invocation. - */ -public class RemoteException extends RuntimeException { - public RemoteException(String msg) { - super(msg); - } - - public RemoteException(Exception e) { - super("by " + e.toString()); - } -} diff --git a/src/main/javassist/rmi/RemoteRef.java b/src/main/javassist/rmi/RemoteRef.java deleted file mode 100644 index fbd93add..00000000 --- a/src/main/javassist/rmi/RemoteRef.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.rmi; - -/** - * Remote reference. This class is internally used for sending a remote - * reference through a network stream. - */ -public class RemoteRef implements java.io.Serializable { - public int oid; - public String classname; - - public RemoteRef(int i) { - oid = i; - classname = null; - } - - public RemoteRef(int i, String name) { - oid = i; - classname = name; - } -} diff --git a/src/main/javassist/rmi/Sample.java b/src/main/javassist/rmi/Sample.java deleted file mode 100644 index b632cd03..00000000 --- a/src/main/javassist/rmi/Sample.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.rmi; - -/** - * A template used for defining a proxy class. - * The class file of this class is read by the StubGenerator - * class. - */ -public class Sample { - private ObjectImporter importer; - private int objectId; - - public Object forward(Object[] args, int identifier) { - return importer.call(objectId, identifier, args); - } - - public static Object forwardStatic(Object[] args, int identifier) - throws RemoteException - { - throw new RemoteException("cannot call a static method."); - } -} diff --git a/src/main/javassist/rmi/StubGenerator.java b/src/main/javassist/rmi/StubGenerator.java deleted file mode 100644 index eac40916..00000000 --- a/src/main/javassist/rmi/StubGenerator.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.rmi; - -import javassist.*; -import java.lang.reflect.Method; -import java.util.Hashtable; -import javassist.CtMethod.ConstParameter; - -/** - * A stub-code generator. It is used for producing a proxy class. - * - *

    The proxy class for class A is as follows: - * - *

    - * - *

    Instances of the proxy class is created by an - * ObjectImporter object. - */ -public class StubGenerator implements Translator { - private static final String fieldImporter = "importer"; - private static final String fieldObjectId = "objectId"; - private static final String accessorObjectId = "_getObjectId"; - private static final String sampleClass = "javassist.rmi.Sample"; - - private ClassPool classPool; - private Hashtable proxyClasses; - private CtMethod forwardMethod; - private CtMethod forwardStaticMethod; - - private CtClass[] proxyConstructorParamTypes; - private CtClass[] interfacesForProxy; - private CtClass[] exceptionForProxy; - - /** - * Constructs a stub-code generator. - */ - public StubGenerator() { - proxyClasses = new Hashtable(); - } - - /** - * Initializes the object. - * This is a method declared in javassist.Translator. - * - * @see javassist.Translator#start(ClassPool) - */ - public void start(ClassPool pool) throws NotFoundException { - classPool = pool; - CtClass c = pool.get(sampleClass); - forwardMethod = c.getDeclaredMethod("forward"); - forwardStaticMethod = c.getDeclaredMethod("forwardStatic"); - - proxyConstructorParamTypes - = pool.get(new String[] { "javassist.rmi.ObjectImporter", - "int" }); - interfacesForProxy - = pool.get(new String[] { "java.io.Serializable", - "javassist.rmi.Proxy" }); - exceptionForProxy - = new CtClass[] { pool.get("javassist.rmi.RemoteException") }; - } - - /** - * Does nothing. - * This is a method declared in javassist.Translator. - * @see javassist.Translator#onLoad(ClassPool,String) - */ - public void onLoad(ClassPool pool, String classname) {} - - /** - * Returns true if the specified class is a proxy class - * recorded by makeProxyClass(). - * - * @param name a fully-qualified class name - */ - public boolean isProxyClass(String name) { - return proxyClasses.get(name) != null; - } - - /** - * Makes a proxy class. The produced class is substituted - * for the original class. - * - * @param clazz the class referenced - * through the proxy class. - * @return false if the proxy class - * has been already produced. - */ - public synchronized boolean makeProxyClass(Class clazz) - throws CannotCompileException, NotFoundException - { - String classname = clazz.getName(); - if (proxyClasses.get(classname) != null) - return false; - else { - CtClass ctclazz = produceProxyClass(classPool.get(classname), - clazz); - proxyClasses.put(classname, ctclazz); - modifySuperclass(ctclazz); - return true; - } - } - - private CtClass produceProxyClass(CtClass orgclass, Class orgRtClass) - throws CannotCompileException, NotFoundException - { - int modify = orgclass.getModifiers(); - if (Modifier.isAbstract(modify) || Modifier.isNative(modify) - || !Modifier.isPublic(modify)) - throw new CannotCompileException(orgclass.getName() - + " must be public, non-native, and non-abstract."); - - CtClass proxy = classPool.makeClass(orgclass.getName(), - orgclass.getSuperclass()); - - proxy.setInterfaces(interfacesForProxy); - - CtField f - = new CtField(classPool.get("javassist.rmi.ObjectImporter"), - fieldImporter, proxy); - f.setModifiers(Modifier.PRIVATE); - proxy.addField(f, CtField.Initializer.byParameter(0)); - - f = new CtField(CtClass.intType, fieldObjectId, proxy); - f.setModifiers(Modifier.PRIVATE); - proxy.addField(f, CtField.Initializer.byParameter(1)); - - proxy.addMethod(CtNewMethod.getter(accessorObjectId, f)); - - proxy.addConstructor(CtNewConstructor.defaultConstructor(proxy)); - CtConstructor cons - = CtNewConstructor.skeleton(proxyConstructorParamTypes, - null, proxy); - proxy.addConstructor(cons); - - try { - addMethods(proxy, orgRtClass.getMethods()); - return proxy; - } - catch (SecurityException e) { - throw new CannotCompileException(e); - } - } - - private CtClass toCtClass(Class rtclass) throws NotFoundException { - String name; - if (!rtclass.isArray()) - name = rtclass.getName(); - else { - StringBuffer sbuf = new StringBuffer(); - do { - sbuf.append("[]"); - rtclass = rtclass.getComponentType(); - } while(rtclass.isArray()); - sbuf.insert(0, rtclass.getName()); - name = sbuf.toString(); - } - - return classPool.get(name); - } - - private CtClass[] toCtClass(Class[] rtclasses) throws NotFoundException { - int n = rtclasses.length; - CtClass[] ctclasses = new CtClass[n]; - for (int i = 0; i < n; ++i) - ctclasses[i] = toCtClass(rtclasses[i]); - - return ctclasses; - } - - /* ms must not be an array of CtMethod. To invoke a method ms[i] - * on a server, a client must send i to the server. - */ - private void addMethods(CtClass proxy, Method[] ms) - throws CannotCompileException, NotFoundException - { - CtMethod wmethod; - for (int i = 0; i < ms.length; ++i) { - Method m = ms[i]; - int mod = m.getModifiers(); - if (m.getDeclaringClass() != Object.class - && !Modifier.isFinal(mod)) - if (Modifier.isPublic(mod)) { - CtMethod body; - if (Modifier.isStatic(mod)) - body = forwardStaticMethod; - else - body = forwardMethod; - - wmethod - = CtNewMethod.wrapped(toCtClass(m.getReturnType()), - m.getName(), - toCtClass(m.getParameterTypes()), - exceptionForProxy, - body, - ConstParameter.integer(i), - proxy); - wmethod.setModifiers(mod); - proxy.addMethod(wmethod); - } - else if (!Modifier.isProtected(mod) - && !Modifier.isPrivate(mod)) - // if package method - throw new CannotCompileException( - "the methods must be public, protected, or private."); - } - } - - /** - * Adds a default constructor to the super classes. - */ - private void modifySuperclass(CtClass orgclass) - throws CannotCompileException, NotFoundException - { - CtClass superclazz; - for (;; orgclass = superclazz) { - superclazz = orgclass.getSuperclass(); - if (superclazz == null) - break; - - try { - superclazz.getDeclaredConstructor(null); - break; // the constructor with no arguments is found. - } - catch (NotFoundException e) { - } - - superclazz.addConstructor( - CtNewConstructor.defaultConstructor(superclazz)); - } - } -} diff --git a/src/main/javassist/rmi/package.html b/src/main/javassist/rmi/package.html deleted file mode 100644 index 5432a948..00000000 --- a/src/main/javassist/rmi/package.html +++ /dev/null @@ -1,16 +0,0 @@ - - -Sample implementation of remote method invocation. - -

    This package enables applets to access remote objects -running on the web server with regular Java syntax. -It is provided as a sample implementation with Javassist. -All the programs in this package uses only the regular -Javassist API; they never call any hidden methods. - -

    The most significant class of this package is -ObjectImporter. -See the description of this class first. - - - diff --git a/src/main/javassist/tools/HotSwapper.java b/src/main/javassist/tools/HotSwapper.java deleted file mode 100644 index 7b5b5fa2..00000000 --- a/src/main/javassist/tools/HotSwapper.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.tools; - -import com.sun.jdi.*; -import com.sun.jdi.connect.*; -import com.sun.jdi.event.*; -import com.sun.jdi.request.*; -import java.io.*; -import java.util.*; - -class Trigger { - void doSwap() {} -} - -/** - * A utility class for dynamically reloading a class by - * the Java Platform Debugger Architecture (JPDA), or HotSwap. - * It works only with JDK 1.4 and later. - * - *

    Note: The new definition of the reloaded class must declare - * the same set of methods and fields as the original definition. The - * schema change between the original and new definitions is not allowed - * by the JPDA. - * - *

    To use this class, the JVM must be launched with the following - * command line options: - * - *

    - * - *

    Note that 8000 is the port number used by HotSwapper. - * Any port number can be specified. Since HotSwapper does not - * launch another JVM for running a target application, this port number - * is used only for inter-thread communication. - * - *

    Furthermore, JAVA_HOME/lib/tools.jar must be included - * in the class path. - * - *

    Using HotSwapper is easy. See the following example: - * - *

    - * - *

    reload() - * first unload the Test class and load a new version of - * the Test class. - * classFile is a byte array containing the new contents of - * the class file for the Test class. The developers can - * repatedly call reload() on the same HotSwapper - * object so that they can reload a number of classes. - * - * @since 3.1 - */ -public class HotSwapper { - private VirtualMachine jvm; - private MethodEntryRequest request; - private Map newClassFiles; - - private Trigger trigger; - - private static final String HOST_NAME = "localhost"; - private static final String TRIGGER_NAME = "javassist.tools.Trigger"; - - /** - * Connects to the JVM. - * - * @param port the port number used for the connection to the JVM. - */ - public HotSwapper(int port) - throws IOException, IllegalConnectorArgumentsException - { - this(Integer.toString(port)); - } - - /** - * Connects to the JVM. - * - * @param port the port number used for the connection to the JVM. - */ - public HotSwapper(String port) - throws IOException, IllegalConnectorArgumentsException - { - jvm = null; - request = null; - newClassFiles = null; - trigger = new Trigger(); - AttachingConnector connector - = (AttachingConnector)findConnector("com.sun.jdi.SocketAttach"); - - Map arguments = connector.defaultArguments(); - ((Connector.Argument)arguments.get("hostname")).setValue(HOST_NAME); - ((Connector.Argument)arguments.get("port")).setValue(port); - jvm = connector.attach(arguments); - EventRequestManager manager = jvm.eventRequestManager(); - request = methodEntryRequests(manager, TRIGGER_NAME); - } - - private Connector findConnector(String connector) throws IOException { - List connectors = Bootstrap.virtualMachineManager().allConnectors(); - Iterator iter = connectors.iterator(); - while (iter.hasNext()) { - Connector con = (Connector)iter.next(); - if (con.name().equals(connector)) { - return con; - } - } - - throw new IOException("Not found: " + connector); - } - - private static MethodEntryRequest methodEntryRequests( - EventRequestManager manager, - String classpattern) { - MethodEntryRequest mereq = manager.createMethodEntryRequest(); - mereq.addClassFilter(classpattern); - mereq.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); - return mereq; - } - - /* Stops triggering a hotswapper when reload() is called. - */ - private void deleteEventRequest(EventRequestManager manager, - MethodEntryRequest request) { - manager.deleteEventRequest(request); - } - - /** - * Reloads a class. - * - * @param className the fully-qualified class name. - * @param classFile the contents of the class file. - */ - public void reload(String className, byte[] classFile) { - ReferenceType classtype = toRefType(className); - Map map = new HashMap(); - map.put(classtype, classFile); - reload2(map, className); - } - - /** - * Reloads a class. - * - * @param classFiles a map between fully-qualified class names - * and class files. The type of the class names - * is String and the type of the - * class files is byte[]. - */ - public void reload(Map classFiles) { - Set set = classFiles.entrySet(); - Iterator it = set.iterator(); - Map map = new HashMap(); - String className = null; - while (it.hasNext()) { - Map.Entry e = (Map.Entry)it.next(); - className = (String)e.getKey(); - map.put(toRefType(className), e.getValue()); - } - - if (className != null) - reload2(map, className + " etc."); - } - - private ReferenceType toRefType(String className) { - List list = jvm.classesByName(className); - if (list == null || list.isEmpty()) - throw new RuntimeException("no such a class: " + className); - else - return (ReferenceType)list.get(0); - } - - private void reload2(Map map, String msg) { - synchronized (trigger) { - startDaemon(); - newClassFiles = map; - request.enable(); - trigger.doSwap(); - request.disable(); - Map ncf = newClassFiles; - if (ncf != null) { - newClassFiles = null; - throw new RuntimeException("failed to reload: " + msg); - } - } - } - - private void startDaemon() { - new Thread() { - private void errorMsg(Throwable e) { - System.err.print("Exception in thread \"HotSwap\" "); - e.printStackTrace(System.err); - } - - public void run() { - EventSet events = null; - try { - events = waitEvent(); - EventIterator iter = events.eventIterator(); - while (iter.hasNext()) { - Event event = iter.nextEvent(); - if (event instanceof MethodEntryEvent) { - hotswap(); - break; - } - } - } - catch (Throwable e) { - errorMsg(e); - } - try { - if (events != null) - events.resume(); - } - catch (Throwable e) { - errorMsg(e); - } - } - }.start(); - } - - EventSet waitEvent() throws InterruptedException { - EventQueue queue = jvm.eventQueue(); - return queue.remove(); - } - - void hotswap() { - Map map = newClassFiles; - jvm.redefineClasses(map); - newClassFiles = null; - } -} diff --git a/src/main/javassist/tools/package.html b/src/main/javassist/tools/package.html index 1566e121..bee6208d 100644 --- a/src/main/javassist/tools/package.html +++ b/src/main/javassist/tools/package.html @@ -1,6 +1,6 @@ -Utility classes. +Covenient tools. diff --git a/src/main/javassist/tools/reflect/CannotCreateException.java b/src/main/javassist/tools/reflect/CannotCreateException.java new file mode 100644 index 00000000..efa14111 --- /dev/null +++ b/src/main/javassist/tools/reflect/CannotCreateException.java @@ -0,0 +1,29 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.reflect; + +/** + * Signals that ClassMetaobject.newInstance() fails. + */ +public class CannotCreateException extends Exception { + public CannotCreateException(String s) { + super(s); + } + + public CannotCreateException(Exception e) { + super("by " + e.toString()); + } +} diff --git a/src/main/javassist/tools/reflect/CannotInvokeException.java b/src/main/javassist/tools/reflect/CannotInvokeException.java new file mode 100644 index 00000000..d283d6d7 --- /dev/null +++ b/src/main/javassist/tools/reflect/CannotInvokeException.java @@ -0,0 +1,68 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.reflect; + +import java.lang.reflect.InvocationTargetException; +import java.lang.IllegalAccessException; + +/** + * Thrown when method invocation using the reflection API has thrown + * an exception. + * + * @see javassist.tools.reflect.Metaobject#trapMethodcall(int, Object[]) + * @see javassist.tools.reflect.ClassMetaobject#trapMethodcall(int, Object[]) + * @see javassist.tools.reflect.ClassMetaobject#invoke(Object, int, Object[]) + */ +public class CannotInvokeException extends RuntimeException { + + private Throwable err = null; + + /** + * Returns the cause of this exception. It may return null. + */ + public Throwable getReason() { return err; } + + /** + * Constructs a CannotInvokeException with an error message. + */ + public CannotInvokeException(String reason) { + super(reason); + } + + /** + * Constructs a CannotInvokeException with an InvocationTargetException. + */ + public CannotInvokeException(InvocationTargetException e) { + super("by " + e.getTargetException().toString()); + err = e.getTargetException(); + } + + /** + * Constructs a CannotInvokeException with an IllegalAccessException. + */ + public CannotInvokeException(IllegalAccessException e) { + super("by " + e.toString()); + err = e; + } + + /** + * Constructs a CannotInvokeException with an ClassNotFoundException. + */ + public CannotInvokeException(ClassNotFoundException e) { + super("by " + e.toString()); + err = e; + } +} diff --git a/src/main/javassist/tools/reflect/CannotReflectException.java b/src/main/javassist/tools/reflect/CannotReflectException.java new file mode 100644 index 00000000..30c448b3 --- /dev/null +++ b/src/main/javassist/tools/reflect/CannotReflectException.java @@ -0,0 +1,34 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.reflect; + +import javassist.CannotCompileException; + +/** + * Thrown by makeReflective() in Reflection + * 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.tools.reflect.Reflection#makeReflective(CtClass,CtClass,CtClass) + * @see javassist.CannotCompileException + */ +public class CannotReflectException extends CannotCompileException { + public CannotReflectException(String msg) { + super(msg); + } +} diff --git a/src/main/javassist/tools/reflect/ClassMetaobject.java b/src/main/javassist/tools/reflect/ClassMetaobject.java new file mode 100644 index 00000000..d0a645cc --- /dev/null +++ b/src/main/javassist/tools/reflect/ClassMetaobject.java @@ -0,0 +1,367 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.reflect; + +import java.lang.reflect.*; +import java.util.Arrays; +import java.io.Serializable; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * A runtime class metaobject. + * + *

    A ClassMetaobject is created for every + * class of reflective objects. It can be used to hold values + * shared among the reflective objects of the same class. + * + *

    To obtain a class metaobject, calls _getClass() + * on a reflective object. For example, + * + *

    + * + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.Metalevel + */ +public class ClassMetaobject implements Serializable { + /** + * The base-level methods controlled by a metaobject + * are renamed so that they begin with + * methodPrefix "_m_". + */ + static final String methodPrefix = "_m_"; + static final int methodPrefixLen = 3; + + private Class javaClass; + private Constructor[] constructors; + private Method[] methods; + + /** + * Specifies how a java.lang.Class object is loaded. + * + *

    If true, it is loaded by: + *

    + *

    If false, it is loaded by Class.forName(). + * The default value is false. + */ + public static boolean useContextClassLoader = false; + + /** + * Constructs a ClassMetaobject. + * + * @param params params[0] is the name of the class + * of the reflective objects. + */ + public ClassMetaobject(String[] params) + { + try { + javaClass = getClassObject(params[0]); + } + catch (ClassNotFoundException e) { + javaClass = null; + } + + constructors = javaClass.getConstructors(); + methods = null; + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.writeUTF(javaClass.getName()); + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + javaClass = getClassObject(in.readUTF()); + constructors = javaClass.getConstructors(); + methods = null; + } + + private Class getClassObject(String name) throws ClassNotFoundException { + if (useContextClassLoader) + return Thread.currentThread().getContextClassLoader() + .loadClass(name); + else + return Class.forName(name); + } + + /** + * Obtains the java.lang.Class representing this class. + */ + public final Class getJavaClass() { + return javaClass; + } + + /** + * Obtains the name of this class. + */ + public final String getName() { + return javaClass.getName(); + } + + /** + * Returns true if obj is an instance of this class. + */ + public final boolean isInstance(Object obj) { + return javaClass.isInstance(obj); + } + + /** + * Creates a new instance of the class. + * + * @param args the arguments passed to the constructor. + */ + public final Object newInstance(Object[] args) + throws CannotCreateException + { + int n = constructors.length; + for (int i = 0; i < n; ++i) { + try { + return constructors[i].newInstance(args); + } + catch (IllegalArgumentException e) { + // try again + } + catch (InstantiationException e) { + throw new CannotCreateException(e); + } + catch (IllegalAccessException e) { + throw new CannotCreateException(e); + } + catch (InvocationTargetException e) { + throw new CannotCreateException(e); + } + } + + throw new CannotCreateException("no constructor matches"); + } + + /** + * Is invoked when static fields of the base-level + * class are read and the runtime system intercepts it. + * This method simply returns the value of the field. + * + *

    Every subclass of this class should redefine this method. + */ + public Object trapFieldRead(String name) { + Class jc = getJavaClass(); + try { + return jc.getField(name).get(null); + } + catch (NoSuchFieldException e) { + throw new RuntimeException(e.toString()); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Is invoked when static fields of the base-level + * class are modified and the runtime system intercepts it. + * This method simply sets the field to the given value. + * + *

    Every subclass of this class should redefine this method. + */ + public void trapFieldWrite(String name, Object value) { + Class jc = getJavaClass(); + try { + jc.getField(name).set(null, value); + } + catch (NoSuchFieldException e) { + throw new RuntimeException(e.toString()); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Invokes a method whose name begins with + * methodPrefix "_m_" and the identifier. + * + * @exception CannotInvokeException if the invocation fails. + */ + static public Object invoke(Object target, int identifier, Object[] args) + throws Throwable + { + Method[] allmethods = target.getClass().getMethods(); + int n = allmethods.length; + String head = methodPrefix + identifier; + for (int i = 0; i < n; ++i) + if (allmethods[i].getName().startsWith(head)) { + try { + return allmethods[i].invoke(target, args); + } catch (java.lang.reflect.InvocationTargetException e) { + throw e.getTargetException(); + } catch (java.lang.IllegalAccessException e) { + throw new CannotInvokeException(e); + } + } + + throw new CannotInvokeException("cannot find a method"); + } + + /** + * Is invoked when static methods of the base-level + * class are called and the runtime system intercepts it. + * This method simply executes the intercepted method invocation + * with the original parameters and returns the resulting value. + * + *

    Every subclass of this class should redefine this method. + */ + public Object trapMethodcall(int identifier, Object[] args) + throws Throwable + { + try { + Method[] m = getReflectiveMethods(); + return m[identifier].invoke(null, args); + } + catch (java.lang.reflect.InvocationTargetException e) { + throw e.getTargetException(); + } + catch (java.lang.IllegalAccessException e) { + throw new CannotInvokeException(e); + } + } + + /** + * Returns an array of the methods defined on the given reflective + * object. This method is for the internal use only. + */ + public final Method[] getReflectiveMethods() { + if (methods != null) + return methods; + + Class baseclass = getJavaClass(); + Method[] allmethods = baseclass.getDeclaredMethods(); + int n = allmethods.length; + int[] index = new int[n]; + int max = 0; + for (int i = 0; i < n; ++i) { + Method m = allmethods[i]; + String mname = m.getName(); + if (mname.startsWith(methodPrefix)) { + int k = 0; + for (int j = methodPrefixLen;; ++j) { + char c = mname.charAt(j); + if ('0' <= c && c <= '9') + k = k * 10 + c - '0'; + else + break; + } + + index[i] = ++k; + if (k > max) + max = k; + } + } + + methods = new Method[max]; + for (int i = 0; i < n; ++i) + if (index[i] > 0) + methods[index[i] - 1] = allmethods[i]; + + return methods; + } + + /** + * Returns the java.lang.reflect.Method object representing + * the method specified by identifier. + * + *

    Note that the actual method returned will be have an altered, + * reflective name i.e. _m_2_... + * + * @param identifier the identifier index + * given to trapMethodcall() etc. + * @see Metaobject#trapMethodcall(int,Object[]) + * @see #trapMethodcall(int,Object[]) + */ + public final Method getMethod(int identifier) { + return getReflectiveMethods()[identifier]; + } + + /** + * Returns the name of the method specified + * by identifier. + */ + public final String getMethodName(int identifier) { + String mname = getReflectiveMethods()[identifier].getName(); + int j = ClassMetaobject.methodPrefixLen; + for (;;) { + char c = mname.charAt(j++); + if (c < '0' || '9' < c) + break; + } + + return mname.substring(j); + } + + /** + * Returns an array of Class objects representing the + * formal parameter types of the method specified + * by identifier. + */ + public final Class[] getParameterTypes(int identifier) { + return getReflectiveMethods()[identifier].getParameterTypes(); + } + + /** + * Returns a Class objects representing the + * return type of the method specified by identifier. + */ + public final Class getReturnType(int identifier) { + return getReflectiveMethods()[identifier].getReturnType(); + } + + /** + * Returns the identifier index of the method, as identified by its + * original name. + * + *

    This method is useful, in conjuction with + * ClassMetaobject#getMethod(), to obtain a quick reference + * to the original method in the reflected class (i.e. not the proxy + * method), using the original name of the method. + * + *

    Written by Brett Randall and Shigeru Chiba. + * + * @param originalName The original name of the reflected method + * @param argTypes array of Class specifying the method signature + * @return the identifier index of the original method + * @throws NoSuchMethodException if the method does not exist + * + * @see ClassMetaobject#getMethod(int) + */ + public final int getMethodIndex(String originalName, Class[] argTypes) + throws NoSuchMethodException + { + Method[] mthds = getReflectiveMethods(); + for (int i = 0; i < mthds.length; i++) { + if (mthds[i] == null) + continue; + + // check name and parameter types match + if (getMethodName(i).equals(originalName) + && Arrays.equals(argTypes, mthds[i].getParameterTypes())) + return i; + } + + throw new NoSuchMethodException("Method " + originalName + + " not found"); + } +} diff --git a/src/main/javassist/tools/reflect/Compiler.java b/src/main/javassist/tools/reflect/Compiler.java new file mode 100644 index 00000000..7e1fb943 --- /dev/null +++ b/src/main/javassist/tools/reflect/Compiler.java @@ -0,0 +1,162 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.reflect; + +import javassist.CtClass; +import javassist.ClassPool; +import java.io.PrintStream; + +class CompiledClass { + public String classname; + public String metaobject; + public String classobject; +} + +/** + * A bytecode translator for reflection. + * + *

    This translator directly modifies class files on a local disk so that + * the classes represented by those class files are reflective. + * After the modification, the class files can be run with the standard JVM + * without javassist.tools.reflect.Loader + * or any other user-defined class loader. + * + *

    The modified class files are given as the command-line parameters, + * which are a sequence of fully-qualified class names followed by options: + * + *

    -m classname : specifies the class of the + * metaobjects associated with instances of the class followed by + * this option. The default is javassit.reflect.Metaobject. + * + *

    -c classname : specifies the class of the + * class metaobjects associated with instances of the class followed by + * this option. The default is javassit.reflect.ClassMetaobject. + * + *

    If a class name is not followed by any options, the class indicated + * by that class name is not reflective. + * + *

    For example, + *

    + * + *

    modifies class files Dog.class, Cat.class, + * and Cow.class. + * The metaobject of a Dog object is a MetaDog object and the class + * metaobject is a CMetaDog object. + * The metaobject of a Cat object is a MetaCat object but + * the class metaobject is a default one. + * Cow objects are not reflective. + * + *

    Note that if the super class is also made reflective, it must be done + * before the sub class. + * + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.ClassMetaobject + * @see javassist.tools.reflect.Reflection + */ +public class Compiler { + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + help(System.err); + return; + } + + CompiledClass[] entries = new CompiledClass[args.length]; + int n = parse(args, entries); + + if (n < 1) { + System.err.println("bad parameter."); + return; + } + + processClasses(entries, n); + } + + private static void processClasses(CompiledClass[] entries, int n) + throws Exception + { + Reflection implementor = new Reflection(); + ClassPool pool = ClassPool.getDefault(); + implementor.start(pool); + + for (int i = 0; i < n; ++i) { + CtClass c = pool.get(entries[i].classname); + if (entries[i].metaobject != null + || entries[i].classobject != null) { + String metaobj, classobj; + + if (entries[i].metaobject == null) + metaobj = "javassist.tools.reflect.Metaobject"; + else + metaobj = entries[i].metaobject; + + if (entries[i].classobject == null) + classobj = "javassist.tools.reflect.ClassMetaobject"; + else + classobj = entries[i].classobject; + + if (!implementor.makeReflective(c, pool.get(metaobj), + pool.get(classobj))) + System.err.println("Warning: " + c.getName() + + " is reflective. It was not changed."); + + System.err.println(c.getName() + ": " + metaobj + ", " + + classobj); + } + else + System.err.println(c.getName() + ": not reflective"); + } + + for (int i = 0; i < n; ++i) { + implementor.onLoad(pool, entries[i].classname); + pool.get(entries[i].classname).writeFile(); + } + } + + private static int parse(String[] args, CompiledClass[] result) { + int n = -1; + for (int i = 0; i < args.length; ++i) { + String a = args[i]; + if (a.equals("-m")) + if (n < 0 || i + 1 > args.length) + return -1; + else + result[n].metaobject = args[++i]; + else if (a.equals("-c")) + if (n < 0 || i + 1 > args.length) + return -1; + else + result[n].classobject = args[++i]; + else if (a.charAt(0) == '-') + return -1; + else { + CompiledClass cc = new CompiledClass(); + cc.classname = a; + cc.metaobject = null; + cc.classobject = null; + result[++n] = cc; + } + } + + return n + 1; + } + + private static void help(PrintStream out) { + out.println("Usage: java javassist.tools.reflect.Compiler"); + out.println(" ( [-m ] [-c ])+"); + } +} diff --git a/src/main/javassist/tools/reflect/Loader.java b/src/main/javassist/tools/reflect/Loader.java new file mode 100644 index 00000000..36189edf --- /dev/null +++ b/src/main/javassist/tools/reflect/Loader.java @@ -0,0 +1,163 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.reflect; + +import javassist.CannotCompileException; +import javassist.NotFoundException; +import javassist.ClassPool; + +/** + * A class loader for reflection. + * + *

    To run a program, say MyApp, + * including a reflective class, + * you must write a start-up program as follows: + * + *

    + * + *

    Then run this program as follows: + * + *

    + * + *

    This command runs Main.main() with arg1, ... + * and Main.main() runs MyApp.main() with + * arg1, ... + * The Person class is modified + * to be a reflective class. Method calls on a Person + * object are intercepted by an instance of MyMetaobject. + * + *

    Also, you can run MyApp in a slightly different way: + * + *

    + * + *

    This program is run as follows: + * + *

    + * + *

    The difference from the former one is that the class Main + * is loaded by javassist.tools.reflect.Loader whereas the class + * Main2 is not. Thus, Main belongs + * to the same name space (security domain) as MyApp + * whereas Main2 does not; Main2 belongs + * to the same name space as javassist.tools.reflect.Loader. + * For more details, + * see the notes in the manual page of javassist.Loader. + * + *

    The class Main2 is equivalent to this class: + * + *

    + * + *

    Note: + * + *

    javassist.tools.reflect.Loader does not make a class reflective + * if that class is in a java.* or + * javax.* pacakge because of the specifications + * on the class loading algorithm of Java. The JVM does not allow to + * load such a system class with a user class loader. + * + *

    To avoid this limitation, those classes should be statically + * modified with javassist.tools.reflect.Compiler and the original + * class files should be replaced. + * + * @see javassist.tools.reflect.Reflection + * @see javassist.tools.reflect.Compiler + * @see javassist.Loader + */ +public class Loader extends javassist.Loader { + protected Reflection reflection; + + /** + * Loads a class with an instance of Loader + * and calls main() in that class. + * + * @param args command line parameters. + *

    + */ + public static void main(String[] args) throws Throwable { + Loader cl = new Loader(); + cl.run(args); + } + + /** + * Constructs a new class loader. + */ + public Loader() throws CannotCompileException, NotFoundException { + super(); + delegateLoadingOf("javassist.tools.reflect.Loader"); + + reflection = new Reflection(); + ClassPool pool = ClassPool.getDefault(); + addTranslator(pool, reflection); + } + + /** + * Produces a reflective class. + * If the super class is also made reflective, it must be done + * before the sub class. + * + * @param clazz the reflective class. + * @param metaobject the class of metaobjects. + * It must be a subclass of + * Metaobject. + * @param metaclass the class of the class metaobject. + * It must be a subclass of + * ClassMetaobject. + * @return false if the class is already reflective. + * + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.ClassMetaobject + */ + public boolean makeReflective(String clazz, + String metaobject, String metaclass) + throws CannotCompileException, NotFoundException + { + return reflection.makeReflective(clazz, metaobject, metaclass); + } +} diff --git a/src/main/javassist/tools/reflect/Metalevel.java b/src/main/javassist/tools/reflect/Metalevel.java new file mode 100644 index 00000000..db45cad9 --- /dev/null +++ b/src/main/javassist/tools/reflect/Metalevel.java @@ -0,0 +1,38 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.reflect; + +/** + * An interface to access a metaobject and a class metaobject. + * This interface is implicitly implemented by the reflective + * class. + */ +public interface Metalevel { + /** + * Obtains the class metaobject associated with this object. + */ + ClassMetaobject _getClass(); + + /** + * Obtains the metaobject associated with this object. + */ + Metaobject _getMetaobject(); + + /** + * Changes the metaobject associated with this object. + */ + void _setMetaobject(Metaobject m); +} diff --git a/src/main/javassist/tools/reflect/Metaobject.java b/src/main/javassist/tools/reflect/Metaobject.java new file mode 100644 index 00000000..e89b5398 --- /dev/null +++ b/src/main/javassist/tools/reflect/Metaobject.java @@ -0,0 +1,236 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.reflect; + +import java.lang.reflect.Method; +import java.io.Serializable; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * A runtime metaobject. + * + *

    A Metaobject is created for + * every object at the base level. A different reflective object is + * associated with a different metaobject. + * + *

    The metaobject intercepts method calls + * on the reflective object at the base-level. To change the behavior + * of the method calls, a subclass of Metaobject + * should be defined. + * + *

    To obtain a metaobject, calls _getMetaobject() + * on a reflective object. For example, + * + *

    + * + * @see javassist.tools.reflect.ClassMetaobject + * @see javassist.tools.reflect.Metalevel + */ +public class Metaobject implements Serializable { + protected ClassMetaobject classmetaobject; + protected Metalevel baseobject; + protected Method[] methods; + + /** + * Constructs a Metaobject. The metaobject is + * constructed before the constructor is called on the base-level + * object. + * + * @param self the object that this metaobject is associated with. + * @param args the parameters passed to the constructor of + * self. + */ + public Metaobject(Object self, Object[] args) { + baseobject = (Metalevel)self; + classmetaobject = baseobject._getClass(); + methods = classmetaobject.getReflectiveMethods(); + } + + /** + * Constructs a Metaobject without initialization. + * If calling this constructor, a subclass should be responsible + * for initialization. + */ + protected Metaobject() { + baseobject = null; + classmetaobject = null; + methods = null; + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.writeObject(baseobject); + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + baseobject = (Metalevel)in.readObject(); + classmetaobject = baseobject._getClass(); + methods = classmetaobject.getReflectiveMethods(); + } + + /** + * Obtains the class metaobject associated with this metaobject. + * + * @see javassist.tools.reflect.ClassMetaobject + */ + public final ClassMetaobject getClassMetaobject() { + return classmetaobject; + } + + /** + * Obtains the object controlled by this metaobject. + */ + public final Object getObject() { + return baseobject; + } + + /** + * Changes the object controlled by this metaobject. + * + * @param self the object + */ + public final void setObject(Object self) { + baseobject = (Metalevel)self; + classmetaobject = baseobject._getClass(); + methods = classmetaobject.getReflectiveMethods(); + + // call _setMetaobject() after the metaobject is settled. + baseobject._setMetaobject(this); + } + + /** + * Returns the name of the method specified + * by identifier. + */ + public final String getMethodName(int identifier) { + String mname = methods[identifier].getName(); + int j = ClassMetaobject.methodPrefixLen; + for (;;) { + char c = mname.charAt(j++); + if (c < '0' || '9' < c) + break; + } + + return mname.substring(j); + } + + /** + * Returns an array of Class objects representing the + * formal parameter types of the method specified + * by identifier. + */ + public final Class[] getParameterTypes(int identifier) { + return methods[identifier].getParameterTypes(); + } + + /** + * Returns a Class objects representing the + * return type of the method specified by identifier. + */ + public final Class getReturnType(int identifier) { + return methods[identifier].getReturnType(); + } + + /** + * Is invoked when public fields of the base-level + * class are read and the runtime system intercepts it. + * This method simply returns the value of the field. + * + *

    Every subclass of this class should redefine this method. + */ + public Object trapFieldRead(String name) { + Class jc = getClassMetaobject().getJavaClass(); + try { + return jc.getField(name).get(getObject()); + } + catch (NoSuchFieldException e) { + throw new RuntimeException(e.toString()); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Is invoked when public fields of the base-level + * class are modified and the runtime system intercepts it. + * This method simply sets the field to the given value. + * + *

    Every subclass of this class should redefine this method. + */ + public void trapFieldWrite(String name, Object value) { + Class jc = getClassMetaobject().getJavaClass(); + try { + jc.getField(name).set(getObject(), value); + } + catch (NoSuchFieldException e) { + throw new RuntimeException(e.toString()); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Is invoked when base-level method invocation is intercepted. + * This method simply executes the intercepted method invocation + * with the original parameters and returns the resulting value. + * + *

    Every subclass of this class should redefine this method. + * + *

    Note: this method is not invoked if the base-level method + * is invoked by a constructor in the super class. For example, + * + *

    + * + *

    if an instance of B is created, + * the invocation of initialize() in B is intercepted only once. + * The first invocation by the constructor in A is not intercepted. + * This is because the link between a base-level object and a + * metaobject is not created until the execution of a + * constructor of the super class finishes. + */ + public Object trapMethodcall(int identifier, Object[] args) + throws Throwable + { + try { + return methods[identifier].invoke(getObject(), args); + } + catch (java.lang.reflect.InvocationTargetException e) { + throw e.getTargetException(); + } + catch (java.lang.IllegalAccessException e) { + throw new CannotInvokeException(e); + } + } +} diff --git a/src/main/javassist/tools/reflect/Reflection.java b/src/main/javassist/tools/reflect/Reflection.java new file mode 100644 index 00000000..06ce2ccf --- /dev/null +++ b/src/main/javassist/tools/reflect/Reflection.java @@ -0,0 +1,384 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.reflect; + +import javassist.*; +import javassist.CtMethod.ConstParameter; + +/** + * The class implementing the behavioral reflection mechanism. + * + *

    If a class is reflective, + * then all the method invocations on every + * instance of that class are intercepted by the runtime + * metaobject controlling that instance. The methods inherited from the + * super classes are also intercepted except final methods. To intercept + * a final method in a super class, that super class must be also reflective. + * + *

    To do this, the original class file representing a reflective class: + * + *

    + * + *

    is modified so that it represents a class: + * + *

    + * + * @see javassist.tools.reflect.ClassMetaobject + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.Loader + * @see javassist.tools.reflect.Compiler + */ +public class Reflection implements Translator { + + static final String classobjectField = "_classobject"; + static final String classobjectAccessor = "_getClass"; + static final String metaobjectField = "_metaobject"; + static final String metaobjectGetter = "_getMetaobject"; + static final String metaobjectSetter = "_setMetaobject"; + static final String readPrefix = "_r_"; + static final String writePrefix = "_w_"; + + static final String metaobjectClassName = "javassist.tools.reflect.Metaobject"; + static final String classMetaobjectClassName + = "javassist.tools.reflect.ClassMetaobject"; + + protected CtMethod trapMethod, trapStaticMethod; + protected CtMethod trapRead, trapWrite; + protected CtClass[] readParam; + + protected ClassPool classPool; + protected CodeConverter converter; + + private boolean isExcluded(String name) { + return name.startsWith(ClassMetaobject.methodPrefix) + || name.equals(classobjectAccessor) + || name.equals(metaobjectSetter) + || name.equals(metaobjectGetter) + || name.startsWith(readPrefix) + || name.startsWith(writePrefix); + } + + /** + * Constructs a new Reflection object. + */ + public Reflection() { + classPool = null; + converter = new CodeConverter(); + } + + /** + * Initializes the object. + */ + public void start(ClassPool pool) throws NotFoundException { + classPool = pool; + final String msg + = "javassist.tools.reflect.Sample is not found or broken."; + try { + CtClass c = classPool.get("javassist.tools.reflect.Sample"); + trapMethod = c.getDeclaredMethod("trap"); + trapStaticMethod = c.getDeclaredMethod("trapStatic"); + trapRead = c.getDeclaredMethod("trapRead"); + trapWrite = c.getDeclaredMethod("trapWrite"); + readParam + = new CtClass[] { classPool.get("java.lang.Object") }; + } + catch (NotFoundException e) { + throw new RuntimeException(msg); + } + } + + /** + * Inserts hooks for intercepting accesses to the fields declared + * in reflective classes. + */ + public void onLoad(ClassPool pool, String classname) + throws CannotCompileException, NotFoundException + { + CtClass clazz = pool.get(classname); + clazz.instrument(converter); + } + + /** + * Produces a reflective class. + * If the super class is also made reflective, it must be done + * before the sub class. + * + * @param classname the name of the reflective class + * @param metaobject the class name of metaobjects. + * @param metaclass the class name of the class metaobject. + * @return false if the class is already reflective. + * + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.ClassMetaobject + */ + public boolean makeReflective(String classname, + String metaobject, String metaclass) + throws CannotCompileException, NotFoundException + { + return makeReflective(classPool.get(classname), + classPool.get(metaobject), + classPool.get(metaclass)); + } + + /** + * Produces a reflective class. + * If the super class is also made reflective, it must be done + * before the sub class. + * + * @param clazz the reflective class. + * @param metaobject the class of metaobjects. + * It must be a subclass of + * Metaobject. + * @param metaclass the class of the class metaobject. + * It must be a subclass of + * ClassMetaobject. + * @return false if the class is already reflective. + * + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.ClassMetaobject + */ + public boolean makeReflective(Class clazz, + Class metaobject, Class metaclass) + throws CannotCompileException, NotFoundException + { + return makeReflective(clazz.getName(), metaobject.getName(), + metaclass.getName()); + } + + /** + * Produces a reflective class. It modifies the given + * CtClass object and makes it reflective. + * If the super class is also made reflective, it must be done + * before the sub class. + * + * @param clazz the reflective class. + * @param metaobject the class of metaobjects. + * It must be a subclass of + * Metaobject. + * @param metaclass the class of the class metaobject. + * It must be a subclass of + * ClassMetaobject. + * @return false if the class is already reflective. + * + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.ClassMetaobject + */ + public boolean makeReflective(CtClass clazz, + CtClass metaobject, CtClass metaclass) + 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); + } + + /** + * Registers a reflective class. The field accesses to the instances + * of this class are instrumented. + */ + private void registerReflectiveClass(CtClass clazz) { + CtField[] fs = clazz.getDeclaredFields(); + for (int i = 0; i < fs.length; ++i) { + CtField f = fs[i]; + int mod = f.getModifiers(); + if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) { + String name = f.getName(); + converter.replaceFieldRead(f, clazz, readPrefix + name); + converter.replaceFieldWrite(f, clazz, writePrefix + name); + } + } + } + + private boolean modifyClassfile(CtClass clazz, CtClass metaobject, + CtClass metaclass) + throws CannotCompileException, NotFoundException + { + if (clazz.getAttribute("Reflective") != null) + return false; // this is already reflective. + else + clazz.setAttribute("Reflective", new byte[0]); + + CtClass mlevel = classPool.get("javassist.tools.reflect.Metalevel"); + boolean addMeta = !clazz.subtypeOf(mlevel); + if (addMeta) + clazz.addInterface(mlevel); + + processMethods(clazz, addMeta); + processFields(clazz); + + CtField f; + if (addMeta) { + f = new CtField(classPool.get("javassist.tools.reflect.Metaobject"), + metaobjectField, clazz); + f.setModifiers(Modifier.PROTECTED); + clazz.addField(f, CtField.Initializer.byNewWithParams(metaobject)); + + clazz.addMethod(CtNewMethod.getter(metaobjectGetter, f)); + clazz.addMethod(CtNewMethod.setter(metaobjectSetter, f)); + } + + f = new CtField(classPool.get("javassist.tools.reflect.ClassMetaobject"), + classobjectField, clazz); + f.setModifiers(Modifier.PRIVATE | Modifier.STATIC); + clazz.addField(f, CtField.Initializer.byNew(metaclass, + new String[] { clazz.getName() })); + + clazz.addMethod(CtNewMethod.getter(classobjectAccessor, f)); + return true; + } + + private void processMethods(CtClass clazz, boolean dontSearch) + throws CannotCompileException, NotFoundException + { + CtMethod[] ms = clazz.getMethods(); + for (int i = 0; i < ms.length; ++i) { + CtMethod m = ms[i]; + int mod = m.getModifiers(); + if (Modifier.isPublic(mod) && !Modifier.isAbstract(mod)) + processMethods0(mod, clazz, m, i, dontSearch); + } + } + + private void processMethods0(int mod, CtClass clazz, + CtMethod m, int identifier, boolean dontSearch) + throws CannotCompileException, NotFoundException + { + CtMethod body; + String name = m.getName(); + + if (isExcluded(name)) // internally-used method inherited + return; // from a reflective class. + + CtMethod m2; + if (m.getDeclaringClass() == clazz) { + if (Modifier.isNative(mod)) + return; + + m2 = m; + if (Modifier.isFinal(mod)) { + mod &= ~Modifier.FINAL; + m2.setModifiers(mod); + } + } + else { + if (Modifier.isFinal(mod)) + return; + + mod &= ~Modifier.NATIVE; + m2 = CtNewMethod.delegator(findOriginal(m, dontSearch), clazz); + m2.setModifiers(mod); + clazz.addMethod(m2); + } + + m2.setName(ClassMetaobject.methodPrefix + identifier + + "_" + name); + + if (Modifier.isStatic(mod)) + body = trapStaticMethod; + else + body = trapMethod; + + CtMethod wmethod + = CtNewMethod.wrapped(m.getReturnType(), name, + m.getParameterTypes(), m.getExceptionTypes(), + body, ConstParameter.integer(identifier), + clazz); + wmethod.setModifiers(mod); + clazz.addMethod(wmethod); + } + + private CtMethod findOriginal(CtMethod m, boolean dontSearch) + throws NotFoundException + { + if (dontSearch) + return m; + + String name = m.getName(); + CtMethod[] ms = m.getDeclaringClass().getDeclaredMethods(); + for (int i = 0; i < ms.length; ++i) { + String orgName = ms[i].getName(); + if (orgName.endsWith(name) + && orgName.startsWith(ClassMetaobject.methodPrefix) + && ms[i].getSignature().equals(m.getSignature())) + return ms[i]; + } + + return m; + } + + private void processFields(CtClass clazz) + throws CannotCompileException, NotFoundException + { + CtField[] fs = clazz.getDeclaredFields(); + for (int i = 0; i < fs.length; ++i) { + CtField f = fs[i]; + int mod = f.getModifiers(); + if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) { + mod |= Modifier.STATIC; + String name = f.getName(); + CtClass ftype = f.getType(); + CtMethod wmethod + = CtNewMethod.wrapped(ftype, readPrefix + name, + readParam, null, trapRead, + ConstParameter.string(name), + clazz); + wmethod.setModifiers(mod); + clazz.addMethod(wmethod); + CtClass[] writeParam = new CtClass[2]; + writeParam[0] = classPool.get("java.lang.Object"); + writeParam[1] = ftype; + wmethod = CtNewMethod.wrapped(CtClass.voidType, + writePrefix + name, + writeParam, null, trapWrite, + ConstParameter.string(name), clazz); + wmethod.setModifiers(mod); + clazz.addMethod(wmethod); + } + } + } +} diff --git a/src/main/javassist/tools/reflect/Sample.java b/src/main/javassist/tools/reflect/Sample.java new file mode 100644 index 00000000..4e70236a --- /dev/null +++ b/src/main/javassist/tools/reflect/Sample.java @@ -0,0 +1,56 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.reflect; + +/** + * A template used for defining a reflective class. + */ +public class Sample { + private Metaobject _metaobject; + private static ClassMetaobject _classobject; + + public Object trap(Object[] args, int identifier) throws Throwable { + Metaobject mobj; + mobj = _metaobject; + if (mobj == null) + return ClassMetaobject.invoke(this, identifier, args); + else + return mobj.trapMethodcall(identifier, args); + } + + public static Object trapStatic(Object[] args, int identifier) + throws Throwable + { + return _classobject.trapMethodcall(identifier, args); + } + + public static Object trapRead(Object[] args, String name) { + if (args[0] == null) + return _classobject.trapFieldRead(name); + else + return ((Metalevel)args[0])._getMetaobject().trapFieldRead(name); + } + + public static Object trapWrite(Object[] args, String name) { + Metalevel base = (Metalevel)args[0]; + if (base == null) + _classobject.trapFieldWrite(name, args[1]); + else + base._getMetaobject().trapFieldWrite(name, args[1]); + + return null; + } +} diff --git a/src/main/javassist/tools/reflect/package.html b/src/main/javassist/tools/reflect/package.html new file mode 100644 index 00000000..10a41964 --- /dev/null +++ b/src/main/javassist/tools/reflect/package.html @@ -0,0 +1,35 @@ + + +Runtime Behavioral Reflection. + +

    (also recently known as interceptors or AOP?) + +

    This package enables a metaobject to trap method calls and field +accesses on a regular Java object. It provides a class +Reflection, which is a main module for implementing +runtime behavioral reflection. +It also provides +a class Loader and Compiler +as utilities for dynamically or statically +translating a regular class into a reflective class. + +

    An instance of the reflective class is associated with +a runtime metaobject and a runtime class metaobject, which control +the behavior of that instance. +The runtime +metaobject is created for every (base-level) instance but the +runtime class metaobject is created for every (base-level) class. +Metaobject is the root class of the runtime +metaobject and ClassMetaobject is the root class +of the runtime class metaobject. + +

    This package is provided as a sample implementation of the +reflection mechanism with Javassist. All the programs in this package +uses only the regular Javassist API; they never call any hidden +methods. + +

    The most significant class in this package is Reflection. +See the description of this class first. + + + diff --git a/src/main/javassist/tools/rmi/AppletServer.java b/src/main/javassist/tools/rmi/AppletServer.java new file mode 100644 index 00000000..5056e24c --- /dev/null +++ b/src/main/javassist/tools/rmi/AppletServer.java @@ -0,0 +1,250 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.rmi; + +import java.io.*; + +import javassist.tools.web.*; +import javassist.CannotCompileException; +import javassist.NotFoundException; +import javassist.ClassPool; +import java.lang.reflect.Method; +import java.util.Hashtable; +import java.util.Vector; + +/** + * An AppletServer object is a web server that an ObjectImporter + * communicates with. It makes the objects specified by + * exportObject() remotely accessible from applets. + * If the classes of the exported objects are requested by the client-side + * JVM, this web server sends proxy classes for the requested classes. + * + * @see javassist.tools.rmi.ObjectImporter + */ +public class AppletServer extends Webserver { + private StubGenerator stubGen; + private Hashtable exportedNames; + private Vector exportedObjects; + + private static final byte[] okHeader + = "HTTP/1.0 200 OK\r\n\r\n".getBytes(); + + /** + * Constructs a web server. + * + * @param port port number + */ + public AppletServer(String port) + throws IOException, NotFoundException, CannotCompileException + { + this(Integer.parseInt(port)); + } + + /** + * Constructs a web server. + * + * @param port port number + */ + public AppletServer(int port) + throws IOException, NotFoundException, CannotCompileException + { + this(ClassPool.getDefault(), new StubGenerator(), port); + } + + /** + * Constructs a web server. + * + * @param port port number + * @param src the source of classs files. + */ + public AppletServer(int port, ClassPool src) + throws IOException, NotFoundException, CannotCompileException + { + this(new ClassPool(src), new StubGenerator(), port); + } + + private AppletServer(ClassPool loader, StubGenerator gen, int port) + throws IOException, NotFoundException, CannotCompileException + { + super(port); + exportedNames = new Hashtable(); + exportedObjects = new Vector(); + stubGen = gen; + addTranslator(loader, gen); + } + + /** + * Begins the HTTP service. + */ + public void run() { + super.run(); + } + + /** + * Exports an object. + * This method produces the bytecode of the proxy class used + * to access the exported object. A remote applet can load + * the proxy class and call a method on the exported object. + * + * @param name the name used for looking the object up. + * @param obj the exported object. + * @return the object identifier + * + * @see javassist.tools.rmi.ObjectImporter#lookupObject(String) + */ + public synchronized int exportObject(String name, Object obj) + throws CannotCompileException + { + Class clazz = obj.getClass(); + ExportedObject eo = new ExportedObject(); + eo.object = obj; + eo.methods = clazz.getMethods(); + exportedObjects.addElement(eo); + eo.identifier = exportedObjects.size() - 1; + if (name != null) + exportedNames.put(name, eo); + + try { + stubGen.makeProxyClass(clazz); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + + return eo.identifier; + } + + /** + * Processes a request from a web browser (an ObjectImporter). + */ + public void doReply(InputStream in, OutputStream out, String cmd) + throws IOException, BadHttpRequest + { + if (cmd.startsWith("POST /rmi ")) + processRMI(in, out); + else if (cmd.startsWith("POST /lookup ")) + lookupName(cmd, in, out); + else + super.doReply(in, out, cmd); + } + + private void processRMI(InputStream ins, OutputStream outs) + throws IOException + { + ObjectInputStream in = new ObjectInputStream(ins); + + int objectId = in.readInt(); + int methodId = in.readInt(); + Exception err = null; + Object rvalue = null; + try { + ExportedObject eo + = (ExportedObject)exportedObjects.elementAt(objectId); + Object[] args = readParameters(in); + rvalue = convertRvalue(eo.methods[methodId].invoke(eo.object, + args)); + } + catch(Exception e) { + err = e; + logging2(e.toString()); + } + + outs.write(okHeader); + ObjectOutputStream out = new ObjectOutputStream(outs); + if (err != null) { + out.writeBoolean(false); + out.writeUTF(err.toString()); + } + else + try { + out.writeBoolean(true); + out.writeObject(rvalue); + } + catch (NotSerializableException e) { + logging2(e.toString()); + } + catch (InvalidClassException e) { + logging2(e.toString()); + } + + out.flush(); + out.close(); + in.close(); + } + + private Object[] readParameters(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + int n = in.readInt(); + Object[] args = new Object[n]; + for (int i = 0; i < n; ++i) { + Object a = in.readObject(); + if (a instanceof RemoteRef) { + RemoteRef ref = (RemoteRef)a; + ExportedObject eo + = (ExportedObject)exportedObjects.elementAt(ref.oid); + a = eo.object; + } + + args[i] = a; + } + + return args; + } + + private Object convertRvalue(Object rvalue) + throws CannotCompileException + { + if (rvalue == null) + return null; // the return type is void. + + String classname = rvalue.getClass().getName(); + if (stubGen.isProxyClass(classname)) + return new RemoteRef(exportObject(null, rvalue), classname); + else + return rvalue; + } + + private void lookupName(String cmd, InputStream ins, OutputStream outs) + throws IOException + { + ObjectInputStream in = new ObjectInputStream(ins); + String name = DataInputStream.readUTF(in); + ExportedObject found = (ExportedObject)exportedNames.get(name); + outs.write(okHeader); + ObjectOutputStream out = new ObjectOutputStream(outs); + if (found == null) { + logging2(name + "not found."); + out.writeInt(-1); // error code + out.writeUTF("error"); + } + else { + logging2(name); + out.writeInt(found.identifier); + out.writeUTF(found.object.getClass().getName()); + } + + out.flush(); + out.close(); + in.close(); + } +} + +class ExportedObject { + public int identifier; + public Object object; + public Method[] methods; +} diff --git a/src/main/javassist/tools/rmi/ObjectImporter.java b/src/main/javassist/tools/rmi/ObjectImporter.java new file mode 100644 index 00000000..75f5aa60 --- /dev/null +++ b/src/main/javassist/tools/rmi/ObjectImporter.java @@ -0,0 +1,298 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.rmi; + +import java.io.*; +import java.net.*; +import java.applet.Applet; +import java.lang.reflect.*; + +/** + * The object importer enables applets to call a method on a remote + * object running on the Webserver (the main class of this + * package). + * + *

    To access the remote + * object, the applet first calls lookupObject() and + * obtains a proxy object, which is a reference to that object. + * The class name of the proxy object is identical to that of + * the remote object. + * The proxy object provides the same set of methods as the remote object. + * If one of the methods is invoked on the proxy object, + * the invocation is delegated to the remote object. + * From the viewpoint of the applet, therefore, the two objects are + * identical. The applet can access the object on the server + * with the regular Java syntax without concern about the actual + * location. + * + *

    The methods remotely called by the applet must be public. + * This is true even if the applet's class and the remote object's classs + * belong to the same package. + * + *

    If class X is a class of remote objects, a subclass of X must be + * also a class of remote objects. On the other hand, this restriction + * is not applied to the superclass of X. The class X does not have to + * contain a constructor taking no arguments. + * + *

    The parameters to a remote method is passed in the call-by-value + * manner. Thus all the parameter classes must implement + * java.io.Serializable. However, if the parameter is the + * proxy object, the reference to the remote object instead of a copy of + * the object is passed to the method. + * + *

    Because of the limitations of the current implementation, + *

    + * + *

    All the exceptions thrown by the remote object are converted + * into RemoteException. Since this exception is a subclass + * of RuntimeException, the caller method does not need + * to catch the exception. However, good programs should catch + * the RuntimeException. + * + * @see javassist.tools.rmi.AppletServer + * @see javassist.tools.rmi.RemoteException + * @see javassist.tools.web.Viewer + */ +public class ObjectImporter implements java.io.Serializable { + private final byte[] endofline = { 0x0d, 0x0a }; + private String servername, orgServername; + private int port, orgPort; + + protected byte[] lookupCommand = "POST /lookup HTTP/1.0".getBytes(); + protected byte[] rmiCommand = "POST /rmi HTTP/1.0".getBytes(); + + /** + * Constructs an object importer. + * + *

    Remote objects are imported from the web server that the given + * applet has been loaded from. + * + * @param applet the applet loaded from the Webserver. + */ + public ObjectImporter(Applet applet) { + URL codebase = applet.getCodeBase(); + orgServername = servername = codebase.getHost(); + orgPort = port = codebase.getPort(); + } + + /** + * Constructs an object importer. + * + *

    If you run a program with javassist.tools.web.Viewer, + * you can construct an object importer as follows: + * + *

    + * + * @see javassist.tools.web.Viewer + */ + public ObjectImporter(String servername, int port) { + this.orgServername = this.servername = servername; + this.orgPort = this.port = port; + } + + /** + * Finds the object exported by a server with the specified name. + * If the object is not found, this method returns null. + * + * @param name the name of the exported object. + * @return the proxy object or null. + */ + public Object getObject(String name) { + try { + return lookupObject(name); + } + catch (ObjectNotFoundException e) { + return null; + } + } + + /** + * Sets an http proxy server. After this method is called, the object + * importer connects a server through the http proxy server. + */ + public void setHttpProxy(String host, int port) { + String proxyHeader = "POST http://" + orgServername + ":" + orgPort; + String cmd = proxyHeader + "/lookup HTTP/1.0"; + lookupCommand = cmd.getBytes(); + cmd = proxyHeader + "/rmi HTTP/1.0"; + rmiCommand = cmd.getBytes(); + this.servername = host; + this.port = port; + } + + /** + * Finds the object exported by the server with the specified name. + * It sends a POST request to the server (via an http proxy server + * if needed). + * + * @param name the name of the exported object. + * @return the proxy object. + */ + public Object lookupObject(String name) throws ObjectNotFoundException + { + try { + Socket sock = new Socket(servername, port); + OutputStream out = sock.getOutputStream(); + out.write(lookupCommand); + out.write(endofline); + out.write(endofline); + + ObjectOutputStream dout = new ObjectOutputStream(out); + dout.writeUTF(name); + dout.flush(); + + InputStream in = new BufferedInputStream(sock.getInputStream()); + skipHeader(in); + ObjectInputStream din = new ObjectInputStream(in); + int n = din.readInt(); + String classname = din.readUTF(); + din.close(); + dout.close(); + sock.close(); + + if (n >= 0) + return createProxy(n, classname); + } + catch (Exception e) { + e.printStackTrace(); + throw new ObjectNotFoundException(name, e); + } + + throw new ObjectNotFoundException(name); + } + + private static final Class[] proxyConstructorParamTypes + = new Class[] { ObjectImporter.class, int.class }; + + private Object createProxy(int oid, String classname) throws Exception { + Class c = Class.forName(classname); + Constructor cons = c.getConstructor(proxyConstructorParamTypes); + return cons.newInstance(new Object[] { this, new Integer(oid) }); + } + + /** + * Calls a method on a remote object. + * It sends a POST request to the server (via an http proxy server + * if needed). + * + *

    This method is called by only proxy objects. + */ + public Object call(int objectid, int methodid, Object[] args) + throws RemoteException + { + boolean result; + Object rvalue; + String errmsg; + + try { + /* This method establishes a raw tcp connection for sending + * a POST message. Thus the object cannot communicate a + * remote object beyond a fire wall. To avoid this problem, + * the connection should be established with a mechanism + * collaborating a proxy server. Unfortunately, java.lang.URL + * does not seem to provide such a mechanism. + * + * You might think that using HttpURLConnection is a better + * way than constructing a raw tcp connection. Unfortunately, + * URL.openConnection() does not return an HttpURLConnection + * object in Netscape's JVM. It returns a + * netscape.net.URLConnection object. + * + * lookupObject() has the same problem. + */ + Socket sock = new Socket(servername, port); + OutputStream out = new BufferedOutputStream( + sock.getOutputStream()); + out.write(rmiCommand); + out.write(endofline); + out.write(endofline); + + ObjectOutputStream dout = new ObjectOutputStream(out); + dout.writeInt(objectid); + dout.writeInt(methodid); + writeParameters(dout, args); + dout.flush(); + + InputStream ins = new BufferedInputStream(sock.getInputStream()); + skipHeader(ins); + ObjectInputStream din = new ObjectInputStream(ins); + result = din.readBoolean(); + rvalue = null; + errmsg = null; + if (result) + rvalue = din.readObject(); + else + errmsg = din.readUTF(); + + din.close(); + dout.close(); + sock.close(); + + if (rvalue instanceof RemoteRef) { + RemoteRef ref = (RemoteRef)rvalue; + rvalue = createProxy(ref.oid, ref.classname); + } + } + catch (ClassNotFoundException e) { + throw new RemoteException(e); + } + catch (IOException e) { + throw new RemoteException(e); + } + catch (Exception e) { + throw new RemoteException(e); + } + + if (result) + return rvalue; + else + throw new RemoteException(errmsg); + } + + private void skipHeader(InputStream in) throws IOException { + int len; + do { + int c; + len = 0; + while ((c = in.read()) >= 0 && c != 0x0d) + ++len; + + in.read(); /* skip 0x0a (LF) */ + } while (len > 0); + } + + private void writeParameters(ObjectOutputStream dout, Object[] params) + throws IOException + { + int n = params.length; + dout.writeInt(n); + for (int i = 0; i < n; ++i) + if (params[i] instanceof Proxy) { + Proxy p = (Proxy)params[i]; + dout.writeObject(new RemoteRef(p._getObjectId())); + } + else + dout.writeObject(params[i]); + } +} diff --git a/src/main/javassist/tools/rmi/ObjectNotFoundException.java b/src/main/javassist/tools/rmi/ObjectNotFoundException.java new file mode 100644 index 00000000..e54a3257 --- /dev/null +++ b/src/main/javassist/tools/rmi/ObjectNotFoundException.java @@ -0,0 +1,26 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.rmi; + +public class ObjectNotFoundException extends Exception { + public ObjectNotFoundException(String name) { + super(name + " is not exported"); + } + + public ObjectNotFoundException(String name, Exception e) { + super(name + " because of " + e.toString()); + } +} diff --git a/src/main/javassist/tools/rmi/Proxy.java b/src/main/javassist/tools/rmi/Proxy.java new file mode 100644 index 00000000..1512b87f --- /dev/null +++ b/src/main/javassist/tools/rmi/Proxy.java @@ -0,0 +1,25 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.rmi; + +/** + * An interface implemented by proxy classes. + * + * @see javassist.tools.rmi.StubGenerator + */ +public interface Proxy { + int _getObjectId(); +} diff --git a/src/main/javassist/tools/rmi/RemoteException.java b/src/main/javassist/tools/rmi/RemoteException.java new file mode 100644 index 00000000..27ebe71e --- /dev/null +++ b/src/main/javassist/tools/rmi/RemoteException.java @@ -0,0 +1,30 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.rmi; + +/** + * RemoteException represents any exception thrown + * during remote method invocation. + */ +public class RemoteException extends RuntimeException { + public RemoteException(String msg) { + super(msg); + } + + public RemoteException(Exception e) { + super("by " + e.toString()); + } +} diff --git a/src/main/javassist/tools/rmi/RemoteRef.java b/src/main/javassist/tools/rmi/RemoteRef.java new file mode 100644 index 00000000..edbf349f --- /dev/null +++ b/src/main/javassist/tools/rmi/RemoteRef.java @@ -0,0 +1,35 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.rmi; + +/** + * Remote reference. This class is internally used for sending a remote + * reference through a network stream. + */ +public class RemoteRef implements java.io.Serializable { + public int oid; + public String classname; + + public RemoteRef(int i) { + oid = i; + classname = null; + } + + public RemoteRef(int i, String name) { + oid = i; + classname = name; + } +} diff --git a/src/main/javassist/tools/rmi/Sample.java b/src/main/javassist/tools/rmi/Sample.java new file mode 100644 index 00000000..a65b908d --- /dev/null +++ b/src/main/javassist/tools/rmi/Sample.java @@ -0,0 +1,36 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.rmi; + +/** + * A template used for defining a proxy class. + * The class file of this class is read by the StubGenerator + * class. + */ +public class Sample { + private ObjectImporter importer; + private int objectId; + + public Object forward(Object[] args, int identifier) { + return importer.call(objectId, identifier, args); + } + + public static Object forwardStatic(Object[] args, int identifier) + throws RemoteException + { + throw new RemoteException("cannot call a static method."); + } +} diff --git a/src/main/javassist/tools/rmi/StubGenerator.java b/src/main/javassist/tools/rmi/StubGenerator.java new file mode 100644 index 00000000..7681bd6d --- /dev/null +++ b/src/main/javassist/tools/rmi/StubGenerator.java @@ -0,0 +1,255 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.rmi; + +import javassist.*; +import java.lang.reflect.Method; +import java.util.Hashtable; +import javassist.CtMethod.ConstParameter; + +/** + * A stub-code generator. It is used for producing a proxy class. + * + *

    The proxy class for class A is as follows: + * + *

    + * + *

    Instances of the proxy class is created by an + * ObjectImporter object. + */ +public class StubGenerator implements Translator { + private static final String fieldImporter = "importer"; + private static final String fieldObjectId = "objectId"; + private static final String accessorObjectId = "_getObjectId"; + private static final String sampleClass = "javassist.tools.rmi.Sample"; + + private ClassPool classPool; + private Hashtable proxyClasses; + private CtMethod forwardMethod; + private CtMethod forwardStaticMethod; + + private CtClass[] proxyConstructorParamTypes; + private CtClass[] interfacesForProxy; + private CtClass[] exceptionForProxy; + + /** + * Constructs a stub-code generator. + */ + public StubGenerator() { + proxyClasses = new Hashtable(); + } + + /** + * Initializes the object. + * This is a method declared in javassist.Translator. + * + * @see javassist.Translator#start(ClassPool) + */ + public void start(ClassPool pool) throws NotFoundException { + classPool = pool; + CtClass c = pool.get(sampleClass); + forwardMethod = c.getDeclaredMethod("forward"); + forwardStaticMethod = c.getDeclaredMethod("forwardStatic"); + + proxyConstructorParamTypes + = pool.get(new String[] { "javassist.tools.rmi.ObjectImporter", + "int" }); + interfacesForProxy + = pool.get(new String[] { "java.io.Serializable", + "javassist.tools.rmi.Proxy" }); + exceptionForProxy + = new CtClass[] { pool.get("javassist.tools.rmi.RemoteException") }; + } + + /** + * Does nothing. + * This is a method declared in javassist.Translator. + * @see javassist.Translator#onLoad(ClassPool,String) + */ + public void onLoad(ClassPool pool, String classname) {} + + /** + * Returns true if the specified class is a proxy class + * recorded by makeProxyClass(). + * + * @param name a fully-qualified class name + */ + public boolean isProxyClass(String name) { + return proxyClasses.get(name) != null; + } + + /** + * Makes a proxy class. The produced class is substituted + * for the original class. + * + * @param clazz the class referenced + * through the proxy class. + * @return false if the proxy class + * has been already produced. + */ + public synchronized boolean makeProxyClass(Class clazz) + throws CannotCompileException, NotFoundException + { + String classname = clazz.getName(); + if (proxyClasses.get(classname) != null) + return false; + else { + CtClass ctclazz = produceProxyClass(classPool.get(classname), + clazz); + proxyClasses.put(classname, ctclazz); + modifySuperclass(ctclazz); + return true; + } + } + + private CtClass produceProxyClass(CtClass orgclass, Class orgRtClass) + throws CannotCompileException, NotFoundException + { + int modify = orgclass.getModifiers(); + if (Modifier.isAbstract(modify) || Modifier.isNative(modify) + || !Modifier.isPublic(modify)) + throw new CannotCompileException(orgclass.getName() + + " must be public, non-native, and non-abstract."); + + CtClass proxy = classPool.makeClass(orgclass.getName(), + orgclass.getSuperclass()); + + proxy.setInterfaces(interfacesForProxy); + + CtField f + = new CtField(classPool.get("javassist.tools.rmi.ObjectImporter"), + fieldImporter, proxy); + f.setModifiers(Modifier.PRIVATE); + proxy.addField(f, CtField.Initializer.byParameter(0)); + + f = new CtField(CtClass.intType, fieldObjectId, proxy); + f.setModifiers(Modifier.PRIVATE); + proxy.addField(f, CtField.Initializer.byParameter(1)); + + proxy.addMethod(CtNewMethod.getter(accessorObjectId, f)); + + proxy.addConstructor(CtNewConstructor.defaultConstructor(proxy)); + CtConstructor cons + = CtNewConstructor.skeleton(proxyConstructorParamTypes, + null, proxy); + proxy.addConstructor(cons); + + try { + addMethods(proxy, orgRtClass.getMethods()); + return proxy; + } + catch (SecurityException e) { + throw new CannotCompileException(e); + } + } + + private CtClass toCtClass(Class rtclass) throws NotFoundException { + String name; + if (!rtclass.isArray()) + name = rtclass.getName(); + else { + StringBuffer sbuf = new StringBuffer(); + do { + sbuf.append("[]"); + rtclass = rtclass.getComponentType(); + } while(rtclass.isArray()); + sbuf.insert(0, rtclass.getName()); + name = sbuf.toString(); + } + + return classPool.get(name); + } + + private CtClass[] toCtClass(Class[] rtclasses) throws NotFoundException { + int n = rtclasses.length; + CtClass[] ctclasses = new CtClass[n]; + for (int i = 0; i < n; ++i) + ctclasses[i] = toCtClass(rtclasses[i]); + + return ctclasses; + } + + /* ms must not be an array of CtMethod. To invoke a method ms[i] + * on a server, a client must send i to the server. + */ + private void addMethods(CtClass proxy, Method[] ms) + throws CannotCompileException, NotFoundException + { + CtMethod wmethod; + for (int i = 0; i < ms.length; ++i) { + Method m = ms[i]; + int mod = m.getModifiers(); + if (m.getDeclaringClass() != Object.class + && !Modifier.isFinal(mod)) + if (Modifier.isPublic(mod)) { + CtMethod body; + if (Modifier.isStatic(mod)) + body = forwardStaticMethod; + else + body = forwardMethod; + + wmethod + = CtNewMethod.wrapped(toCtClass(m.getReturnType()), + m.getName(), + toCtClass(m.getParameterTypes()), + exceptionForProxy, + body, + ConstParameter.integer(i), + proxy); + wmethod.setModifiers(mod); + proxy.addMethod(wmethod); + } + else if (!Modifier.isProtected(mod) + && !Modifier.isPrivate(mod)) + // if package method + throw new CannotCompileException( + "the methods must be public, protected, or private."); + } + } + + /** + * Adds a default constructor to the super classes. + */ + private void modifySuperclass(CtClass orgclass) + throws CannotCompileException, NotFoundException + { + CtClass superclazz; + for (;; orgclass = superclazz) { + superclazz = orgclass.getSuperclass(); + if (superclazz == null) + break; + + try { + superclazz.getDeclaredConstructor(null); + break; // the constructor with no arguments is found. + } + catch (NotFoundException e) { + } + + superclazz.addConstructor( + CtNewConstructor.defaultConstructor(superclazz)); + } + } +} diff --git a/src/main/javassist/tools/rmi/package.html b/src/main/javassist/tools/rmi/package.html new file mode 100644 index 00000000..5432a948 --- /dev/null +++ b/src/main/javassist/tools/rmi/package.html @@ -0,0 +1,16 @@ + + +Sample implementation of remote method invocation. + +

    This package enables applets to access remote objects +running on the web server with regular Java syntax. +It is provided as a sample implementation with Javassist. +All the programs in this package uses only the regular +Javassist API; they never call any hidden methods. + +

    The most significant class of this package is +ObjectImporter. +See the description of this class first. + + + diff --git a/src/main/javassist/tools/web/BadHttpRequest.java b/src/main/javassist/tools/web/BadHttpRequest.java new file mode 100644 index 00000000..606ce997 --- /dev/null +++ b/src/main/javassist/tools/web/BadHttpRequest.java @@ -0,0 +1,34 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.web; + +/** + * Thrown when receiving an invalid HTTP request. + */ +public class BadHttpRequest extends Exception { + private Exception e; + + public BadHttpRequest() { e = null; } + + public BadHttpRequest(Exception _e) { e = _e; } + + public String toString() { + if (e == null) + return super.toString(); + else + return e.toString(); + } +} diff --git a/src/main/javassist/tools/web/Viewer.java b/src/main/javassist/tools/web/Viewer.java new file mode 100644 index 00000000..6e737406 --- /dev/null +++ b/src/main/javassist/tools/web/Viewer.java @@ -0,0 +1,208 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.web; + +import java.io.*; +import java.net.*; + +/** + * A sample applet viewer. + * + *

    This is a sort of applet viewer that can run any program even if + * the main class is not a subclass of Applet. + * This viewwer first calls main() in the main class. + * + *

    To run, you should type: + * + *

    + * + *

    This command calls Main.main() with arg1,... + * All classes including Main are fetched from + * a server http://host:port. + * Only the class file for Viewer must exist + * on a local file system at the client side; even other + * javassist.* classes are not needed at the client side. + * Viewer uses only Java core API classes. + * + *

    Note: since a Viewer object is a class loader, + * a program loaded by this object can call a method in Viewer. + * For example, you can write something like this: + * + *

    + * + */ +public class Viewer extends ClassLoader { + private String server; + private int port; + + /** + * Starts a program. + */ + public static void main(String[] args) throws Throwable { + if (args.length >= 3) { + Viewer cl = new Viewer(args[0], Integer.parseInt(args[1])); + String[] args2 = new String[args.length - 3]; + System.arraycopy(args, 3, args2, 0, args.length - 3); + cl.run(args[2], args2); + } + else + System.err.println( + "Usage: java javassist.tools.web.Viewer class [args ...]"); + } + + /** + * Constructs a viewer. + * + * @param host server name + * @param p port number + */ + public Viewer(String host, int p) { + server = host; + port = p; + } + + /** + * Returns the server name. + */ + public String getServer() { return server; } + + /** + * Returns the port number. + */ + public int getPort() { return port; } + + /** + * Invokes main() in the class specified by classname. + * + * @param classname executed class + * @param args the arguments 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 synchronized Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + Class c = findLoadedClass(name); + if (c == null) + c = findClass(name); + + if (c == null) + throw new ClassNotFoundException(name); + + if (resolve) + resolveClass(c); + + return c; + } + + /** + * Finds the specified class. The implementation in this class + * fetches the class from the http server. If the class is + * either java.*, javax.*, or + * Viewer, then it is loaded by the parent class + * loader. + * + *

    This method can be overridden by a subclass of + * Viewer. + */ + protected Class findClass(String name) throws ClassNotFoundException { + Class c = null; + if (name.startsWith("java.") || name.startsWith("javax.") + || name.equals("javassist.tools.web.Viewer")) + c = findSystemClass(name); + + if (c == null) + try { + byte[] b = fetchClass(name); + if (b != null) + c = defineClass(name, b, 0, b.length); + } + catch (Exception e) { + } + + return c; + } + + /** + * Fetches the class file of the specified class from the http + * server. + */ + protected byte[] fetchClass(String classname) throws Exception + { + byte[] b; + URL url = new URL("http", server, port, + "/" + classname.replace('.', '/') + ".class"); + URLConnection con = url.openConnection(); + con.connect(); + int size = con.getContentLength(); + InputStream s = con.getInputStream(); + if (size <= 0) + b = readStream(s); + else { + b = new byte[size]; + int len = 0; + do { + int n = s.read(b, len, size - len); + if (n < 0) { + s.close(); + throw new IOException("the stream was closed: " + + classname); + } + len += n; + } while (len < size); + } + + s.close(); + return b; + } + + private byte[] readStream(InputStream fin) throws IOException { + byte[] buf = new byte[4096]; + int size = 0; + int len = 0; + do { + size += len; + if (buf.length - size <= 0) { + byte[] newbuf = new byte[buf.length * 2]; + System.arraycopy(buf, 0, newbuf, 0, size); + buf = newbuf; + } + + len = fin.read(buf, size, buf.length - size); + } while (len >= 0); + + byte[] result = new byte[size]; + System.arraycopy(buf, 0, result, 0, size); + return result; + } +} diff --git a/src/main/javassist/tools/web/Webserver.java b/src/main/javassist/tools/web/Webserver.java new file mode 100644 index 00000000..e580feca --- /dev/null +++ b/src/main/javassist/tools/web/Webserver.java @@ -0,0 +1,406 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.tools.web; + +import java.net.*; +import java.io.*; +import java.util.Date; +import javassist.*; + +/** + * A web server for running sample programs. + * + *

    This enables a Java program to instrument class files loaded by + * web browsers for applets. Since the (standard) security manager + * does not allow an applet to create and use a class loader, + * instrumenting class files must be done by this web server. + * + *

    Note: although this class is included in the Javassist API, + * it is provided as a sample implementation of the web server using + * Javassist. Especially, there might be security flaws in this server. + * Please use this with YOUR OWN RISK. + */ +public class Webserver { + private ServerSocket socket; + private ClassPool classPool; + protected Translator translator; + + private final static byte[] endofline = { 0x0d, 0x0a }; + private byte[] filebuffer = new byte[4096]; + + private final static int typeHtml = 1; + private final static int typeClass = 2; + private final static int typeGif = 3; + private final static int typeJpeg = 4; + private final static int typeText = 5; + + /** + * If this field is not null, the class files taken from + * ClassPool are written out under the directory + * specified by this field. The directory name must not end + * with a directory separator. + */ + public String debugDir = null; + + /** + * The top directory of html (and .gif, .class, ...) files. + * It must end with the directory separator such as "/". + * (For portability, "/" should be used as the directory separator. + * Javassist automatically translates "/" into a platform-dependent + * character.) + * If this field is null, the top directory is the current one where + * the JVM is running. + * + *

    If the given URL indicates a class file and the class file + * is not found under the directory specified by this variable, + * then Class.getResourceAsStream() is called + * for searching the Java class paths. + */ + public String htmlfileBase = null; + + /** + * Starts a web server. + * The port number is specified by the first argument. + */ + public static void main(String[] args) throws IOException { + if (args.length == 1) { + Webserver web = new Webserver(args[0]); + web.run(); + } + else + System.err.println( + "Usage: java javassist.tools.web.Webserver "); + } + + /** + * Constructs a web server. + * + * @param port port number + */ + public Webserver(String port) throws IOException { + this(Integer.parseInt(port)); + } + + /** + * Constructs a web server. + * + * @param port port number + */ + public Webserver(int port) throws IOException { + socket = new ServerSocket(port); + classPool = null; + translator = null; + } + + /** + * Requests the web server to use the specified + * ClassPool object for obtaining a class file. + */ + public void setClassPool(ClassPool loader) { + classPool = loader; + } + + /** + * Adds a translator, which is called whenever a client requests + * a class file. + * + * @param cp the ClassPool object for obtaining + * a class file. + * @param t a translator. + */ + public void addTranslator(ClassPool cp, Translator t) + throws NotFoundException, CannotCompileException + { + classPool = cp; + translator = t; + t.start(classPool); + } + + /** + * Closes the socket. + */ + public void end() throws IOException { + socket.close(); + } + + /** + * Prints a log message. + */ + public void logging(String msg) { + System.out.println(msg); + } + + /** + * Prints a log message. + */ + public void logging(String msg1, String msg2) { + System.out.print(msg1); + System.out.print(" "); + System.out.println(msg2); + } + + /** + * Prints a log message. + */ + public void logging(String msg1, String msg2, String msg3) { + System.out.print(msg1); + System.out.print(" "); + System.out.print(msg2); + System.out.print(" "); + System.out.println(msg3); + } + + /** + * Prints a log message with indentation. + */ + public void logging2(String msg) { + System.out.print(" "); + System.out.println(msg); + } + + /** + * Begins the HTTP service. + */ + public void run() { + System.err.println("ready to service..."); + for (;;) + try { + ServiceThread th = new ServiceThread(this, socket.accept()); + th.start(); + } + catch (IOException e) { + logging(e.toString()); + } + } + + final void process(Socket clnt) throws IOException { + InputStream in = new BufferedInputStream(clnt.getInputStream()); + String cmd = readLine(in); + logging(clnt.getInetAddress().getHostName(), + new Date().toString(), cmd); + while (skipLine(in) > 0){ + } + + OutputStream out = new BufferedOutputStream(clnt.getOutputStream()); + try { + doReply(in, out, cmd); + } + catch (BadHttpRequest e) { + replyError(out, e); + } + + out.flush(); + in.close(); + out.close(); + clnt.close(); + } + + private String readLine(InputStream in) throws IOException { + StringBuffer buf = new StringBuffer(); + int c; + while ((c = in.read()) >= 0 && c != 0x0d) + buf.append((char)c); + + in.read(); /* skip 0x0a (LF) */ + return buf.toString(); + } + + private int skipLine(InputStream in) throws IOException { + int c; + int len = 0; + while ((c = in.read()) >= 0 && c != 0x0d) + ++len; + + in.read(); /* skip 0x0a (LF) */ + return len; + } + + /** + * Proceses a HTTP request from a client. + * + * @param out the output stream to a client + * @param cmd the command received from a client + */ + public void doReply(InputStream in, OutputStream out, String cmd) + throws IOException, BadHttpRequest + { + int len; + int fileType; + String filename, urlName; + + if (cmd.startsWith("GET /")) + filename = urlName = cmd.substring(5, cmd.indexOf(' ', 5)); + else + throw new BadHttpRequest(); + + if (filename.endsWith(".class")) + fileType = typeClass; + else if (filename.endsWith(".html") || filename.endsWith(".htm")) + fileType = typeHtml; + else if (filename.endsWith(".gif")) + fileType = typeGif; + else if (filename.endsWith(".jpg")) + fileType = typeJpeg; + else + fileType = typeText; // or textUnknown + + len = filename.length(); + if (fileType == typeClass + && letUsersSendClassfile(out, filename, len)) + return; + + checkFilename(filename, len); + if (htmlfileBase != null) + filename = htmlfileBase + filename; + + if (File.separatorChar != '/') + filename = filename.replace('/', File.separatorChar); + + File file = new File(filename); + if (file.canRead()) { + sendHeader(out, file.length(), fileType); + FileInputStream fin = new FileInputStream(file); + for (;;) { + len = fin.read(filebuffer); + if (len <= 0) + break; + else + out.write(filebuffer, 0, len); + } + + fin.close(); + return; + } + + // If the file is not found under the html-file directory, + // then Class.getResourceAsStream() is tried. + + if (fileType == typeClass) { + InputStream fin + = getClass().getResourceAsStream("/" + urlName); + if (fin != null) { + ByteArrayOutputStream barray = new ByteArrayOutputStream(); + for (;;) { + len = fin.read(filebuffer); + if (len <= 0) + break; + else + barray.write(filebuffer, 0, len); + } + + byte[] classfile = barray.toByteArray(); + sendHeader(out, classfile.length, typeClass); + out.write(classfile); + fin.close(); + return; + } + } + + throw new BadHttpRequest(); + } + + private void checkFilename(String filename, int len) + throws BadHttpRequest + { + for (int i = 0; i < len; ++i) { + char c = filename.charAt(i); + if (!Character.isJavaIdentifierPart(c) && c != '.' && c != '/') + throw new BadHttpRequest(); + } + + if (filename.indexOf("..") >= 0) + throw new BadHttpRequest(); + } + + private boolean letUsersSendClassfile(OutputStream out, + String filename, int length) + throws IOException, BadHttpRequest + { + if (classPool == null) + return false; + + byte[] classfile; + String classname + = filename.substring(0, length - 6).replace('/', '.'); + try { + if (translator != null) + translator.onLoad(classPool, classname); + + CtClass c = classPool.get(classname); + classfile = c.toBytecode(); + if (debugDir != null) + c.writeFile(debugDir); + } + catch (Exception e) { + throw new BadHttpRequest(e); + } + + sendHeader(out, classfile.length, typeClass); + out.write(classfile); + return true; + } + + private void sendHeader(OutputStream out, long dataLength, int filetype) + throws IOException + { + out.write("HTTP/1.0 200 OK".getBytes()); + out.write(endofline); + out.write("Content-Length: ".getBytes()); + out.write(Long.toString(dataLength).getBytes()); + out.write(endofline); + if (filetype == typeClass) + out.write("Content-Type: application/octet-stream".getBytes()); + else if (filetype == typeHtml) + out.write("Content-Type: text/html".getBytes()); + else if (filetype == typeGif) + out.write("Content-Type: image/gif".getBytes()); + else if (filetype == typeJpeg) + out.write("Content-Type: image/jpg".getBytes()); + else if (filetype == typeText) + out.write("Content-Type: text/plain".getBytes()); + + out.write(endofline); + out.write(endofline); + } + + private void replyError(OutputStream out, BadHttpRequest e) + throws IOException + { + logging2("bad request: " + e.toString()); + out.write("HTTP/1.0 400 Bad Request".getBytes()); + out.write(endofline); + out.write(endofline); + out.write("

    Bad Request

    ".getBytes()); + } +} + +class ServiceThread extends Thread { + Webserver web; + Socket sock; + + public ServiceThread(Webserver w, Socket s) { + web = w; + sock = s; + } + + public void run() { + try { + web.process(sock); + } + catch (IOException e) { + } + } +} diff --git a/src/main/javassist/tools/web/package.html b/src/main/javassist/tools/web/package.html new file mode 100644 index 00000000..0c7fb453 --- /dev/null +++ b/src/main/javassist/tools/web/package.html @@ -0,0 +1,7 @@ + + +Simple web server for running sample code. + +

    This package provides a simple web server for sample packages. + + diff --git a/src/main/javassist/util/HotSwapper.java b/src/main/javassist/util/HotSwapper.java new file mode 100644 index 00000000..07abe7a0 --- /dev/null +++ b/src/main/javassist/util/HotSwapper.java @@ -0,0 +1,252 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.util; + +import com.sun.jdi.*; +import com.sun.jdi.connect.*; +import com.sun.jdi.event.*; +import com.sun.jdi.request.*; +import java.io.*; +import java.util.*; + +class Trigger { + void doSwap() {} +} + +/** + * A utility class for dynamically reloading a class by + * the Java Platform Debugger Architecture (JPDA), or HotSwap. + * It works only with JDK 1.4 and later. + * + *

    Note: The new definition of the reloaded class must declare + * the same set of methods and fields as the original definition. The + * schema change between the original and new definitions is not allowed + * by the JPDA. + * + *

    To use this class, the JVM must be launched with the following + * command line options: + * + *

      + *

      For Java 1.4,
      + *

      java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
      + *

      For Java 5,
      + *

      java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
      + *
    + * + *

    Note that 8000 is the port number used by HotSwapper. + * Any port number can be specified. Since HotSwapper does not + * launch another JVM for running a target application, this port number + * is used only for inter-thread communication. + * + *

    Furthermore, JAVA_HOME/lib/tools.jar must be included + * in the class path. + * + *

    Using HotSwapper is easy. See the following example: + * + *

      + * CtClass clazz = ...
      + * byte[] classFile = clazz.toBytecode();
      + * HotSwapper hs = new HostSwapper(8000);  // 8000 is a port number.
      + * hs.reload("Test", classFile);
      + * 
    + * + *

    reload() + * first unload the Test class and load a new version of + * the Test class. + * classFile is a byte array containing the new contents of + * the class file for the Test class. The developers can + * repatedly call reload() on the same HotSwapper + * object so that they can reload a number of classes. + * + * @since 3.1 + */ +public class HotSwapper { + private VirtualMachine jvm; + private MethodEntryRequest request; + private Map newClassFiles; + + private Trigger trigger; + + private static final String HOST_NAME = "localhost"; + private static final String TRIGGER_NAME = Trigger.class.getName(); + + /** + * Connects to the JVM. + * + * @param port the port number used for the connection to the JVM. + */ + public HotSwapper(int port) + throws IOException, IllegalConnectorArgumentsException + { + this(Integer.toString(port)); + } + + /** + * Connects to the JVM. + * + * @param port the port number used for the connection to the JVM. + */ + public HotSwapper(String port) + throws IOException, IllegalConnectorArgumentsException + { + jvm = null; + request = null; + newClassFiles = null; + trigger = new Trigger(); + AttachingConnector connector + = (AttachingConnector)findConnector("com.sun.jdi.SocketAttach"); + + Map arguments = connector.defaultArguments(); + ((Connector.Argument)arguments.get("hostname")).setValue(HOST_NAME); + ((Connector.Argument)arguments.get("port")).setValue(port); + jvm = connector.attach(arguments); + EventRequestManager manager = jvm.eventRequestManager(); + request = methodEntryRequests(manager, TRIGGER_NAME); + } + + private Connector findConnector(String connector) throws IOException { + List connectors = Bootstrap.virtualMachineManager().allConnectors(); + Iterator iter = connectors.iterator(); + while (iter.hasNext()) { + Connector con = (Connector)iter.next(); + if (con.name().equals(connector)) { + return con; + } + } + + throw new IOException("Not found: " + connector); + } + + private static MethodEntryRequest methodEntryRequests( + EventRequestManager manager, + String classpattern) { + MethodEntryRequest mereq = manager.createMethodEntryRequest(); + mereq.addClassFilter(classpattern); + mereq.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + return mereq; + } + + /* Stops triggering a hotswapper when reload() is called. + */ + private void deleteEventRequest(EventRequestManager manager, + MethodEntryRequest request) { + manager.deleteEventRequest(request); + } + + /** + * Reloads a class. + * + * @param className the fully-qualified class name. + * @param classFile the contents of the class file. + */ + public void reload(String className, byte[] classFile) { + ReferenceType classtype = toRefType(className); + Map map = new HashMap(); + map.put(classtype, classFile); + reload2(map, className); + } + + /** + * Reloads a class. + * + * @param classFiles a map between fully-qualified class names + * and class files. The type of the class names + * is String and the type of the + * class files is byte[]. + */ + public void reload(Map classFiles) { + Set set = classFiles.entrySet(); + Iterator it = set.iterator(); + Map map = new HashMap(); + String className = null; + while (it.hasNext()) { + Map.Entry e = (Map.Entry)it.next(); + className = (String)e.getKey(); + map.put(toRefType(className), e.getValue()); + } + + if (className != null) + reload2(map, className + " etc."); + } + + private ReferenceType toRefType(String className) { + List list = jvm.classesByName(className); + if (list == null || list.isEmpty()) + throw new RuntimeException("no such a class: " + className); + else + return (ReferenceType)list.get(0); + } + + private void reload2(Map map, String msg) { + synchronized (trigger) { + startDaemon(); + newClassFiles = map; + request.enable(); + trigger.doSwap(); + request.disable(); + Map ncf = newClassFiles; + if (ncf != null) { + newClassFiles = null; + throw new RuntimeException("failed to reload: " + msg); + } + } + } + + private void startDaemon() { + new Thread() { + private void errorMsg(Throwable e) { + System.err.print("Exception in thread \"HotSwap\" "); + e.printStackTrace(System.err); + } + + public void run() { + EventSet events = null; + try { + events = waitEvent(); + EventIterator iter = events.eventIterator(); + while (iter.hasNext()) { + Event event = iter.nextEvent(); + if (event instanceof MethodEntryEvent) { + hotswap(); + break; + } + } + } + catch (Throwable e) { + errorMsg(e); + } + try { + if (events != null) + events.resume(); + } + catch (Throwable e) { + errorMsg(e); + } + } + }.start(); + } + + EventSet waitEvent() throws InterruptedException { + EventQueue queue = jvm.eventQueue(); + return queue.remove(); + } + + void hotswap() { + Map map = newClassFiles; + jvm.redefineClasses(map); + newClassFiles = null; + } +} diff --git a/src/main/javassist/util/package.html b/src/main/javassist/util/package.html new file mode 100644 index 00000000..349d9962 --- /dev/null +++ b/src/main/javassist/util/package.html @@ -0,0 +1,5 @@ + + +Utility classes. + + diff --git a/src/main/javassist/util/proxy/FactoryHelper.java b/src/main/javassist/util/proxy/FactoryHelper.java new file mode 100644 index 00000000..01bd6bf1 --- /dev/null +++ b/src/main/javassist/util/proxy/FactoryHelper.java @@ -0,0 +1,182 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.util.proxy; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import javassist.CannotCompileException; +import javassist.bytecode.ClassFile; + +/** + * A helper class for implementing ProxyFactory. + * The users of ProxyFactory do not have to see this class. + * + * @see ProxyFactory + */ +public class FactoryHelper { + /** + * Returns an index for accessing arrays in this class. + * + * @throws RuntimeException if a given type is not a primitive type. + */ + public static final int typeIndex(Class type) { + Class[] list = primitiveTypes; + int n = list.length; + for (int i = 0; i < n; i++) + if (list[i] == type) + return i; + + throw new RuntimeException("bad type:" + type.getName()); + } + + /** + * Class objects representing primitive types. + */ + public static final Class[] primitiveTypes = { + Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE, + Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE + }; + + /** + * The fully-qualified names of wrapper classes for primitive types. + */ + public static final String[] wrapperTypes = { + "java.lang.Boolean", "java.lang.Byte", "java.lang.Character", + "java.lang.Short", "java.lang.Integer", "java.lang.Long", + "java.lang.Float", "java.lang.Double", "java.lang.Void" + }; + + /** + * The descriptors of the constructors of wrapper classes. + */ + public static final String[] wrapperDesc = { + "(Z)V", "(B)V", "(C)V", "(S)V", "(I)V", "(J)V", + "(F)V", "(D)V" + }; + + /** + * The names of methods for obtaining a primitive value + * from a wrapper object. For example, intValue() + * is such a method for obtaining an integer value from a + * java.lang.Integer object. + */ + public static final String[] unwarpMethods = { + "booleanValue", "byteValue", "charValue", "shortValue", + "intValue", "longValue", "floatValue", "doubleValue" + }; + + /** + * The descriptors of the unwrapping methods contained + * in unwrapMethods. + */ + public static final String[] unwrapDesc = { + "()Z", "()B", "()C", "()S", "()I", "()J", "()F", "()D" + }; + + /** + * The data size of primitive types. long + * and double are 2; the others are 1. + */ + public static final int[] dataSize = { + 1, 1, 1, 1, 1, 2, 1, 2 + }; + + /** + * Loads a class file by a given class loader. + */ + public static Class toClass(ClassFile cf, ClassLoader loader) + throws CannotCompileException + { + try { + byte[] b = toBytecode(cf); + Class cl = Class.forName("java.lang.ClassLoader"); + java.lang.reflect.Method method = cl.getDeclaredMethod( + "defineClass", new Class[] { String.class, byte[].class, + Integer.TYPE, Integer.TYPE }); + method.setAccessible(true); + Object[] args = new Object[] { cf.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); + } + } + + private static byte[] toBytecode(ClassFile cf) throws IOException { + ByteArrayOutputStream barray = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(barray); + try { + cf.write(out); + } + finally { + out.close(); + } + + return barray.toByteArray(); + } + + /** + * Writes a class file. + */ + public static void writeFile(ClassFile cf, String directoryName) + throws CannotCompileException { + try { + writeFile0(cf, directoryName); + } + catch (IOException e) { + throw new CannotCompileException(e); + } + } + + private static void writeFile0(ClassFile cf, String directoryName) + throws CannotCompileException, IOException { + String classname = cf.getName(); + String filename = directoryName + File.separatorChar + + classname.replace('.', File.separatorChar) + ".class"; + int pos = filename.lastIndexOf(File.separatorChar); + if (pos > 0) { + String dir = filename.substring(0, pos); + if (!dir.equals(".")) + new File(dir).mkdirs(); + } + + DataOutputStream out = new DataOutputStream(new BufferedOutputStream( + new FileOutputStream(filename))); + try { + cf.write(out); + } + catch (IOException e) { + throw e; + } + finally { + out.close(); + } + } +} diff --git a/src/main/javassist/util/proxy/MethodFilter.java b/src/main/javassist/util/proxy/MethodFilter.java new file mode 100644 index 00000000..67f9e409 --- /dev/null +++ b/src/main/javassist/util/proxy/MethodFilter.java @@ -0,0 +1,30 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.util.proxy; + +import java.lang.reflect.Method; + +/** + * Selector of the methods implemented by a handler. + * + * @see ProxyFactory#setFilter(MethodFilter) + */ +public interface MethodFilter { + /** + * Returns true if the given method is implemented by a handler. + */ + boolean isHandled(Method m); +} diff --git a/src/main/javassist/util/proxy/MethodHandler.java b/src/main/javassist/util/proxy/MethodHandler.java new file mode 100644 index 00000000..7bd28ba0 --- /dev/null +++ b/src/main/javassist/util/proxy/MethodHandler.java @@ -0,0 +1,48 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.util.proxy; + +import java.lang.reflect.Method; + +/** + * The interface implemented by the invocation handler of a proxy + * instance. + * + * @see ProxyFactory#setHandler(MethodHandler) + */ +public interface MethodHandler { + /** + * Is called when a method is invoked on a proxy instance associated + * with this handler. This method must process that method invocation. + * + * @param self the proxy instance. + * @param thisMethod the overridden method declared in the super + * class or interface. + * @param proceed the forwarder method for invoking the overridden + * method. It is null if the overridden mehtod is + * abstract or declared in the interface. + * @param args an array of objects containing the values of + * the arguments passed in the method invocation + * on the proxy instance. If a parameter type is + * a primitive type, the type of the array element + * is a wrapper class. + * @return the resulting value of the method invocation. + * + * @throws Exception if the method invocation fails. + */ + Object invoke(Object self, Method thisMethod, Method proceed, + Object[] args) throws Exception; +} diff --git a/src/main/javassist/util/proxy/ProxyFactory.java b/src/main/javassist/util/proxy/ProxyFactory.java new file mode 100644 index 00000000..356c23bf --- /dev/null +++ b/src/main/javassist/util/proxy/ProxyFactory.java @@ -0,0 +1,699 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.util.proxy; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javassist.CannotCompileException; +import javassist.bytecode.*; + +/** + * Factory of dynamic proxy classes. + * + *

    This factory generates a class that extends the given super class and implements + * the given interfaces. The calls of the methods inherited from the super class are + * forwarded and then invoke() is called on the method handler + * associated with the generated class. The calls of the methods from the interfaces + * are also forwarded to the method handler. + * + *

    For example, if the following code is executed, + * + *

      + * ProxyFactory f = new ProxyFactory();
      + * f.setSuperclass(Foo.class);
      + * MethodHandler mi = new MethodHandler() {
      + *     public Object invoke(Object self, Method m, Method proceed,
      + *                          Object[] args) throws Exception {
      + *         System.out.println("Name: " + m.getName());
      + *         proceed.invoke(self, args);  // execute the original method.
      + *     }
      + * };
      + * f.setHandler(mi);
      + * Class c = f.createClass();
      + * Foo foo = (Foo)c.newInstance();
      + * 
    + * + *

    Then, the following method call will be forwarded to MethodHandler + * mi and prints a message before executing the originally called method + * bar() in Foo. + * + *

      + * foo.bar();
      + * 
    + * + *

    To change the method handler during runtime, + * execute the following code: + * + *

      + * MethodHandler mi2 = ... ;    // another handler
      + * ((ProxyObject)foo).setHandler(mi2);
      + * 
    + * + *

    Here is an example of method handler. It does not execute + * anything except invoking the original method: + * + *

      + * class SimpleHandler implements MethodHandler {
      + *     public Object invoke(Object self, Method m,
      + *                          Method proceed, Object[] args) throws Exception {
      + *         return proceed.invoke(self, args);
      + *     }
      + * }
      + * 
    + * + * @see MethodHandler + * @since 3.1 + */ +public class ProxyFactory { + private Class superClass; + private Class[] interfaces; + private MethodFilter methodFilter; + private MethodHandler handler; + private Class thisClass; + + /** + * If the value of this variable is not null, the class file of + * the generated proxy class is written under the directory specified + * by this variable. For example, if the value is + * ".", then the class file is written under the current + * directory. This method is for debugging. + * + *

    The default value is null. + */ + public String writeDirectory; + + private static final Class OBJECT_TYPE = Object.class; + + private static final String HOLDER = "_methods_"; + private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;"; + private static final String HANDLER = "handler"; + private static final String DEFAULT_INTERCEPTOR = "default_interceptor"; + private static final String HANDLER_TYPE + = 'L' + MethodHandler.class.getName().replace('.', '/') + ';'; + private static final String HANDLER_SETTER = "setHandler"; + private static final String HANDLER_SETTER_TYPE = "(" + HANDLER_TYPE + ")V"; + + /** + * Constructs a factory of proxy class. + */ + public ProxyFactory() { + superClass = null; + interfaces = null; + methodFilter = null; + handler = new MethodHandler() { + public Object invoke(Object self, Method m, + Method proceed, Object[] args) + throws Exception + { + return proceed.invoke(self, args); + } + }; + thisClass = null; + writeDirectory = null; + } + + /** + * Sets the super class of a proxy class. + */ + public void setSuperclass(Class clazz) { + superClass = clazz; + } + + /** + * Sets the interfaces of a proxy class. + */ + public void setInterfaces(Class[] ifs) { + interfaces = ifs; + } + + /** + * Sets a filter that selects the methods that will be controlled by a handler. + */ + public void setFilter(MethodFilter mf) { + methodFilter = mf; + } + + /** + * Generates a proxy class. + */ + public Class createClass() { + if (thisClass == null) + try { + ClassFile cf = make(); + ClassLoader cl = getClassLoader(); + if (writeDirectory != null) + FactoryHelper.writeFile(cf, writeDirectory); + + thisClass = FactoryHelper.toClass(cf, cl); + setHandler(); + } + catch (CannotCompileException e) { + throw new RuntimeException(e.getMessage(), e); + } + + return thisClass; + } + + protected ClassLoader getClassLoader() { + if (superClass != null && !superClass.getName().equals("java.lang.Object")) + return superClass.getClassLoader(); + else if (interfaces != null && interfaces.length > 0) + return interfaces[0].getClassLoader(); + else + return this.getClass().getClassLoader(); + // return Thread.currentThread().getContextClassLoader(); + } + + /** + * Sets the default invocation handler. This invocation handler is shared + * among all the instances of a proxy class unless another is explicitly + * specified. + */ + public void setHandler(MethodHandler mi) { + handler = mi; + setHandler(); + } + + private void setHandler() { + if (thisClass != null && handler != null) + try { + Field f = thisClass.getField(DEFAULT_INTERCEPTOR); + f.setAccessible(true); + f.set(null, handler); + f.setAccessible(false); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static int counter = 0; + + private ClassFile make() throws CannotCompileException { + String superName, classname; + if (interfaces == null) + interfaces = new Class[0]; + + if (superClass == null) { + superClass = OBJECT_TYPE; + superName = superClass.getName(); + classname = interfaces.length == 0 ? superName + : interfaces[0].getName(); + } + else { + superName = superClass.getName(); + classname = superName; + } + + if (Modifier.isFinal(superClass.getModifiers())) + throw new CannotCompileException(superName + " is final"); + + // generate a proxy name. + classname = classname + "_$$_javassist_" + counter++; + if (classname.startsWith("java.")) + classname = "org.javassist.tmp." + classname; + + ClassFile cf = new ClassFile(false, classname, superName); + cf.setAccessFlags(AccessFlag.PUBLIC); + setInterfaces(cf, interfaces); + ConstPool pool = cf.getConstPool(); + FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR, HANDLER_TYPE); + finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); + cf.addField(finfo); + + FieldInfo finfo2 = new FieldInfo(pool, HANDLER, HANDLER_TYPE); + finfo2.setAccessFlags(AccessFlag.PRIVATE); + cf.addField(finfo2); + + HashMap allMethods = getMethods(superClass, interfaces); + int size = allMethods.size(); + makeConstructors(classname, cf, pool, classname); + int s = overrideMethods(cf, pool, classname, allMethods); + addMethodsHolder(cf, pool, classname, s); + addSetter(classname, cf, pool); + + thisClass = null; + return cf; + } + + private static void setInterfaces(ClassFile cf, Class[] interfaces) { + String setterIntf = ProxyObject.class.getName(); + String[] list; + if (interfaces == null || interfaces.length == 0) + list = new String[] { setterIntf }; + else { + list = new String[interfaces.length + 1]; + for (int i = 0; i < interfaces.length; i++) + list[i] = interfaces[i].getName(); + + list[interfaces.length] = setterIntf; + } + + cf.setInterfaces(list); + } + + private static void addMethodsHolder(ClassFile cf, ConstPool cp, + String classname, int size) + throws CannotCompileException + { + FieldInfo finfo = new FieldInfo(cp, HOLDER, HOLDER_TYPE); + finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.STATIC); + cf.addField(finfo); + MethodInfo minfo = new MethodInfo(cp, "", "()V"); + Bytecode code = new Bytecode(cp, 0, 0); + code.addIconst(size * 2); + code.addAnewarray("java.lang.reflect.Method"); + code.addPutstatic(classname, HOLDER, HOLDER_TYPE); + code.addOpcode(Bytecode.RETURN); + minfo.setCodeAttribute(code.toCodeAttribute()); + cf.addMethod(minfo); + } + + private static void addSetter(String classname, ClassFile cf, ConstPool cp) + throws CannotCompileException + { + MethodInfo minfo = new MethodInfo(cp, HANDLER_SETTER, + HANDLER_SETTER_TYPE); + minfo.setAccessFlags(AccessFlag.PUBLIC); + Bytecode code = new Bytecode(cp, 2, 2); + code.addAload(0); + code.addAload(1); + code.addPutfield(classname, HANDLER, HANDLER_TYPE); + code.addOpcode(Bytecode.RETURN); + minfo.setCodeAttribute(code.toCodeAttribute()); + cf.addMethod(minfo); + } + + private int overrideMethods(ClassFile cf, ConstPool cp, String className, + HashMap allMethods) + throws CannotCompileException + { + String prefix = makeUniqueName("_d", allMethods); + Set entries = allMethods.entrySet(); + Iterator it = entries.iterator(); + int index = 0; + while (it.hasNext()) { + Map.Entry e = (Map.Entry)it.next(); + String key = (String)e.getKey(); + Method meth = (Method)e.getValue(); + int mod = meth.getModifiers(); + if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod) + && isVisible(mod, className, meth)) + if (methodFilter == null || methodFilter.isHandled(meth)) + override(className, meth, prefix, index++, + keyToDesc(key), cf, cp); + } + + return index; + } + + private void override(String thisClassname, Method meth, String prefix, + int index, String desc, ClassFile cf, ConstPool cp) + throws CannotCompileException + { + Class declClass = meth.getDeclaringClass(); + String delegatorName = prefix + index + meth.getName(); + if (Modifier.isAbstract(meth.getModifiers())) + delegatorName = null; + else { + MethodInfo delegator + = makeDelegator(meth, desc, cp, declClass, delegatorName); + cf.addMethod(delegator); + } + + MethodInfo forwarder + = makeForwarder(thisClassname, meth, desc, cp, declClass, + delegatorName, index); + cf.addMethod(forwarder); + } + + private void makeConstructors(String thisClassName, ClassFile cf, + ConstPool cp, String classname) throws CannotCompileException + { + Constructor[] cons = superClass.getDeclaredConstructors(); + for (int i = 0; i < cons.length; i++) { + Constructor c = cons[i]; + int mod = c.getModifiers(); + if (!Modifier.isFinal(mod) && !Modifier.isPrivate(mod) + && isVisible(mod, classname, c)) { + MethodInfo m = makeConstructor(thisClassName, c, cp, superClass); + cf.addMethod(m); + } + } + } + + private static String makeUniqueName(String name, HashMap hash) { + Set keys = hash.keySet(); + if (makeUniqueName0(name, keys.iterator())) + return name; + + for (int i = 100; i < 999; i++) { + String s = name + i; + if (makeUniqueName0(s, keys.iterator())) + return s; + } + + throw new RuntimeException("cannot make a unique method name"); + } + + private static boolean makeUniqueName0(String name, Iterator it) { + while (it.hasNext()) { + String key = (String)it.next(); + if (key.startsWith(name)) + return false; + } + + return true; + } + + /** + * Returns true if the method is visible from the class. + * + * @param mod the modifiers of the method. + */ + private static boolean isVisible(int mod, String from, Member meth) { + if ((mod & Modifier.PRIVATE) != 0) + return false; + else if ((mod & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) + return true; + else { + String p = getPackageName(from); + String q = getPackageName(meth.getDeclaringClass().getName()); + if (p == null) + return q == null; + else + return p.equals(q); + } + } + + private static String getPackageName(String name) { + int i = name.lastIndexOf('.'); + if (i < 0) + return null; + else + return name.substring(0, i); + } + + private static HashMap getMethods(Class superClass, Class[] interfaceTypes) { + HashMap hash = new HashMap(); + for (int i = 0; i < interfaceTypes.length; i++) + getMethods(hash, interfaceTypes[i]); + + getMethods(hash, superClass); + return hash; + } + + private static void getMethods(HashMap hash, Class clazz) { + Class[] ifs = clazz.getInterfaces(); + for (int i = 0; i < ifs.length; i++) + getMethods(hash, ifs[i]); + + Class parent = clazz.getSuperclass(); + if (parent != null) + getMethods(hash, parent); + + Method[] methods = clazz.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) + if (!Modifier.isPrivate(methods[i].getModifiers())) { + Method m = methods[i]; + String key = m.getName() + ':' + RuntimeSupport.makeDescriptor(m); + hash.put(key, methods[i]); + } + } + + private static String keyToDesc(String key) { + return key.substring(key.indexOf(':') + 1); + } + + private static MethodInfo makeConstructor(String thisClassName, Constructor cons, + ConstPool cp, Class superClass) { + String desc = RuntimeSupport.makeDescriptor(cons.getParameterTypes(), + Void.TYPE); + MethodInfo minfo = new MethodInfo(cp, "", desc); + minfo.setAccessFlags(Modifier.PUBLIC); // cons.getModifiers() & ~Modifier.NATIVE + setThrows(minfo, cp, cons.getExceptionTypes()); + Bytecode code = new Bytecode(cp, 0, 0); + code.addAload(0); + code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE); + code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE); + code.addAload(0); + int s = addLoadParameters(code, cons.getParameterTypes(), 1); + code.addInvokespecial(superClass.getName(), "", desc); + code.addOpcode(Opcode.RETURN); + code.setMaxLocals(++s); + minfo.setCodeAttribute(code.toCodeAttribute()); + return minfo; + } + + private static MethodInfo makeDelegator(Method meth, String desc, + ConstPool cp, Class declClass, String delegatorName) { + MethodInfo delegator = new MethodInfo(cp, delegatorName, desc); + delegator.setAccessFlags(Modifier.FINAL | Modifier.PUBLIC + | (meth.getModifiers() & ~(Modifier.PRIVATE + | Modifier.PROTECTED + | Modifier.ABSTRACT + | Modifier.NATIVE + | Modifier.SYNCHRONIZED))); + setThrows(delegator, cp, meth); + Bytecode code = new Bytecode(cp, 0, 0); + code.addAload(0); + int s = addLoadParameters(code, meth.getParameterTypes(), 1); + code.addInvokespecial(declClass.getName(), meth.getName(), desc); + addReturn(code, meth.getReturnType()); + code.setMaxLocals(++s); + delegator.setCodeAttribute(code.toCodeAttribute()); + return delegator; + } + + /** + * @param delegatorName null if the original method is abstract. + */ + private static MethodInfo makeForwarder(String thisClassName, + Method meth, String desc, ConstPool cp, + Class declClass, String delegatorName, int index) { + MethodInfo forwarder = new MethodInfo(cp, meth.getName(), desc); + forwarder.setAccessFlags(Modifier.FINAL + | (meth.getModifiers() & ~(Modifier.ABSTRACT + | Modifier.NATIVE + | Modifier.SYNCHRONIZED))); + setThrows(forwarder, cp, meth); + int args = Descriptor.paramSize(desc); + Bytecode code = new Bytecode(cp, 0, args + 2); + /* + * if (methods[index * 2] == null) { + * methods[index * 2] + * = RuntimeSupport.findMethod(this, , ); + * methods[index * 2 + 1] + * = RuntimeSupport.findMethod(this, , ); + * or = null // the original method is abstract. + * } + * return ($r)handler.invoke(this, methods[index * 2], + * methods[index * 2 + 1], $args); + */ + int origIndex = index * 2; + int delIndex = index * 2 + 1; + int arrayVar = args + 1; + code.addGetstatic(thisClassName, HOLDER, HOLDER_TYPE); + code.addAstore(arrayVar); + code.addAload(arrayVar); + code.addIconst(origIndex); + code.addOpcode(Opcode.AALOAD); + code.addOpcode(Opcode.IFNONNULL); + int pc = code.currentPc(); + code.addIndex(0); + + callFindMethod(code, "findSuperMethod", arrayVar, origIndex, meth.getName(), desc); + callFindMethod(code, "findMethod", arrayVar, delIndex, delegatorName, desc); + + code.write16bit(pc, code.currentPc() - pc + 1); + code.addAload(0); + code.addGetfield(thisClassName, HANDLER, HANDLER_TYPE); + code.addAload(0); + + code.addAload(arrayVar); + code.addIconst(origIndex); + code.addOpcode(Opcode.AALOAD); + + code.addAload(arrayVar); + code.addIconst(delIndex); + code.addOpcode(Opcode.AALOAD); + + makeParameterList(code, meth.getParameterTypes()); + code.addInvokeinterface(MethodHandler.class.getName(), "invoke", + "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", + 5); + Class retType = meth.getReturnType(); + addUnwrapper(code, retType); + addReturn(code, retType); + + forwarder.setCodeAttribute(code.toCodeAttribute()); + return forwarder; + } + + private static void setThrows(MethodInfo minfo, ConstPool cp, Method orig) { + Class[] exceptions = orig.getExceptionTypes(); + setThrows(minfo, cp, exceptions); + } + + private static void setThrows(MethodInfo minfo, ConstPool cp, + Class[] exceptions) { + if (exceptions.length == 0) + return; + + String[] list = new String[exceptions.length]; + for (int i = 0; i < exceptions.length; i++) + list[i] = exceptions[i].getName(); + + ExceptionsAttribute ea = new ExceptionsAttribute(cp); + ea.setExceptions(list); + minfo.setExceptionsAttribute(ea); + } + + private static int addLoadParameters(Bytecode code, Class[] params, + int offset) { + int stacksize = 0; + int n = params.length; + for (int i = 0; i < n; ++i) + stacksize += addLoad(code, stacksize + offset, params[i]); + + return stacksize; + } + + private static int addLoad(Bytecode code, int n, Class type) { + if (type.isPrimitive()) { + if (type == Long.TYPE) { + code.addLload(n); + return 2; + } + else if (type == Float.TYPE) + code.addFload(n); + else if (type == Double.TYPE) { + code.addDload(n); + return 2; + } + else + code.addIload(n); + } + else + code.addAload(n); + + return 1; + } + + private static int addReturn(Bytecode code, Class type) { + if (type.isPrimitive()) { + if (type == Long.TYPE) { + code.addOpcode(Opcode.LRETURN); + return 2; + } + else if (type == Float.TYPE) + code.addOpcode(Opcode.FRETURN); + else if (type == Double.TYPE) { + code.addOpcode(Opcode.DRETURN); + return 2; + } + else if (type == Void.TYPE) { + code.addOpcode(Opcode.RETURN); + return 0; + } + else + code.addOpcode(Opcode.IRETURN); + } + else + code.addOpcode(Opcode.ARETURN); + + return 1; + } + + private static void makeParameterList(Bytecode code, Class[] params) { + int regno = 1; + int n = params.length; + code.addIconst(n); + code.addAnewarray("java/lang/Object"); + for (int i = 0; i < n; i++) { + code.addOpcode(Opcode.DUP); + code.addIconst(i); + Class type = params[i]; + if (type.isPrimitive()) + regno = makeWrapper(code, type, regno); + else { + code.addAload(regno); + regno++; + } + + code.addOpcode(Opcode.AASTORE); + } + } + + private static int makeWrapper(Bytecode code, Class type, int regno) { + int index = FactoryHelper.typeIndex(type); + String wrapper = FactoryHelper.wrapperTypes[index]; + code.addNew(wrapper); + code.addOpcode(Opcode.DUP); + addLoad(code, regno, type); + code.addInvokespecial(wrapper, "", + FactoryHelper.wrapperDesc[index]); + return regno + FactoryHelper.dataSize[index]; + } + + /** + * @param methodName might be null. + */ + private static void callFindMethod(Bytecode code, String findMethod, + int arrayVar, int index, String methodName, String desc) { + String findClass = RuntimeSupport.class.getName(); + String findDesc + = "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Method;"; + + code.addAload(arrayVar); + code.addIconst(index); + if (methodName == null) + code.addOpcode(Opcode.ACONST_NULL); + else { + code.addAload(0); + code.addLdc(methodName); + code.addLdc(desc); + code.addInvokestatic(findClass, findMethod, findDesc); + } + + code.addOpcode(Opcode.AASTORE); + } + + private static void addUnwrapper(Bytecode code, Class type) { + if (type.isPrimitive()) { + if (type == Void.TYPE) + code.addOpcode(Opcode.POP); + else { + int index = FactoryHelper.typeIndex(type); + String wrapper = FactoryHelper.wrapperTypes[index]; + code.addCheckcast(wrapper); + code.addInvokevirtual(wrapper, + FactoryHelper.unwarpMethods[index], + FactoryHelper.unwrapDesc[index]); + } + } + else + code.addCheckcast(type.getName()); + } +} diff --git a/src/main/javassist/util/proxy/ProxyObject.java b/src/main/javassist/util/proxy/ProxyObject.java new file mode 100644 index 00000000..4cba5c7b --- /dev/null +++ b/src/main/javassist/util/proxy/ProxyObject.java @@ -0,0 +1,29 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.util.proxy; + +/** + * The interface implemented by proxy classes. + * + * @see ProxyFactory + */ +public interface ProxyObject { + /** + * Sets a handler. It can be used for changing handlers + * during runtime. + */ + void setHandler(MethodHandler mi); +} diff --git a/src/main/javassist/util/proxy/RuntimeSupport.java b/src/main/javassist/util/proxy/RuntimeSupport.java new file mode 100644 index 00000000..837ed6ca --- /dev/null +++ b/src/main/javassist/util/proxy/RuntimeSupport.java @@ -0,0 +1,157 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.util.proxy; + +import java.lang.reflect.Method; + +/** + * Runtime support routines that the classes generated by ProxyFactory use. + * + * @see ProxyFactory + */ +public class RuntimeSupport { + /** + * Finds a method with the given name and descriptor. + * It searches only the class of self. + * + * @throws RuntimeException if the method is not found. + */ + public static Method findMethod(Object self, String name, String desc) { + Method m = findMethod2(self.getClass(), name, desc); + if (m == null) + error(self, name, desc); + + return m; + } + + /** + * Finds a method that has the given name and descriptor and is declared + * in the super class. + * + * @throws RuntimeException if the method is not found. + */ + public static Method findSuperMethod(Object self, String name, String desc) { + Class clazz = self.getClass(); + Method m = findSuperMethod2(clazz.getSuperclass(), name, desc); + if (m == null) + m = searchInterfaces(clazz, name, desc); + + if (m == null) + error(self, name, desc); + + return m; + } + + private static void error(Object self, String name, String desc) { + throw new RuntimeException("not found " + name + ":" + desc + + " in " + self.getClass().getName()); + } + + private static Method findSuperMethod2(Class clazz, String name, String desc) { + Method m = findMethod2(clazz, name, desc); + if (m != null) + return m; + + Class superClass = clazz.getSuperclass(); + if (superClass != null) { + m = findSuperMethod2(superClass, name, desc); + if (m != null) + return m; + } + + return searchInterfaces(clazz, name, desc); + } + + private static Method searchInterfaces(Class clazz, String name, String desc) { + Method m = null; + Class[] interfaces = clazz.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + m = findSuperMethod2(interfaces[i], name, desc); + if (m != null) + return m; + } + + return m; + } + + private static Method findMethod2(Class clazz, String name, String desc) { + Method[] methods = clazz.getDeclaredMethods(); + int n = methods.length; + for (int i = 0; i < n; i++) + if (methods[i].getName().equals(name) + && makeDescriptor(methods[i]).equals(desc)) + return methods[i]; + + return null; + } + + /** + * Makes a descriptor for a given method. + */ + public static String makeDescriptor(Method m) { + Class[] params = m.getParameterTypes(); + return makeDescriptor(params, m.getReturnType()); + } + + /** + * Makes a descriptor for a given method. + * + * @param params parameter types. + * @param retType return type. + */ + public static String makeDescriptor(Class[] params, Class retType) { + StringBuffer sbuf = new StringBuffer(); + sbuf.append('('); + for (int i = 0; i < params.length; i++) + makeDesc(sbuf, params[i]); + + sbuf.append(')'); + makeDesc(sbuf, retType); + return sbuf.toString(); + } + + private static void makeDesc(StringBuffer sbuf, Class type) { + if (type.isArray()) { + sbuf.append('['); + makeDesc(sbuf, type.getComponentType()); + } + else if (type.isPrimitive()) { + if (type == Void.TYPE) + sbuf.append('V'); + else if (type == Integer.TYPE) + sbuf.append('I'); + else if (type == Byte.TYPE) + sbuf.append('B'); + else if (type == Long.TYPE) + sbuf.append('J'); + else if (type == Double.TYPE) + sbuf.append('D'); + else if (type == Float.TYPE) + sbuf.append('F'); + else if (type == Character.TYPE) + sbuf.append('C'); + else if (type == Short.TYPE) + sbuf.append('S'); + else if (type == Boolean.TYPE) + sbuf.append('Z'); + else + throw new RuntimeException("bad type: " + type.getName()); + } + else + sbuf.append('L').append(type.getName().replace('.', '/')) + .append(';'); + } +} diff --git a/src/main/javassist/util/proxy/package.html b/src/main/javassist/util/proxy/package.html new file mode 100644 index 00000000..6c1354fa --- /dev/null +++ b/src/main/javassist/util/proxy/package.html @@ -0,0 +1,5 @@ + + +Dynamic proxy (similar to Enhancer of cglib). + + diff --git a/src/main/javassist/web/BadHttpRequest.java b/src/main/javassist/web/BadHttpRequest.java deleted file mode 100644 index a1aee86a..00000000 --- a/src/main/javassist/web/BadHttpRequest.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.web; - -/** - * Thrown when receiving an invalid HTTP request. - */ -public class BadHttpRequest extends Exception { - private Exception e; - - public BadHttpRequest() { e = null; } - - public BadHttpRequest(Exception _e) { e = _e; } - - public String toString() { - if (e == null) - return super.toString(); - else - return e.toString(); - } -} diff --git a/src/main/javassist/web/Viewer.java b/src/main/javassist/web/Viewer.java deleted file mode 100644 index debc52af..00000000 --- a/src/main/javassist/web/Viewer.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.web; - -import java.io.*; -import java.net.*; - -/** - * A sample applet viewer. - * - *

    This is a sort of applet viewer that can run any program even if - * the main class is not a subclass of Applet. - * This viewwer first calls main() in the main class. - * - *

    To run, you should type: - * - *

      % java javassist.web.Viewer host port Main arg1, ...
    - * - *

    This command calls Main.main() with arg1,... - * All classes including Main are fetched from - * a server http://host:port. - * Only the class file for Viewer must exist - * on a local file system at the client side; even other - * javassist.* classes are not needed at the client side. - * Viewer uses only Java core API classes. - * - *

    Note: since a Viewer object is a class loader, - * a program loaded by this object can call a method in Viewer. - * For example, you can write something like this: - * - *

      - * Viewer v = (Viewer)this.getClass().getClassLoader();
      - * String port = v.getPort();
      - * 
    - * - */ -public class Viewer extends ClassLoader { - private String server; - private int port; - - /** - * Starts a program. - */ - public static void main(String[] args) throws Throwable { - if (args.length >= 3) { - Viewer cl = new Viewer(args[0], Integer.parseInt(args[1])); - String[] args2 = new String[args.length - 3]; - System.arraycopy(args, 3, args2, 0, args.length - 3); - cl.run(args[2], args2); - } - else - System.err.println( - "Usage: java javassist.web.Viewer class [args ...]"); - } - - /** - * Constructs a viewer. - * - * @param host server name - * @param p port number - */ - public Viewer(String host, int p) { - server = host; - port = p; - } - - /** - * Returns the server name. - */ - public String getServer() { return server; } - - /** - * Returns the port number. - */ - public int getPort() { return port; } - - /** - * Invokes main() in the class specified by classname. - * - * @param classname executed class - * @param args the arguments 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 synchronized Class loadClass(String name, boolean resolve) - throws ClassNotFoundException - { - Class c = findLoadedClass(name); - if (c == null) - c = findClass(name); - - if (c == null) - throw new ClassNotFoundException(name); - - if (resolve) - resolveClass(c); - - return c; - } - - /** - * Finds the specified class. The implementation in this class - * fetches the class from the http server. If the class is - * either java.*, javax.*, or - * Viewer, then it is loaded by the parent class - * loader. - * - *

    This method can be overridden by a subclass of - * Viewer. - */ - protected Class findClass(String name) throws ClassNotFoundException { - Class c = null; - if (name.startsWith("java.") || name.startsWith("javax.") - || name.equals("javassist.web.Viewer")) - c = findSystemClass(name); - - if (c == null) - try { - byte[] b = fetchClass(name); - if (b != null) - c = defineClass(name, b, 0, b.length); - } - catch (Exception e) { - } - - return c; - } - - /** - * Fetches the class file of the specified class from the http - * server. - */ - protected byte[] fetchClass(String classname) throws Exception - { - byte[] b; - URL url = new URL("http", server, port, - "/" + classname.replace('.', '/') + ".class"); - URLConnection con = url.openConnection(); - con.connect(); - int size = con.getContentLength(); - InputStream s = con.getInputStream(); - if (size <= 0) - b = readStream(s); - else { - b = new byte[size]; - int len = 0; - do { - int n = s.read(b, len, size - len); - if (n < 0) { - s.close(); - throw new IOException("the stream was closed: " - + classname); - } - len += n; - } while (len < size); - } - - s.close(); - return b; - } - - private byte[] readStream(InputStream fin) throws IOException { - byte[] buf = new byte[4096]; - int size = 0; - int len = 0; - do { - size += len; - if (buf.length - size <= 0) { - byte[] newbuf = new byte[buf.length * 2]; - System.arraycopy(buf, 0, newbuf, 0, size); - buf = newbuf; - } - - len = fin.read(buf, size, buf.length - size); - } while (len >= 0); - - byte[] result = new byte[size]; - System.arraycopy(buf, 0, result, 0, size); - return result; - } -} diff --git a/src/main/javassist/web/Webserver.java b/src/main/javassist/web/Webserver.java deleted file mode 100644 index 4da031df..00000000 --- a/src/main/javassist/web/Webserver.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 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.web; - -import java.net.*; -import java.io.*; -import java.util.Date; -import javassist.*; - -/** - * A web server for running sample programs. - * - *

    This enables a Java program to instrument class files loaded by - * web browsers for applets. Since the (standard) security manager - * does not allow an applet to create and use a class loader, - * instrumenting class files must be done by this web server. - * - *

    Note: although this class is included in the Javassist API, - * it is provided as a sample implementation of the web server using - * Javassist. Especially, there might be security flaws in this server. - * Please use this with YOUR OWN RISK. - */ -public class Webserver { - private ServerSocket socket; - private ClassPool classPool; - protected Translator translator; - - private final static byte[] endofline = { 0x0d, 0x0a }; - private byte[] filebuffer = new byte[4096]; - - private final static int typeHtml = 1; - private final static int typeClass = 2; - private final static int typeGif = 3; - private final static int typeJpeg = 4; - private final static int typeText = 5; - - /** - * If this field is not null, the class files taken from - * ClassPool are written out under the directory - * specified by this field. The directory name must not end - * with a directory separator. - */ - public String debugDir = null; - - /** - * The top directory of html (and .gif, .class, ...) files. - * It must end with the directory separator such as "/". - * (For portability, "/" should be used as the directory separator. - * Javassist automatically translates "/" into a platform-dependent - * character.) - * If this field is null, the top directory is the current one where - * the JVM is running. - * - *

    If the given URL indicates a class file and the class file - * is not found under the directory specified by this variable, - * then Class.getResourceAsStream() is called - * for searching the Java class paths. - */ - public String htmlfileBase = null; - - /** - * Starts a web server. - * The port number is specified by the first argument. - */ - public static void main(String[] args) throws IOException { - if (args.length == 1) { - Webserver web = new Webserver(args[0]); - web.run(); - } - else - System.err.println( - "Usage: java javassist.web.Webserver "); - } - - /** - * Constructs a web server. - * - * @param port port number - */ - public Webserver(String port) throws IOException { - this(Integer.parseInt(port)); - } - - /** - * Constructs a web server. - * - * @param port port number - */ - public Webserver(int port) throws IOException { - socket = new ServerSocket(port); - classPool = null; - translator = null; - } - - /** - * Requests the web server to use the specified - * ClassPool object for obtaining a class file. - */ - public void setClassPool(ClassPool loader) { - classPool = loader; - } - - /** - * Adds a translator, which is called whenever a client requests - * a class file. - * - * @param cp the ClassPool object for obtaining - * a class file. - * @param t a translator. - */ - public void addTranslator(ClassPool cp, Translator t) - throws NotFoundException, CannotCompileException - { - classPool = cp; - translator = t; - t.start(classPool); - } - - /** - * Closes the socket. - */ - public void end() throws IOException { - socket.close(); - } - - /** - * Prints a log message. - */ - public void logging(String msg) { - System.out.println(msg); - } - - /** - * Prints a log message. - */ - public void logging(String msg1, String msg2) { - System.out.print(msg1); - System.out.print(" "); - System.out.println(msg2); - } - - /** - * Prints a log message. - */ - public void logging(String msg1, String msg2, String msg3) { - System.out.print(msg1); - System.out.print(" "); - System.out.print(msg2); - System.out.print(" "); - System.out.println(msg3); - } - - /** - * Prints a log message with indentation. - */ - public void logging2(String msg) { - System.out.print(" "); - System.out.println(msg); - } - - /** - * Begins the HTTP service. - */ - public void run() { - System.err.println("ready to service..."); - for (;;) - try { - ServiceThread th = new ServiceThread(this, socket.accept()); - th.start(); - } - catch (IOException e) { - logging(e.toString()); - } - } - - final void process(Socket clnt) throws IOException { - InputStream in = new BufferedInputStream(clnt.getInputStream()); - String cmd = readLine(in); - logging(clnt.getInetAddress().getHostName(), - new Date().toString(), cmd); - while (skipLine(in) > 0){ - } - - OutputStream out = new BufferedOutputStream(clnt.getOutputStream()); - try { - doReply(in, out, cmd); - } - catch (BadHttpRequest e) { - replyError(out, e); - } - - out.flush(); - in.close(); - out.close(); - clnt.close(); - } - - private String readLine(InputStream in) throws IOException { - StringBuffer buf = new StringBuffer(); - int c; - while ((c = in.read()) >= 0 && c != 0x0d) - buf.append((char)c); - - in.read(); /* skip 0x0a (LF) */ - return buf.toString(); - } - - private int skipLine(InputStream in) throws IOException { - int c; - int len = 0; - while ((c = in.read()) >= 0 && c != 0x0d) - ++len; - - in.read(); /* skip 0x0a (LF) */ - return len; - } - - /** - * Proceses a HTTP request from a client. - * - * @param out the output stream to a client - * @param cmd the command received from a client - */ - public void doReply(InputStream in, OutputStream out, String cmd) - throws IOException, BadHttpRequest - { - int len; - int fileType; - String filename, urlName; - - if (cmd.startsWith("GET /")) - filename = urlName = cmd.substring(5, cmd.indexOf(' ', 5)); - else - throw new BadHttpRequest(); - - if (filename.endsWith(".class")) - fileType = typeClass; - else if (filename.endsWith(".html") || filename.endsWith(".htm")) - fileType = typeHtml; - else if (filename.endsWith(".gif")) - fileType = typeGif; - else if (filename.endsWith(".jpg")) - fileType = typeJpeg; - else - fileType = typeText; // or textUnknown - - len = filename.length(); - if (fileType == typeClass - && letUsersSendClassfile(out, filename, len)) - return; - - checkFilename(filename, len); - if (htmlfileBase != null) - filename = htmlfileBase + filename; - - if (File.separatorChar != '/') - filename = filename.replace('/', File.separatorChar); - - File file = new File(filename); - if (file.canRead()) { - sendHeader(out, file.length(), fileType); - FileInputStream fin = new FileInputStream(file); - for (;;) { - len = fin.read(filebuffer); - if (len <= 0) - break; - else - out.write(filebuffer, 0, len); - } - - fin.close(); - return; - } - - // If the file is not found under the html-file directory, - // then Class.getResourceAsStream() is tried. - - if (fileType == typeClass) { - InputStream fin - = getClass().getResourceAsStream("/" + urlName); - if (fin != null) { - ByteArrayOutputStream barray = new ByteArrayOutputStream(); - for (;;) { - len = fin.read(filebuffer); - if (len <= 0) - break; - else - barray.write(filebuffer, 0, len); - } - - byte[] classfile = barray.toByteArray(); - sendHeader(out, classfile.length, typeClass); - out.write(classfile); - fin.close(); - return; - } - } - - throw new BadHttpRequest(); - } - - private void checkFilename(String filename, int len) - throws BadHttpRequest - { - for (int i = 0; i < len; ++i) { - char c = filename.charAt(i); - if (!Character.isJavaIdentifierPart(c) && c != '.' && c != '/') - throw new BadHttpRequest(); - } - - if (filename.indexOf("..") >= 0) - throw new BadHttpRequest(); - } - - private boolean letUsersSendClassfile(OutputStream out, - String filename, int length) - throws IOException, BadHttpRequest - { - if (classPool == null) - return false; - - byte[] classfile; - String classname - = filename.substring(0, length - 6).replace('/', '.'); - try { - if (translator != null) - translator.onLoad(classPool, classname); - - CtClass c = classPool.get(classname); - classfile = c.toBytecode(); - if (debugDir != null) - c.writeFile(debugDir); - } - catch (Exception e) { - throw new BadHttpRequest(e); - } - - sendHeader(out, classfile.length, typeClass); - out.write(classfile); - return true; - } - - private void sendHeader(OutputStream out, long dataLength, int filetype) - throws IOException - { - out.write("HTTP/1.0 200 OK".getBytes()); - out.write(endofline); - out.write("Content-Length: ".getBytes()); - out.write(Long.toString(dataLength).getBytes()); - out.write(endofline); - if (filetype == typeClass) - out.write("Content-Type: application/octet-stream".getBytes()); - else if (filetype == typeHtml) - out.write("Content-Type: text/html".getBytes()); - else if (filetype == typeGif) - out.write("Content-Type: image/gif".getBytes()); - else if (filetype == typeJpeg) - out.write("Content-Type: image/jpg".getBytes()); - else if (filetype == typeText) - out.write("Content-Type: text/plain".getBytes()); - - out.write(endofline); - out.write(endofline); - } - - private void replyError(OutputStream out, BadHttpRequest e) - throws IOException - { - logging2("bad request: " + e.toString()); - out.write("HTTP/1.0 400 Bad Request".getBytes()); - out.write(endofline); - out.write(endofline); - out.write("

    Bad Request

    ".getBytes()); - } -} - -class ServiceThread extends Thread { - Webserver web; - Socket sock; - - public ServiceThread(Webserver w, Socket s) { - web = w; - sock = s; - } - - public void run() { - try { - web.process(sock); - } - catch (IOException e) { - } - } -} diff --git a/src/main/javassist/web/package.html b/src/main/javassist/web/package.html deleted file mode 100644 index 0c7fb453..00000000 --- a/src/main/javassist/web/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Simple web server for running sample code. - -

    This package provides a simple web server for sample packages. - -