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 11KB

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