/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 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]); } }