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