diff options
Diffstat (limited to 'src/main/javassist/rmi')
-rw-r--r-- | src/main/javassist/rmi/AppletServer.java | 259 | ||||
-rw-r--r-- | src/main/javassist/rmi/ObjectImporter.java | 308 | ||||
-rw-r--r-- | src/main/javassist/rmi/ObjectNotFoundException.java | 36 | ||||
-rw-r--r-- | src/main/javassist/rmi/Proxy.java | 35 | ||||
-rw-r--r-- | src/main/javassist/rmi/RemoteException.java | 40 | ||||
-rw-r--r-- | src/main/javassist/rmi/RemoteRef.java | 45 | ||||
-rw-r--r-- | src/main/javassist/rmi/Sample.java | 46 | ||||
-rw-r--r-- | src/main/javassist/rmi/StubGenerator.java | 261 |
8 files changed, 1030 insertions, 0 deletions
diff --git a/src/main/javassist/rmi/AppletServer.java b/src/main/javassist/rmi/AppletServer.java new file mode 100644 index 00000000..2b8c30e5 --- /dev/null +++ b/src/main/javassist/rmi/AppletServer.java @@ -0,0 +1,259 @@ +/* + * This file is part of the Javassist toolkit. + * + * 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. You may obtain a copy of the License at + * either http://www.mozilla.org/MPL/. + * + * 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. + * + * The Original Code is Javassist. + * + * The Initial Developer of the Original Code is Shigeru Chiba. Portions + * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba. + * All Rights Reserved. + * + * Contributor(s): + * + * The development of this software is supported in part by the PRESTO + * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation. + */ + +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 + * <code>exportObject()</code> 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, int port) + throws IOException, NotFoundException, CannotCompileException + { + super(port); + exportedNames = new Hashtable(); + exportedObjects = new Vector(); + stubGen = (StubGenerator)loader.getTranslator(); + setClassPool(loader); + } + + /** + * 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 object 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 new file mode 100644 index 00000000..19b6cb4e --- /dev/null +++ b/src/main/javassist/rmi/ObjectImporter.java @@ -0,0 +1,308 @@ +/* + * This file is part of the Javassist toolkit. + * + * 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. You may obtain a copy of the License at + * either http://www.mozilla.org/MPL/. + * + * 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. + * + * The Original Code is Javassist. + * + * The Initial Developer of the Original Code is Shigeru Chiba. Portions + * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba. + * All Rights Reserved. + * + * Contributor(s): + * + * The development of this software is supported in part by the PRESTO + * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation. + */ + +package javassist.rmi; + +import java.io.*; +import java.net.*; +import java.awt.*; +import java.applet.Applet; +import java.lang.reflect.*; + +/** + * The object importer enables applets to call a method on a remote + * object running on the <code>Webserver</code>. + * + * <p>To access the remote + * object, the applet first calls <code>lookupObject()</code> 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. + * + * <p>The methods remotely called by the applet must be <code>public</code>. + * This is true even if the applet's class and the remote object's classs + * belong to the same package. + * + * <p>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. + * + * <p>The parameters to a remote method is passed in the <i>call-by-value</i> + * manner. Thus all the parameter classes must implement + * <code>java.io.Serializable</code>. 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. + * + * <p>Because of the limitations of the current implementation, + * <ul> + * <li>The parameter objects cannot contain the proxy + * object as a field value. + * <li>If class <code>C</code> is of the remote object, then + * the applet cannot instantiate <code>C</code> locally or remotely. + * </ul> + * + * <p>All the exceptions thrown by the remote object are converted + * into <code>RemoteException</code>. Since this exception is a subclass + * of <code>RuntimeException</code>, the caller method does not need + * to catch the exception. However, good programs should catch + * the <code>RuntimeException</code>. + * + * @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; + + private byte[] lookupCommand = "POST /lookup HTTP/1.0".getBytes(); + private byte[] rmiCommand = "POST /rmi HTTP/1.0".getBytes(); + + /** + * Constructs an object importer. + * + * <p>Remote objects are imported from the web server that the given + * applet has been loaded from. + * + * @param applet the applet loaded from the <code>Webserver</code>. + */ + public ObjectImporter(Applet applet) { + URL codebase = applet.getCodeBase(); + orgServername = servername = codebase.getHost(); + orgPort = port = codebase.getPort(); + } + + /** + * Constructs an object importer. + * + * <p>If you run a program with <code>javassist.web.Viewer</code>, + * you can construct an object importer as follows: + * + * <ul><pre> + * Viewer v = (Viewer)this.getClass().getClassLoader(); + * ObjectImporter oi = new ObjectImporter(v.getServer(), v.getPort()); + * </pre></ul> + * + * @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). + * + * <p>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 new file mode 100644 index 00000000..e31db370 --- /dev/null +++ b/src/main/javassist/rmi/ObjectNotFoundException.java @@ -0,0 +1,36 @@ +/* + * This file is part of the Javassist toolkit. + * + * 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. You may obtain a copy of the License at + * either http://www.mozilla.org/MPL/. + * + * 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. + * + * The Original Code is Javassist. + * + * The Initial Developer of the Original Code is Shigeru Chiba. Portions + * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba. + * All Rights Reserved. + * + * Contributor(s): + * + * The development of this software is supported in part by the PRESTO + * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation. + */ + +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 new file mode 100644 index 00000000..f310d358 --- /dev/null +++ b/src/main/javassist/rmi/Proxy.java @@ -0,0 +1,35 @@ +/* + * This file is part of the Javassist toolkit. + * + * 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. You may obtain a copy of the License at + * either http://www.mozilla.org/MPL/. + * + * 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. + * + * The Original Code is Javassist. + * + * The Initial Developer of the Original Code is Shigeru Chiba. Portions + * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba. + * All Rights Reserved. + * + * Contributor(s): + * + * The development of this software is supported in part by the PRESTO + * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation. + */ + +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 new file mode 100644 index 00000000..6b89c64b --- /dev/null +++ b/src/main/javassist/rmi/RemoteException.java @@ -0,0 +1,40 @@ +/* + * This file is part of the Javassist toolkit. + * + * 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. You may obtain a copy of the License at + * either http://www.mozilla.org/MPL/. + * + * 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. + * + * The Original Code is Javassist. + * + * The Initial Developer of the Original Code is Shigeru Chiba. Portions + * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba. + * All Rights Reserved. + * + * Contributor(s): + * + * The development of this software is supported in part by the PRESTO + * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation. + */ + +package javassist.rmi; + +/** + * <code>RemoteException</code> 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 new file mode 100644 index 00000000..0017ecc1 --- /dev/null +++ b/src/main/javassist/rmi/RemoteRef.java @@ -0,0 +1,45 @@ +/* + * This file is part of the Javassist toolkit. + * + * 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. You may obtain a copy of the License at + * either http://www.mozilla.org/MPL/. + * + * 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. + * + * The Original Code is Javassist. + * + * The Initial Developer of the Original Code is Shigeru Chiba. Portions + * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba. + * All Rights Reserved. + * + * Contributor(s): + * + * The development of this software is supported in part by the PRESTO + * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation. + */ + +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 new file mode 100644 index 00000000..fee85d27 --- /dev/null +++ b/src/main/javassist/rmi/Sample.java @@ -0,0 +1,46 @@ +/* + * This file is part of the Javassist toolkit. + * + * 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. You may obtain a copy of the License at + * either http://www.mozilla.org/MPL/. + * + * 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. + * + * The Original Code is Javassist. + * + * The Initial Developer of the Original Code is Shigeru Chiba. Portions + * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba. + * All Rights Reserved. + * + * Contributor(s): + * + * The development of this software is supported in part by the PRESTO + * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation. + */ + +package javassist.rmi; + +/** + * A template used for defining a proxy class. + * The class file of this class is read by the <code>StubGenerator</code> + * 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 new file mode 100644 index 00000000..9e77c383 --- /dev/null +++ b/src/main/javassist/rmi/StubGenerator.java @@ -0,0 +1,261 @@ +/* + * This file is part of the Javassist toolkit. + * + * 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. You may obtain a copy of the License at + * either http://www.mozilla.org/MPL/. + * + * 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. + * + * The Original Code is Javassist. + * + * The Initial Developer of the Original Code is Shigeru Chiba. Portions + * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba. + * All Rights Reserved. + * + * Contributor(s): + * + * The development of this software is supported in part by the PRESTO + * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation. + */ + +package javassist.rmi; + +import java.io.*; +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. + * + * <p>The proxy class for class A is as follows: + * + * <ul><pre>public class A implements Proxy, Serializable { + * private ObjectImporter importer; + * private int objectId; + * public int _getObjectId() { return objectId; } + * public A(ObjectImporter oi, int id) { + * importer = oi; objectId = id; + * } + * + * ... the same methods that the original class A declares ... + * }</pre></ul> + * + * <p>Instances of the proxy class is created by an + * <code>ObjectImporter</code> 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(); + } + + /** + * 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") }; + } + + public void onWrite(ClassPool pool, String classname) {} + + /** + * Returns <code>true</code> if the specified class is a proxy class + * recorded by <code>makeProxyClass()</code>. + * + * @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 <code>false</code> 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; + + String name = superclazz.getName(); + try { + superclazz.getDeclaredConstructor(null); + break; // the constructor with no arguments is found. + } + catch (NotFoundException e) { + } + + superclazz.addConstructor( + CtNewConstructor.defaultConstructor(superclazz)); + } + } +} |