001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist.tools.rmi;
017:
018: import java.io.*;
019: import java.net.*;
020: import java.applet.Applet;
021: import java.lang.reflect.*;
022:
023: /**
024: * The object importer enables applets to call a method on a remote
025: * object running on the <code>Webserver</code> (the <b>main</b> class of this
026: * package).
027: *
028: * <p>To access the remote
029: * object, the applet first calls <code>lookupObject()</code> and
030: * obtains a proxy object, which is a reference to that object.
031: * The class name of the proxy object is identical to that of
032: * the remote object.
033: * The proxy object provides the same set of methods as the remote object.
034: * If one of the methods is invoked on the proxy object,
035: * the invocation is delegated to the remote object.
036: * From the viewpoint of the applet, therefore, the two objects are
037: * identical. The applet can access the object on the server
038: * with the regular Java syntax without concern about the actual
039: * location.
040: *
041: * <p>The methods remotely called by the applet must be <code>public</code>.
042: * This is true even if the applet's class and the remote object's classs
043: * belong to the same package.
044: *
045: * <p>If class X is a class of remote objects, a subclass of X must be
046: * also a class of remote objects. On the other hand, this restriction
047: * is not applied to the superclass of X. The class X does not have to
048: * contain a constructor taking no arguments.
049: *
050: * <p>The parameters to a remote method is passed in the <i>call-by-value</i>
051: * manner. Thus all the parameter classes must implement
052: * <code>java.io.Serializable</code>. However, if the parameter is the
053: * proxy object, the reference to the remote object instead of a copy of
054: * the object is passed to the method.
055: *
056: * <p>Because of the limitations of the current implementation,
057: * <ul>
058: * <li>The parameter objects cannot contain the proxy
059: * object as a field value.
060: * <li>If class <code>C</code> is of the remote object, then
061: * the applet cannot instantiate <code>C</code> locally or remotely.
062: * </ul>
063: *
064: * <p>All the exceptions thrown by the remote object are converted
065: * into <code>RemoteException</code>. Since this exception is a subclass
066: * of <code>RuntimeException</code>, the caller method does not need
067: * to catch the exception. However, good programs should catch
068: * the <code>RuntimeException</code>.
069: *
070: * @see javassist.tools.rmi.AppletServer
071: * @see javassist.tools.rmi.RemoteException
072: * @see javassist.tools.web.Viewer
073: */
074: public class ObjectImporter implements java.io.Serializable {
075: private final byte[] endofline = { 0x0d, 0x0a };
076: private String servername, orgServername;
077: private int port, orgPort;
078:
079: protected byte[] lookupCommand = "POST /lookup HTTP/1.0".getBytes();
080: protected byte[] rmiCommand = "POST /rmi HTTP/1.0".getBytes();
081:
082: /**
083: * Constructs an object importer.
084: *
085: * <p>Remote objects are imported from the web server that the given
086: * applet has been loaded from.
087: *
088: * @param applet the applet loaded from the <code>Webserver</code>.
089: */
090: public ObjectImporter(Applet applet) {
091: URL codebase = applet.getCodeBase();
092: orgServername = servername = codebase.getHost();
093: orgPort = port = codebase.getPort();
094: }
095:
096: /**
097: * Constructs an object importer.
098: *
099: * <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: */
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: */
121: public Object getObject(String name) {
122: try {
123: return lookupObject(name);
124: } catch (ObjectNotFoundException e) {
125: return null;
126: }
127: }
128:
129: /**
130: * Sets an http proxy server. After this method is called, the object
131: * importer connects a server through the http proxy server.
132: */
133: public void setHttpProxy(String host, int port) {
134: String proxyHeader = "POST http://" + orgServername + ":"
135: + 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: */
152: public Object lookupObject(String name)
153: throws ObjectNotFoundException {
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
166: .getInputStream());
167: skipHeader(in);
168: ObjectInputStream din = new ObjectInputStream(in);
169: int n = din.readInt();
170: String classname = din.readUTF();
171: din.close();
172: dout.close();
173: sock.close();
174:
175: if (n >= 0)
176: return createProxy(n, classname);
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 = new Class[] {
186: ObjectImporter.class, int.class };
187:
188: private Object createProxy(int oid, String classname)
189: throws Exception {
190: Class c = Class.forName(classname);
191: Constructor cons = c.getConstructor(proxyConstructorParamTypes);
192: return cons
193: .newInstance(new Object[] { this , new Integer(oid) });
194: }
195:
196: /**
197: * Calls a method on a remote object.
198: * It sends a POST request to the server (via an http proxy server
199: * if needed).
200: *
201: * <p>This method is called by only proxy objects.
202: */
203: public Object call(int objectid, int methodid, Object[] args)
204: throws RemoteException {
205: boolean result;
206: Object rvalue;
207: String errmsg;
208:
209: try {
210: /* This method establishes a raw tcp connection for sending
211: * a POST message. Thus the object cannot communicate a
212: * remote object beyond a fire wall. To avoid this problem,
213: * the connection should be established with a mechanism
214: * collaborating a proxy server. Unfortunately, java.lang.URL
215: * does not seem to provide such a mechanism.
216: *
217: * You might think that using HttpURLConnection is a better
218: * way than constructing a raw tcp connection. Unfortunately,
219: * URL.openConnection() does not return an HttpURLConnection
220: * object in Netscape's JVM. It returns a
221: * netscape.net.URLConnection object.
222: *
223: * lookupObject() has the same problem.
224: */
225: Socket sock = new Socket(servername, port);
226: OutputStream out = new BufferedOutputStream(sock
227: .getOutputStream());
228: out.write(rmiCommand);
229: out.write(endofline);
230: out.write(endofline);
231:
232: ObjectOutputStream dout = new ObjectOutputStream(out);
233: dout.writeInt(objectid);
234: dout.writeInt(methodid);
235: writeParameters(dout, args);
236: dout.flush();
237:
238: InputStream ins = new BufferedInputStream(sock
239: .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,
285: Object[] params) 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: }
|