PageRenderTime 45ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/otp.net/Otp/OtpConnection.cs

https://github.com/bmizerany/jungerl
C# | 569 lines | 179 code | 51 blank | 339 comment | 17 complexity | 00160d8fcb4a8051b7f34266aa611a97 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, AGPL-1.0
  1. /*``The contents of this file are subject to the Erlang Public License,
  2. * Version 1.1, (the "License"); you may not use this file except in
  3. * compliance with the License. You should have received a copy of the
  4. * Erlang Public License along with this software. If not, it can be
  5. * retrieved via the world wide web at http://www.erlang.org/.
  6. *
  7. * Software distributed under the License is distributed on an "AS IS"
  8. * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. * the License for the specific language governing rights and limitations
  10. * under the License.
  11. *
  12. * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14. * AB. All Rights Reserved.''
  15. *
  16. * Converted from Java to C# by Vlad Dumitrescu (vlad_Dumitrescu@hotmail.com)
  17. */
  18. using System;
  19. using System.Diagnostics;
  20. namespace Otp
  21. {
  22. /*
  23. * Maintains a connection between a C# process and a remote Erlang,
  24. * C# or C node. The object maintains connection state and allows
  25. * data to be sent to and received from the peer.
  26. *
  27. * <p> Once a connection is established between the local node and a
  28. * remote node, the connection object can be used to send and receive
  29. * messages between the nodes and make rpc calls (assuming that the
  30. * remote node is a real Erlang node).
  31. *
  32. * <p> The various receive methods are all blocking and will return
  33. * only when a valid message has been received or an exception is
  34. * raised.
  35. *
  36. * <p> If an exception occurs in any of the methods in this class, the
  37. * connection will be closed and must be explicitely reopened in order
  38. * to resume communication with the peer.
  39. *
  40. * <p> It is not possible to create an instance of this class
  41. * directly. OtpConnection objects are returned by {@link
  42. * OtpSelf#connect(OtpPeer) OtpSelf.connect()} and {@link
  43. * OtpSelf#accept() OtpSelf.accept()}.
  44. **/
  45. public class OtpConnection:AbstractConnection
  46. {
  47. protected internal OtpSelf _self;
  48. protected internal GenericQueue queue; // messages get delivered here
  49. /*
  50. * Accept an incoming connection from a remote node. Used by {@link
  51. * OtpSelf#accept() OtpSelf.accept()} to create a connection
  52. * based on data received when handshaking with the peer node, when
  53. * the remote node is the connection intitiator.
  54. *
  55. * @exception C#.io.IOException if it was not possible to connect to the peer.
  56. *
  57. * @exception OtpAuthException if handshake resulted in an authentication error
  58. */
  59. // package scope
  60. internal OtpConnection(OtpSelf self, System.Net.Sockets.TcpClient s):base(self, s)
  61. {
  62. this._self = self;
  63. this.queue = new GenericQueue();
  64. System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(Start));
  65. t.IsBackground = true;
  66. t.Name = "connection "+self.node()+" | "+s.ToString();
  67. t.Start();
  68. }
  69. /*
  70. * Intiate and open a connection to a remote node.
  71. *
  72. * @exception C#.io.IOException if it was not possible to connect to the peer.
  73. *
  74. * @exception OtpAuthException if handshake resulted in an authentication error.
  75. */
  76. // package scope
  77. internal OtpConnection(OtpSelf self, OtpPeer other):base(self, other)
  78. {
  79. this._self = self;
  80. this.queue = new GenericQueue();
  81. System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(Start));
  82. t.IsBackground = true;
  83. t.Name = "connection2 "+self.node()+" -> "+other.node();
  84. t.Start();
  85. }
  86. public override void deliver(System.Exception e)
  87. {
  88. queue.put(e);
  89. }
  90. public override void deliver(OtpMsg msg)
  91. {
  92. queue.put(msg);
  93. }
  94. /*
  95. * Get information about the node at the peer end of this
  96. * connection.
  97. *
  98. * @return the {@link OtpPeer Node} representing the peer node.
  99. **/
  100. public virtual OtpPeer getPeer()
  101. {
  102. return peer;
  103. }
  104. /*
  105. * Get information about the node at the local end of this
  106. * connection.
  107. *
  108. * @return the {@link OtpSelf Node} representing the local node.
  109. **/
  110. public virtual new OtpSelf self()
  111. {
  112. return _self;
  113. }
  114. /*
  115. * Return the number of messages currently waiting in the receive
  116. * queue for this connection.
  117. */
  118. public virtual int msgCount()
  119. {
  120. return queue.getCount();
  121. }
  122. /*
  123. * Receive a message from a remote process. This method blocks
  124. * until a valid message is received or an exception is raised.
  125. *
  126. * <p> If the remote node sends a message that cannot be decoded
  127. * properly, the connection is closed and the method throws an
  128. * exception.
  129. *
  130. * @return an object containing a single Erlang term.
  131. *
  132. * @exception C#.io.IOException if the connection is not active or
  133. * a communication error occurs.
  134. *
  135. * @exception Erlang.Exit if an exit signal is
  136. * received from a process on the peer node.
  137. *
  138. * @exception OtpAuthException if the remote node
  139. * sends a message containing an invalid cookie.
  140. **/
  141. public virtual Erlang.Object receive()
  142. {
  143. try
  144. {
  145. return receiveMsg().getMsg();
  146. }
  147. catch (Erlang.DecodeException e)
  148. {
  149. close();
  150. throw new System.IO.IOException(e.Message);
  151. }
  152. }
  153. /*
  154. * Receive a message from a remote process. This method blocks at
  155. * most for the specified time, until a valid message is received or
  156. * an exception is raised.
  157. *
  158. * <p> If the remote node sends a message that cannot be decoded
  159. * properly, the connection is closed and the method throws an
  160. * exception.
  161. *
  162. * @param timeout the time in milliseconds that this operation will
  163. * block. Specify 0 to poll the queue.
  164. *
  165. * @return an object containing a single Erlang term.
  166. *
  167. * @exception C#.io.IOException if the connection is not active or
  168. * a communication error occurs.
  169. *
  170. * @exception Erlang.Exit if an exit signal is
  171. * received from a process on the peer node.
  172. *
  173. * @exception OtpAuthException if the remote node
  174. * sends a message containing an invalid cookie.
  175. *
  176. * @exception InterruptedException if no message if the method
  177. * times out before a message becomes available.
  178. **/
  179. public virtual Erlang.Object receive(long timeout)
  180. {
  181. try
  182. {
  183. return receiveMsg(timeout).getMsg();
  184. }
  185. catch (Erlang.DecodeException e)
  186. {
  187. close();
  188. throw new System.IO.IOException(e.Message);
  189. }
  190. }
  191. /*
  192. * Receive a raw (still encoded) message from a remote process.
  193. * This message blocks until a valid message is received or an
  194. * exception is raised.
  195. *
  196. * <p> If the remote node sends a message that cannot be decoded
  197. * properly, the connection is closed and the method throws an
  198. * exception.
  199. *
  200. * @return an object containing a raw (still encoded) Erlang term.
  201. *
  202. * @exception C#.io.IOException if the connection is not active or
  203. * a communication error occurs.
  204. *
  205. * @exception Erlang.Exit if an exit signal is received from a
  206. * process on the peer node, or if the connection is lost for any
  207. * reason.
  208. *
  209. * @exception OtpAuthException if the remote node
  210. * sends a message containing an invalid cookie.
  211. **/
  212. public virtual OtpInputStream receiveBuf()
  213. {
  214. return receiveMsg().getMsgBuf();
  215. }
  216. /*
  217. * Receive a raw (still encoded) message from a remote process. This
  218. * message blocks at most for the specified time until a valid
  219. * message is received or an exception is raised.
  220. *
  221. * <p> If the remote node sends a message that cannot be decoded
  222. * properly, the connection is closed and the method throws an
  223. * exception.
  224. *
  225. * @param timeout the time in milliseconds that this operation will
  226. * block. Specify 0 to poll the queue.
  227. *
  228. * @return an object containing a raw (still encoded) Erlang term.
  229. *
  230. * @exception C#.io.IOException if the connection is not active or
  231. * a communication error occurs.
  232. *
  233. * @exception Erlang.Exit if an exit signal is received from a
  234. * process on the peer node, or if the connection is lost for any
  235. * reason.
  236. *
  237. * @exception OtpAuthException if the remote node
  238. * sends a message containing an invalid cookie.
  239. *
  240. * @exception InterruptedException if no message if the method
  241. * times out before a message becomes available.
  242. **/
  243. public virtual OtpInputStream receiveBuf(long timeout)
  244. {
  245. return receiveMsg(timeout).getMsgBuf();
  246. }
  247. /*
  248. * Receive a messge complete with sender and recipient information.
  249. *
  250. * @return an {@link OtpMsg OtpMsg} containing the header
  251. * information about the sender and recipient, as well as the actual
  252. * message contents.
  253. *
  254. * @exception C#.io.IOException if the connection is not active or
  255. * a communication error occurs.
  256. *
  257. * @exception Erlang.Exit if an exit signal is received from a
  258. * process on the peer node, or if the connection is lost for any
  259. * reason.
  260. *
  261. * @exception OtpAuthException if the remote node
  262. * sends a message containing an invalid cookie.
  263. **/
  264. public virtual OtpMsg receiveMsg()
  265. {
  266. System.Object o = queue.get();
  267. if (o is OtpMsg)
  268. {
  269. return ((OtpMsg) o);
  270. }
  271. else if (o is System.IO.IOException)
  272. {
  273. throw (System.IO.IOException) o;
  274. }
  275. else if (o is Erlang.Exit)
  276. {
  277. throw (Erlang.Exit) o;
  278. }
  279. else if (o is OtpAuthException)
  280. {
  281. throw (OtpAuthException) o;
  282. }
  283. return null;
  284. }
  285. /*
  286. * Receive a messge complete with sender and recipient information.
  287. * This method blocks at most for the specified time.
  288. *
  289. * @param timeout the time in milliseconds that this operation will
  290. * block. Specify 0 to poll the queue.
  291. *
  292. * @return an {@link OtpMsg OtpMsg} containing the header
  293. * information about the sender and recipient, as well as the actual
  294. * message contents.
  295. *
  296. * @exception C#.io.IOException if the connection is not active or
  297. * a communication error occurs.
  298. *
  299. * @exception Erlang.Exit if an exit signal is received from a
  300. * process on the peer node, or if the connection is lost for any
  301. * reason.
  302. *
  303. * @exception OtpAuthException if the remote node
  304. * sends a message containing an invalid cookie.
  305. *
  306. * @exception InterruptedException if no message if the method
  307. * times out before a message becomes available.
  308. **/
  309. public virtual OtpMsg receiveMsg(long timeout)
  310. {
  311. System.Object o = queue.get(timeout);
  312. if (o is OtpMsg)
  313. {
  314. return ((OtpMsg) o);
  315. }
  316. else if (o is System.IO.IOException)
  317. {
  318. throw (System.IO.IOException) o;
  319. }
  320. else if (o is Erlang.Exit)
  321. {
  322. throw (Erlang.Exit) o;
  323. }
  324. else if (o is OtpAuthException)
  325. {
  326. throw (OtpAuthException) o;
  327. }
  328. return null;
  329. }
  330. /*
  331. * Send a message to a process on a remote node.
  332. *
  333. * @param dest the Erlang PID of the remote process.
  334. * @param msg the message to send.
  335. * @exception C#.io.IOException if the connection is not active
  336. * or a communication error occurs.
  337. **/
  338. public virtual void send(Erlang.Pid dest, Erlang.Object msg)
  339. {
  340. // encode and send the message
  341. base.sendBuf(this._self.pid(), dest, new OtpOutputStream(msg));
  342. }
  343. /*
  344. * Send a message to a named process on a remote node.
  345. *
  346. * @param dest the name of the remote process.
  347. * @param msg the message to send.
  348. *
  349. * @exception C#.io.IOException if the connection is not active or
  350. * a communication error occurs.
  351. **/
  352. public virtual void send(System.String dest, Erlang.Object msg)
  353. {
  354. // encode and send the message
  355. base.sendBuf(this._self.pid(), dest, new OtpOutputStream(msg));
  356. }
  357. /*
  358. * Send a pre-encoded message to a named process on a remote node.
  359. *
  360. * @param dest the name of the remote process.
  361. * @param payload the encoded message to send.
  362. *
  363. * @exception C#.io.IOException if the connection is not active or
  364. * a communication error occurs.
  365. **/
  366. public virtual void sendBuf(System.String dest, OtpOutputStream payload)
  367. {
  368. base.sendBuf(this._self.pid(), dest, payload);
  369. }
  370. /*
  371. * Send a pre-encoded message to a process on a remote node.
  372. *
  373. * @param dest the Erlang PID of the remote process.
  374. * @param msg the encoded message to send.
  375. *
  376. * @exception C#.io.IOException if the connection is not active
  377. * or a communication error occurs.
  378. **/
  379. public virtual void sendBuf(Erlang.Pid dest, OtpOutputStream payload)
  380. {
  381. base.sendBuf(this._self.pid(), dest, payload);
  382. }
  383. /*
  384. * Send an RPC request to the remote Erlang node. This convenience
  385. * function creates the following message and sends it to 'rex' on
  386. * the remote node:
  387. *
  388. * <pre>
  389. * { self, { call, Mod, Fun, Args, user }}
  390. * </pre>
  391. *
  392. * <p> Note that this method has unpredicatble results if the remote
  393. * node is not an Erlang node. </p>
  394. *
  395. * @param mod the name of the Erlang module containing the function to be called.
  396. * @param fun the name of the function to call.
  397. * @param args an array of Erlang terms, to be used as arguments to the function.
  398. *
  399. * @exception C#.io.IOException if the connection is not active
  400. * or a communication error occurs.
  401. **/
  402. public virtual void sendRPC(System.String mod, System.String fun, Erlang.Object[] args)
  403. {
  404. sendRPC(mod, fun, new Erlang.List(args));
  405. }
  406. /*
  407. * Send an RPC request to the remote Erlang node. This convenience
  408. * function creates the following message and sends it to 'rex' on
  409. * the remote node:
  410. *
  411. * <pre>
  412. * { self, { call, Mod, Fun, Args, user }}
  413. * </pre>
  414. *
  415. * <p> Note that this method has unpredicatble results if the remote
  416. * node is not an Erlang node. </p>
  417. *
  418. * @param mod the name of the Erlang module containing the function to be called.
  419. * @param fun the name of the function to call.
  420. * @param args a list of Erlang terms, to be used as arguments to the function.
  421. *
  422. * @exception C#.io.IOException if the connection is not active
  423. * or a communication error occurs.
  424. **/
  425. public virtual void sendRPC(System.String mod, System.String fun, Erlang.List args)
  426. {
  427. Erlang.Object[] rpc = new Erlang.Object[2];
  428. Erlang.Object[] call = new Erlang.Object[5];
  429. /*{self, { call, Mod, Fun, Args, user}} */
  430. call[0] = new Erlang.Atom("call");
  431. call[1] = new Erlang.Atom(mod);
  432. call[2] = new Erlang.Atom(fun);
  433. call[3] = args;
  434. call[4] = new Erlang.Atom("user");
  435. rpc[0] = this._self.pid();
  436. rpc[1] = new Erlang.Tuple(call);
  437. send("rex", new Erlang.Tuple(rpc));
  438. }
  439. /*
  440. * Receive an RPC reply from the remote Erlang node. This
  441. * convenience function receives a message from the remote node, and
  442. * expects it to have the following format:
  443. *
  444. * <pre>
  445. * {rex, Term}
  446. * </pre>
  447. *
  448. * @return the second element of the tuple if the received message
  449. * is a two-tuple, otherwise null. No further error checking is
  450. * performed.
  451. *
  452. * @exception C#.io.IOException if the connection is not active or
  453. * a communication error occurs.
  454. *
  455. * @exception Erlang.Exit if an exit signal is
  456. * received from a process on the peer node.
  457. *
  458. * @exception OtpAuthException if the remote node
  459. * sends a message containing an invalid cookie.
  460. **/
  461. public virtual Erlang.Object receiveRPC()
  462. {
  463. Erlang.Object msg = receive();
  464. Debug.WriteLine("receiveRPC: " + msg.ToString());
  465. if (msg is Erlang.Tuple)
  466. {
  467. Erlang.Tuple t = (Erlang.Tuple) msg;
  468. if (t.arity() == 2)
  469. return t.elementAt(1);
  470. // obs: second element
  471. }
  472. return null;
  473. }
  474. /*
  475. * Create a link between the local node and the specified process on
  476. * the remote node. If the link is still active when the remote
  477. * process terminates, an exit signal will be sent to this
  478. * connection. Use {@link #unlink unlink()} to remove the link.
  479. *
  480. * @param dest the Erlang PID of the remote process.
  481. *
  482. * @exception C#.io.IOException if the connection is not active
  483. * or a communication error occurs.
  484. **/
  485. public virtual void link(Erlang.Pid dest)
  486. {
  487. base.sendLink(this._self.pid(), dest);
  488. }
  489. /*
  490. * Remove a link between the local node and the specified process on
  491. * the remote node. This method deactivates links created with
  492. * {@link #link link()}.
  493. *
  494. * @param dest the Erlang PID of the remote process.
  495. *
  496. * @exception C#.io.IOException if the connection is not active or
  497. * a communication error occurs.
  498. **/
  499. public virtual void unlink(Erlang.Pid dest)
  500. {
  501. base.sendUnlink(this._self.pid(), dest);
  502. }
  503. /*
  504. * Send an exit signal to a remote process.
  505. *
  506. * @param dest the Erlang PID of the remote process.
  507. * @param reason a string describing the exit reason.
  508. *
  509. * @exception C#.io.IOException if the connection is not active or
  510. * a communication error occurs.
  511. **/
  512. public virtual void exit(Erlang.Pid dest, System.String reason)
  513. {
  514. base.sendExit2(this._self.pid(), dest, reason);
  515. }
  516. }
  517. }