/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