/Users/lyon/j4p/src/javassist/rmi/ObjectImporter.java
|
1 /*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2003 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.rmi;
17
18 import java.applet.Applet;
19 import java.io.*;
20 import java.lang.reflect.Constructor;
21 import java.net.Socket;
22 import java.net.URL;
23
24 /**
25 * The object importer enables applets to call a method on a remote
26 * object running on the <code>Webserver</code>.
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.rmi.AppletServer
71 * @see javassist.rmi.RemoteException
72 * @see javassist.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 private byte[] lookupCommand
80 = "POST /lookup HTTP/1.0".getBytes();
81 private byte[] rmiCommand
82 = "POST /rmi HTTP/1.0".getBytes();
83
84 /**
85 * Constructs an object importer.
86 *
87 * <p>Remote objects are imported from the
88 * web server that the given
89 * applet has been loaded from.
90 *
91 * @param applet the applet loaded from
92 * the <code>Webserver</code>.
93 */
94 public ObjectImporter(Applet applet) {
95 URL codebase = applet.getCodeBase();
96 orgServername = servername = codebase.getHost();
97 orgPort = port = codebase.getPort();
98 }
99
100 /**
101 * Constructs an object importer.
102 *
103 * <p>If you run a program with <code>javassist.web.Viewer</code>,
104 * you can construct an object importer as follows:
105 *
106 * <ul><pre>
107 * Viewer v = (Viewer)this.getClass().getClassLoader();
108 * ObjectImporter oi = new ObjectImporter(v.getServer(), v.getPort());
109 * </pre></ul>
110 *
111 * @see javassist.web.Viewer
112 */
113 public ObjectImporter(String servername, int port) {
114 this.orgServername = this.servername = servername;
115 this.orgPort = this.port = port;
116 }
117
118 /**
119 * Finds the object exported by a server with the specified name.
120 * If the object is not found, this method returns null.
121 *
122 * @param name the name of the exported object.
123 * @return the proxy object or null.
124 */
125 public Object getObject(String name) {
126 try {
127 return lookupObject(name);
128 } catch (ObjectNotFoundException e) {
129 return null;
130 }
131 }
132
133 /**
134 * Sets an http proxy server. After this method is called, the object
135 * importer connects a server through the http proxy server.
136 */
137 public void setHttpProxy(String host, int port) {
138 String proxyHeader = "POST http://"
139 + orgServername + ":" + orgPort;
140 String cmd = proxyHeader + "/lookup HTTP/1.0";
141 lookupCommand = cmd.getBytes();
142 cmd = proxyHeader + "/rmi HTTP/1.0";
143 rmiCommand = cmd.getBytes();
144 this.servername = host;
145 this.port = port;
146 }
147
148 /**
149 * Finds the object exported by the server with the specified name.
150 * It sends a POST request to the server (via an http proxy server
151 * if needed).
152 *
153 * @param name the name of the exported object.
154 * @return the proxy object.
155 */
156 public Object lookupObject(String name)
157 throws ObjectNotFoundException {
158 try {
159 Socket sock = new Socket(servername, port);
160 OutputStream out = sock.getOutputStream();
161 out.write(lookupCommand);
162 out.write(endofline);
163 out.write(endofline);
164
165 ObjectOutputStream dout = new ObjectOutputStream(out);
166 dout.writeUTF(name);
167 dout.flush();
168
169 InputStream in = new BufferedInputStream(sock.getInputStream());
170 skipHeader(in);
171 ObjectInputStream din = new ObjectInputStream(in);
172 int n = din.readInt();
173 String classname = din.readUTF();
174 din.close();
175 dout.close();
176 sock.close();
177
178 if (n >= 0)
179 return createProxy(n, classname);
180 } catch (Exception e) {
181 e.printStackTrace();
182 throw new ObjectNotFoundException(name, e);
183 }
184
185 throw new ObjectNotFoundException(name);
186 }
187
188 private static final Class[] proxyConstructorParamTypes
189 = new Class[]{ObjectImporter.class, int.class};
190
191 private Object createProxy(int oid, String classname) throws Exception {
192 Class c = Class.forName(classname);
193 Constructor cons = c.getConstructor(proxyConstructorParamTypes);
194 return cons.newInstance(new Object[]{this, new Integer(oid)});
195 }
196
197 /**
198 * Calls a method on a remote object.
199 * It sends a POST request to the server (via an http proxy server
200 * if needed).
201 *
202 * <p>This method is called by only proxy objects.
203 */
204 public Object call(int objectid, int methodid, Object[] args)
205 throws RemoteException {
206 boolean result;
207 Object rvalue;
208 String errmsg;
209
210 try {
211 /* This method establishes a raw tcp connection for sending
212 * a POST message. Thus the object cannot communicate a
213 * remote object beyond a fire wall. To avoid this problem,
214 * the connection should be established with a mechanism
215 * collaborating a proxy server. Unfortunately, java.lang.URL
216 * does not seem to provide such a mechanism.
217 *
218 * You might think that using HttpURLConnection is a better
219 * way than constructing a raw tcp connection. Unfortunately,
220 * URL.openConnection() does not return an HttpURLConnection
221 * object in Netscape's JVM. It returns a
222 * netscape.net.URLConnection object.
223 *
224 * lookupObject() has the same problem.
225 */
226 Socket sock = new Socket(servername, port);
227 OutputStream out = new BufferedOutputStream(
228 sock.getOutputStream());
229 out.write(rmiCommand);
230 out.write(endofline);
231 out.write(endofline);
232
233 ObjectOutputStream dout = new ObjectOutputStream(out);
234 dout.writeInt(objectid);
235 dout.writeInt(methodid);
236 writeParameters(dout, args);
237 dout.flush();
238
239 InputStream ins = new BufferedInputStream(sock.getInputStream());
240 skipHeader(ins);
241 ObjectInputStream din = new ObjectInputStream(ins);
242 result = din.readBoolean();
243 rvalue = null;
244 errmsg = null;
245 if (result)
246 rvalue = din.readObject();
247 else
248 errmsg = din.readUTF();
249
250 din.close();
251 dout.close();
252 sock.close();
253
254 if (rvalue instanceof RemoteRef) {
255 RemoteRef ref = (RemoteRef) rvalue;
256 rvalue = createProxy(ref.oid, ref.classname);
257 }
258 } catch (ClassNotFoundException e) {
259 throw new RemoteException(e);
260 } catch (IOException e) {
261 throw new RemoteException(e);
262 } catch (Exception e) {
263 throw new RemoteException(e);
264 }
265
266 if (result)
267 return rvalue;
268 else
269 throw new RemoteException(errmsg);
270 }
271
272 private void skipHeader(InputStream in) throws IOException {
273 int len;
274 do {
275 int c;
276 len = 0;
277 while ((c = in.read()) >= 0 && c != 0x0d)
278 ++len;
279
280 in.read(); /* skip 0x0a (LF) */
281 } while (len > 0);
282 }
283
284 private void writeParameters(ObjectOutputStream dout, Object[] params)
285 throws IOException {
286 int n = params.length;
287 dout.writeInt(n);
288 for (int i = 0; i < n; ++i)
289 if (params[i] instanceof Proxy) {
290 Proxy p = (Proxy) params[i];
291 dout.writeObject(new RemoteRef(p._getObjectId()));
292 } else
293 dout.writeObject(params[i]);
294 }
295 }
296