• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  */
15 
16 package javassist.tools.rmi;
17 
18 import java.io.*;
19 import java.net.*;
20 import java.applet.Applet;
21 import java.lang.reflect.*;
22 
23 /**
24  * The object importer enables applets to call a method on a remote
25  * object running on the <code>Webserver</code> (the <b>main</b> class of this
26  * package).
27  *
28  * <p>To access the remote
29  * object, the applet first calls <code>lookupObject()</code> and
30  * obtains a proxy object, which is a reference to that object.
31  * The class name of the proxy object is identical to that of
32  * the remote object.
33  * The proxy object provides the same set of methods as the remote object.
34  * If one of the methods is invoked on the proxy object,
35  * the invocation is delegated to the remote object.
36  * From the viewpoint of the applet, therefore, the two objects are
37  * identical. The applet can access the object on the server
38  * with the regular Java syntax without concern about the actual
39  * location.
40  *
41  * <p>The methods remotely called by the applet must be <code>public</code>.
42  * This is true even if the applet's class and the remote object's classs
43  * belong to the same package.
44  *
45  * <p>If class X is a class of remote objects, a subclass of X must be
46  * also a class of remote objects.  On the other hand, this restriction
47  * is not applied to the superclass of X.  The class X does not have to
48  * contain a constructor taking no arguments.
49  *
50  * <p>The parameters to a remote method is passed in the <i>call-by-value</i>
51  * manner.  Thus all the parameter classes must implement
52  * <code>java.io.Serializable</code>.  However, if the parameter is the
53  * proxy object, the reference to the remote object instead of a copy of
54  * the object is passed to the method.
55  *
56  * <p>Because of the limitations of the current implementation,
57  * <ul>
58  * <li>The parameter objects cannot contain the proxy
59  * object as a field value.
60  * <li>If class <code>C</code> is of the remote object, then
61  * the applet cannot instantiate <code>C</code> locally or remotely.
62  * </ul>
63  *
64  * <p>All the exceptions thrown by the remote object are converted
65  * into <code>RemoteException</code>.  Since this exception is a subclass
66  * of <code>RuntimeException</code>, the caller method does not need
67  * to catch the exception.  However, good programs should catch
68  * the <code>RuntimeException</code>.
69  *
70  * @see javassist.tools.rmi.AppletServer
71  * @see javassist.tools.rmi.RemoteException
72  * @see javassist.tools.web.Viewer
73  */
74 public class ObjectImporter implements java.io.Serializable {
75     private final byte[] endofline = { 0x0d, 0x0a };
76     private String servername, orgServername;
77     private int port, orgPort;
78 
79     protected byte[] lookupCommand = "POST /lookup HTTP/1.0".getBytes();
80     protected byte[] rmiCommand = "POST /rmi HTTP/1.0".getBytes();
81 
82     /**
83      * Constructs an object importer.
84      *
85      * <p>Remote objects are imported from the web server that the given
86      * applet has been loaded from.
87      *
88      * @param applet    the applet loaded from the <code>Webserver</code>.
89      */
ObjectImporter(Applet applet)90     public ObjectImporter(Applet applet) {
91         URL codebase = applet.getCodeBase();
92         orgServername = servername = codebase.getHost();
93         orgPort = port = codebase.getPort();
94     }
95 
96     /**
97      * Constructs an object importer.
98      *
99      * <p>If you run a program with <code>javassist.tools.web.Viewer</code>,
100      * you can construct an object importer as follows:
101      *
102      * <ul><pre>
103      * Viewer v = (Viewer)this.getClass().getClassLoader();
104      * ObjectImporter oi = new ObjectImporter(v.getServer(), v.getPort());
105      * </pre></ul>
106      *
107      * @see javassist.tools.web.Viewer
108      */
ObjectImporter(String servername, int port)109     public ObjectImporter(String servername, int port) {
110         this.orgServername = this.servername = servername;
111         this.orgPort = this.port = port;
112     }
113 
114     /**
115      * Finds the object exported by a server with the specified name.
116      * If the object is not found, this method returns null.
117      *
118      * @param name      the name of the exported object.
119      * @return          the proxy object or null.
120      */
getObject(String name)121     public Object getObject(String name) {
122         try {
123             return lookupObject(name);
124         }
125         catch (ObjectNotFoundException e) {
126             return null;
127         }
128     }
129 
130     /**
131      * Sets an http proxy server.  After this method is called, the object
132      * importer connects a server through the http proxy server.
133      */
setHttpProxy(String host, int port)134     public void setHttpProxy(String host, int port) {
135         String proxyHeader = "POST http://" + orgServername + ":" + orgPort;
136         String cmd = proxyHeader + "/lookup HTTP/1.0";
137         lookupCommand = cmd.getBytes();
138         cmd = proxyHeader + "/rmi HTTP/1.0";
139         rmiCommand = cmd.getBytes();
140         this.servername = host;
141         this.port = port;
142     }
143 
144     /**
145      * Finds the object exported by the server with the specified name.
146      * It sends a POST request to the server (via an http proxy server
147      * if needed).
148      *
149      * @param name      the name of the exported object.
150      * @return          the proxy object.
151      */
lookupObject(String name)152     public Object lookupObject(String name) throws ObjectNotFoundException
153     {
154         try {
155             Socket sock = new Socket(servername, port);
156             OutputStream out = sock.getOutputStream();
157             out.write(lookupCommand);
158             out.write(endofline);
159             out.write(endofline);
160 
161             ObjectOutputStream dout = new ObjectOutputStream(out);
162             dout.writeUTF(name);
163             dout.flush();
164 
165             InputStream in = new BufferedInputStream(sock.getInputStream());
166             skipHeader(in);
167             ObjectInputStream din = new ObjectInputStream(in);
168             int n = din.readInt();
169             String classname = din.readUTF();
170             din.close();
171             dout.close();
172             sock.close();
173 
174             if (n >= 0)
175                 return createProxy(n, classname);
176         }
177         catch (Exception e) {
178             e.printStackTrace();
179             throw new ObjectNotFoundException(name, e);
180         }
181 
182         throw new ObjectNotFoundException(name);
183     }
184 
185     private static final Class[] proxyConstructorParamTypes
186         = new Class[] { ObjectImporter.class, int.class };
187 
createProxy(int oid, String classname)188     private Object createProxy(int oid, String classname) throws Exception {
189         Class c = Class.forName(classname);
190         Constructor cons = c.getConstructor(proxyConstructorParamTypes);
191         return cons.newInstance(new Object[] { this, new Integer(oid) });
192     }
193 
194     /**
195      * Calls a method on a remote object.
196      * It sends a POST request to the server (via an http proxy server
197      * if needed).
198      *
199      * <p>This method is called by only proxy objects.
200      */
call(int objectid, int methodid, Object[] args)201     public Object call(int objectid, int methodid, Object[] args)
202         throws RemoteException
203     {
204         boolean result;
205         Object rvalue;
206         String errmsg;
207 
208         try {
209             /* This method establishes a raw tcp connection for sending
210              * a POST message.  Thus the object cannot communicate a
211              * remote object beyond a fire wall.  To avoid this problem,
212              * the connection should be established with a mechanism
213              * collaborating a proxy server.  Unfortunately, java.lang.URL
214              * does not seem to provide such a mechanism.
215              *
216              * You might think that using HttpURLConnection is a better
217              * way than constructing a raw tcp connection.  Unfortunately,
218              * URL.openConnection() does not return an HttpURLConnection
219              * object in Netscape's JVM.  It returns a
220              * netscape.net.URLConnection object.
221              *
222              * lookupObject() has the same problem.
223              */
224             Socket sock = new Socket(servername, port);
225             OutputStream out = new BufferedOutputStream(
226                                                 sock.getOutputStream());
227             out.write(rmiCommand);
228             out.write(endofline);
229             out.write(endofline);
230 
231             ObjectOutputStream dout = new ObjectOutputStream(out);
232             dout.writeInt(objectid);
233             dout.writeInt(methodid);
234             writeParameters(dout, args);
235             dout.flush();
236 
237             InputStream ins = new BufferedInputStream(sock.getInputStream());
238             skipHeader(ins);
239             ObjectInputStream din = new ObjectInputStream(ins);
240             result = din.readBoolean();
241             rvalue = null;
242             errmsg = null;
243             if (result)
244                 rvalue = din.readObject();
245             else
246                 errmsg = din.readUTF();
247 
248             din.close();
249             dout.close();
250             sock.close();
251 
252             if (rvalue instanceof RemoteRef) {
253                 RemoteRef ref = (RemoteRef)rvalue;
254                 rvalue = createProxy(ref.oid, ref.classname);
255             }
256         }
257         catch (ClassNotFoundException e) {
258             throw new RemoteException(e);
259         }
260         catch (IOException e) {
261             throw new RemoteException(e);
262         }
263         catch (Exception e) {
264             throw new RemoteException(e);
265         }
266 
267         if (result)
268             return rvalue;
269         else
270             throw new RemoteException(errmsg);
271     }
272 
skipHeader(InputStream in)273     private void skipHeader(InputStream in) throws IOException {
274         int len;
275         do {
276             int c;
277             len = 0;
278             while ((c = in.read()) >= 0 && c != 0x0d)
279                 ++len;
280 
281             in.read();  /* skip 0x0a (LF) */
282         } while (len > 0);
283     }
284 
writeParameters(ObjectOutputStream dout, Object[] params)285     private void writeParameters(ObjectOutputStream dout, Object[] params)
286         throws IOException
287     {
288         int n = params.length;
289         dout.writeInt(n);
290         for (int i = 0; i < n; ++i)
291             if (params[i] instanceof Proxy) {
292                 Proxy p = (Proxy)params[i];
293                 dout.writeObject(new RemoteRef(p._getObjectId()));
294             }
295             else
296                 dout.writeObject(params[i]);
297     }
298 }
299