You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ObjectImporter.java 10KB

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