/interpreter/tags/at_build150307/src/edu/vub/at/actors/natives/ELFarReference.java

http://ambienttalk.googlecode.com/ · Java · 332 lines · 196 code · 52 blank · 84 comment · 33 complexity · e6d1158569a668a45b002125da0a2a9e MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * ELFarReference.java created on 28-dec-2006 at 10:45:41
  4. * (c) Programming Technology Lab, 2006 - 2007
  5. * Authors: Tom Van Cutsem & Stijn Mostinckx
  6. *
  7. * Permission is hereby granted, free of charge, to any person
  8. * obtaining a copy of this software and associated documentation
  9. * files (the "Software"), to deal in the Software without
  10. * restriction, including without limitation the rights to use,
  11. * copy, modify, merge, publish, distribute, sublicense, and/or
  12. * sell copies of the Software, and to permit persons to whom the
  13. * Software is furnished to do so, subject to the following
  14. * conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be
  17. * included in all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  21. * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  22. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  23. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  24. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  25. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  26. * OTHER DEALINGS IN THE SOFTWARE.
  27. */
  28. package edu.vub.at.actors.natives;
  29. import edu.vub.at.actors.ATAsyncMessage;
  30. import edu.vub.at.actors.eventloops.BlockingFuture;
  31. import edu.vub.at.actors.eventloops.Event;
  32. import edu.vub.at.actors.eventloops.EventLoop;
  33. import edu.vub.at.actors.id.ATObjectID;
  34. import edu.vub.at.actors.net.ConnectionListener;
  35. import edu.vub.at.actors.net.Logging;
  36. import edu.vub.at.actors.net.cmd.CMDTransmitATMessage;
  37. import edu.vub.at.eval.Evaluator;
  38. import edu.vub.at.exceptions.InterpreterException;
  39. import edu.vub.at.exceptions.XIOProblem;
  40. import edu.vub.at.objects.ATObject;
  41. import edu.vub.at.objects.ATTable;
  42. import edu.vub.at.objects.natives.NATTable;
  43. import java.util.Iterator;
  44. import java.util.Vector;
  45. import org.jgroups.Address;
  46. import org.jgroups.SuspectedException;
  47. import org.jgroups.TimeoutException;
  48. import org.jgroups.blocks.MessageDispatcher;
  49. /**
  50. * An instance of the class ELFarReference represents the event loop processor for
  51. * a remote far reference. That is, the event queue of this event loop serves as
  52. * an 'outbox' which is dedicated to a certain receiver object hosted by a remote virtual machine.
  53. *
  54. * This event loop handles event from its event queue by trying to transmit them to a remote virtual machine.
  55. *
  56. * @author tvcutsem
  57. */
  58. public final class ELFarReference extends EventLoop implements ConnectionListener {
  59. // When the Far Reference needs to be interrupted, this field will be set to a non-null value
  60. // The handle tests for the presence of such an interrupt and will call the handleInterrupt()
  61. // method if necessary
  62. private BlockingFuture outboxFuture_ = null;
  63. /**
  64. * Signals the far reference that its owning actor has requested to retract unsent messages.
  65. * The interrupt will be handled as soon as the processing of the current event has finished
  66. * @return a blocking future the ELActor thread can wait on.
  67. */
  68. public BlockingFuture setRetractingFuture() {
  69. outboxFuture_ = new BlockingFuture();
  70. // the reception of a new interrupt may awaken a sleeping ELFarReference
  71. // thread, so we interrupt them, forcing them to reevaluate their conditions
  72. processor_.interrupt();
  73. return outboxFuture_;
  74. }
  75. /**
  76. * Resolves the current interrupt's future with the vector of transmission events that are
  77. * in the event queue of the far reference. This is used to retrieve copies of all messages
  78. * being sent through this far reference. Note that this method performs no deserialisation
  79. * of the events into (copied) messages. Such deserialisation needs to be done by an ELActor,
  80. * not the ELFarReference which will execute this method.
  81. */
  82. public void handleRetractRequest() {
  83. outboxFuture_.resolve(eventQueue_.flush());
  84. }
  85. private final ELActor owner_;
  86. private final ATObjectID destination_;
  87. private final MessageDispatcher dispatcher_;
  88. private boolean connected_;
  89. private Vector disconnectedListeners_; // lazy initialization
  90. private Vector reconnectedListeners_; // lazy initialization
  91. public synchronized void addDisconnectionListener(ATObject listener) {
  92. if (disconnectedListeners_ == null) {
  93. disconnectedListeners_ = new Vector(1);
  94. }
  95. disconnectedListeners_.add(listener);
  96. if (!connected_) {
  97. try {
  98. owner_.event_acceptSelfSend(NATAsyncMessage.createAsyncMessage(listener,
  99. listener, Evaluator._APPLY_, NATTable.atValue(new ATObject[] { NATTable.EMPTY })));
  100. } catch (InterpreterException e) {
  101. Logging.RemoteRef_LOG.error(
  102. "error invoking when:disconnected: listener", e);
  103. }
  104. }
  105. }
  106. public synchronized void addReconnectionListener(ATObject listener) {
  107. if (reconnectedListeners_ == null) {
  108. reconnectedListeners_ = new Vector(1);
  109. }
  110. reconnectedListeners_.add(listener);
  111. }
  112. public synchronized void removeDisconnectionListener(ATObject listener) {
  113. if (disconnectedListeners_ != null) {
  114. disconnectedListeners_.remove(listener);
  115. }
  116. }
  117. public synchronized void removeReconnectionListener(ATObject listener) {
  118. if (reconnectedListeners_ != null) {
  119. reconnectedListeners_.remove(listener);
  120. }
  121. }
  122. public ELFarReference(ATObjectID destination, ELActor owner) {
  123. super("far reference " + destination);
  124. destination_ = destination;
  125. owner_ = owner;
  126. connected_ = true;
  127. // register the remote reference with the MembershipNotifier to keep track
  128. // of the state of the connection with the remote VM
  129. owner_.getHost().membershipNotifier_.addConnectionListener(destination_.getVirtualMachineId(), this);
  130. dispatcher_ = owner_.getHost().messageDispatcher_;
  131. }
  132. /**
  133. * Process message transmission events only when the remote reference
  134. * is connected. Otherwise, wait until notified by the <tt>connected</tt> callback.
  135. */
  136. public void handle(Event event) {
  137. synchronized (this) {
  138. while (!connected_ && outboxFuture_ == null) {
  139. try {
  140. this.wait();
  141. } catch (InterruptedException e) { }
  142. }
  143. if(outboxFuture_ != null) {
  144. // re-enqueue the current event in its proper position
  145. receivePrioritized(event);
  146. // flush the queue
  147. handleRetractRequest();
  148. // else is strictly necessary as the handleInterrupt method has side effects,
  149. // removing the current event from the queue, such that it would be incorrect
  150. // to still send it
  151. } else { // if (connected_) {
  152. event.process(this);
  153. }
  154. }
  155. }
  156. /**
  157. * TransmissionEvent is a named subclass of event, which allows access to the message the
  158. * packet it is trying to send which is used, should the event be retracted. Moreover, the
  159. * notion of an explicit constructor which is called by the ELActor scheduling it, ensures
  160. * that the serialization of the message happens in the correct thread.
  161. *
  162. * @author smostinc
  163. */
  164. private class TransmissionEvent extends Event {
  165. public final Packet serializedMessage_;
  166. // Called by ELActor
  167. public TransmissionEvent(ATAsyncMessage msg) throws XIOProblem {
  168. super("transmit("+msg+")");
  169. serializedMessage_ = new Packet(msg.toString(), msg);
  170. }
  171. // Called by ELFarReference
  172. public void process(Object owner) {
  173. Address destination = getDestinationVMAddress();
  174. if (destination != null) {
  175. try {
  176. Object ack = new CMDTransmitATMessage(destination_
  177. .getActorId(), serializedMessage_).send(
  178. dispatcher_, destination);
  179. // non-null return value indicates an exception
  180. if (ack != null) {
  181. Logging.RemoteRef_LOG.error(this
  182. + ": non-null acknowledgement: " + ack);
  183. }
  184. } catch (TimeoutException e) {
  185. Logging.RemoteRef_LOG.warn(this
  186. + ": timeout while trying to transmit message, retrying");
  187. receivePrioritized(this);
  188. } catch (SuspectedException e) {
  189. Logging.RemoteRef_LOG.warn(this
  190. + ": remote object suspected: " + destination_);
  191. receivePrioritized(this);
  192. } catch (Exception e) {
  193. Logging.RemoteRef_LOG.error(this
  194. + ": error upon message transmission:", e);
  195. }
  196. } else {
  197. Logging.RemoteRef_LOG.info(this + ": suspected a disconnection from " + destination_);
  198. connected_ = false;
  199. }
  200. }
  201. }
  202. public void event_transmit(final ATAsyncMessage msg) throws XIOProblem {
  203. receive(new TransmissionEvent(msg));
  204. // the reception of a new event may awaken a sleeping ELFarReference thread,
  205. // so we interrupt the processor, forcing it to reevaluate its conditions
  206. processor_.interrupt();
  207. }
  208. public ATTable retractUnsentMessages() throws InterpreterException {
  209. BlockingFuture eventVectorF = setRetractingFuture();
  210. try {
  211. Vector events = (Vector)eventVectorF.get();
  212. ATObject[] messages = new ATObject[events.size()];
  213. for(int i = 0; i < events.size(); i++) {
  214. TransmissionEvent current = (TransmissionEvent)events.get(i);
  215. messages[i] = current.serializedMessage_.unpack();
  216. }
  217. return NATTable.atValue(messages);
  218. } catch (Exception e) {
  219. e.printStackTrace();
  220. //throw (InterpreterException) e;
  221. return NATTable.EMPTY;
  222. }
  223. }
  224. /* ========================================================
  225. * == Implementation of the ConnectionListener interface ==
  226. * ========================================================
  227. */
  228. public synchronized void connected() {
  229. Logging.RemoteRef_LOG.info(this + ": reconnected to " + destination_);
  230. connected_ = true;
  231. this.notify();
  232. if (reconnectedListeners_ != null) {
  233. for (Iterator reconnectedIter = reconnectedListeners_.iterator(); reconnectedIter.hasNext();) {
  234. ATObject listener = (ATObject) reconnectedIter.next();
  235. try {
  236. owner_.event_acceptSelfSend(
  237. NATAsyncMessage.createAsyncMessage(listener, listener, Evaluator._APPLY_, NATTable.atValue(new ATObject[] { NATTable.EMPTY })));
  238. } catch (InterpreterException e) {
  239. Logging.RemoteRef_LOG.error("error invoking when:reconnected: listener", e);
  240. }
  241. }
  242. }
  243. }
  244. public synchronized void disconnected() {
  245. // Will only take effect when next trying to send a message
  246. // If currently sending, the message will time out first.
  247. Logging.RemoteRef_LOG.info(this + ": disconnected from " + destination_);
  248. connected_ = false;
  249. if (disconnectedListeners_ != null) {
  250. for (Iterator disconnectedIter = disconnectedListeners_.iterator(); disconnectedIter.hasNext();) {
  251. ATObject listener = (ATObject) disconnectedIter.next();
  252. try {
  253. owner_.event_acceptSelfSend(
  254. NATAsyncMessage.createAsyncMessage(listener, listener, Evaluator._APPLY_, NATTable.atValue(new ATObject[] { NATTable.EMPTY })));
  255. } catch (InterpreterException e) {
  256. Logging.RemoteRef_LOG.error("error invoking when:disconnected: listener", e);
  257. }
  258. }
  259. }
  260. }
  261. private Address getDestinationVMAddress() {
  262. return owner_.getHost().vmAddressBook_.getAddressOf(destination_.getVirtualMachineId());
  263. }
  264. public void execute() {
  265. synchronized (this) {
  266. while (eventQueue_.isEmpty() && outboxFuture_ == null) {
  267. try {
  268. this.wait();
  269. } catch (InterruptedException e) { }
  270. }
  271. if (outboxFuture_ != null) {
  272. handleRetractRequest();
  273. } else { // if(! eventQueue_.isEmpty()) {
  274. try {
  275. handle(eventQueue_.dequeue());
  276. } catch (InterruptedException e) { }
  277. }
  278. }
  279. }
  280. }