/interpreter/tags/at2dist220411/src/edu/vub/at/actors/natives/NATFarReference.java

http://ambienttalk.googlecode.com/ · Java · 650 lines · 366 code · 71 blank · 213 comment · 44 complexity · ec17743723cadf73b62954aaa034ead1 MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * NATFarReference.java created on Dec 6, 2006 at 9:53:20 AM
  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 java.util.Iterator;
  30. import java.util.LinkedList;
  31. import java.util.Vector;
  32. import edu.vub.at.actors.ATAsyncMessage;
  33. import edu.vub.at.actors.ATFarReference;
  34. import edu.vub.at.actors.ATLetter;
  35. import edu.vub.at.actors.id.ATObjectID;
  36. import edu.vub.at.actors.natives.NATActorMirror.NATLetter;
  37. import edu.vub.at.actors.net.ConnectionListener;
  38. import edu.vub.at.eval.Evaluator;
  39. import edu.vub.at.exceptions.InterpreterException;
  40. import edu.vub.at.exceptions.XArityMismatch;
  41. import edu.vub.at.exceptions.XIllegalOperation;
  42. import edu.vub.at.exceptions.XObjectOffline;
  43. import edu.vub.at.exceptions.XSelectorNotFound;
  44. import edu.vub.at.exceptions.XTypeMismatch;
  45. import edu.vub.at.objects.ATBoolean;
  46. import edu.vub.at.objects.ATClosure;
  47. import edu.vub.at.objects.ATContext;
  48. import edu.vub.at.objects.ATField;
  49. import edu.vub.at.objects.ATMethod;
  50. import edu.vub.at.objects.ATNil;
  51. import edu.vub.at.objects.ATObject;
  52. import edu.vub.at.objects.ATTable;
  53. import edu.vub.at.objects.ATTypeTag;
  54. import edu.vub.at.objects.coercion.NativeTypeTags;
  55. import edu.vub.at.objects.grammar.ATSymbol;
  56. import edu.vub.at.objects.mirrors.NativeClosure;
  57. import edu.vub.at.objects.mirrors.PrimitiveMethod;
  58. import edu.vub.at.objects.natives.NATBoolean;
  59. import edu.vub.at.objects.natives.NATByCopy;
  60. import edu.vub.at.objects.natives.NATNil;
  61. import edu.vub.at.objects.natives.NATObject;
  62. import edu.vub.at.objects.natives.NATTable;
  63. import edu.vub.at.objects.natives.NATText;
  64. import edu.vub.at.objects.natives.grammar.AGSymbol;
  65. import edu.vub.at.util.logging.Logging;
  66. /**
  67. *
  68. * NATFarReference is the root of the native classes that represent native far references.
  69. * The AmbientTalk/2 implementation distinguishes between two kinds of far references:
  70. * local and remote far references. The former denote far references to objects hosted by
  71. * actors on the same virtual machine. The latter ones denote far references to remote objects
  72. * that are hosted on a separate virtual (and usually even physical) machine.
  73. *
  74. * This abstract superclass encapsulates all of the code that these two kinds of far references
  75. * have in common. The variabilities are delegated to the subclasses. Subclasses should implement
  76. * an abstract method (transmit) which is invoked by this class when the far reference receives
  77. * a message to be forwarded to the remote principal.
  78. *
  79. * Note that far references are pass by copy and resolve to either a near or a new
  80. * actor-local far reference.
  81. *
  82. * Far references encapsulate the same types as the remote object they represent.
  83. * As such it becomes possible to perform a type test on a far reference as if it
  84. * was performed on the local object itself!
  85. *
  86. * @author tvcutsem
  87. * @author smostinc
  88. */
  89. public abstract class NATFarReference extends NATByCopy implements ATFarReference, ConnectionListener {
  90. // encodes the identity of the far object pointed at
  91. private final ATObjectID objectId_;
  92. // the types with which the remote object is tagged + the FarReference type
  93. private final ATTypeTag[] types_;
  94. /** the state of connectivity of the far reference
  95. * note that this variable is not transient, it will be passed when the ref is
  96. * parameter-passed such that the passed ref is initialized in the correct state
  97. * Note also that access to this variable is synchronized since it may be modified
  98. * by ELVirtualMachine and a thread of FarReferencesThreadPool for remote far references.
  99. */
  100. protected boolean connected_;
  101. private transient Vector disconnectedListeners_; // lazy initialization
  102. private transient Vector reconnectedListeners_; // lazy initialization
  103. private transient Vector takenOfflineListeners_; // lazy initialization
  104. private final transient ELActor owner_;
  105. /**
  106. * 'outbox' stores all messages sent to the receiver object hosted by another actor.
  107. * Each far reference represents an outbox of an actor.
  108. * It contains objects that implement the {@link ATLetter} interface.
  109. *
  110. * Note that access to the outbox is synchronized because it may be modified by:
  111. * -the owner ELActor and ELVirtualMachine in case of local far references.
  112. * -the owner ELActor and a thread of FarReferencesThreadPool in case of remote far references.
  113. */
  114. protected transient LinkedList outbox_ = new LinkedList(); //outbox is not serialized.
  115. protected NATFarReference(ATObjectID objectId, ATTypeTag[] types, ELActor owner, boolean isConnected) {
  116. int size = types.length;
  117. types_ = new ATTypeTag[size + 1];
  118. if (size>0) System.arraycopy(types, 0, types_, 0, size);
  119. types_[size] = NativeTypeTags._FARREF_;
  120. objectId_ = objectId;
  121. connected_ = isConnected;
  122. owner_ = owner;
  123. //register the far reference with the MembershipNotifier to keep track
  124. // of the state of the connection with the remote VM
  125. owner_.getHost().connectionManager_.addConnectionListener(objectId_.getVirtualMachineId(), this);
  126. }
  127. public ATObjectID impl_getObjectId() {
  128. return objectId_;
  129. }
  130. public ATTypeTag[] getTypes() {
  131. return types_;
  132. }
  133. public int hashCode() {
  134. return objectId_.hashCode();
  135. }
  136. public boolean isNativeFarReference() {
  137. return true;
  138. }
  139. public NATFarReference asNativeFarReference() throws XTypeMismatch {
  140. return this;
  141. }
  142. public boolean isFarReference() {
  143. return true;
  144. }
  145. public ATFarReference asFarReference() throws XTypeMismatch {
  146. return this;
  147. }
  148. /* ========================================================
  149. * == Implementation of the ConnectionListener interface ==
  150. * ========================================================
  151. */
  152. // Note that this methods are called from ELVirtualMachine#ConnectionListenerManager,
  153. // i.e. a different actor than the one owning the reference.
  154. public synchronized void connected() {
  155. // sanity check: don't connect twice
  156. if (!connected_) {
  157. connected_ = true;
  158. notifyStateToSendLoop(true);
  159. //this.notify();
  160. notifyConnected();
  161. }
  162. }
  163. public synchronized void disconnected() {
  164. // sanity check: don't disconnect twice
  165. if (connected_) {
  166. // Will only take effect when next trying to send a message
  167. // If currently sending, the message will time out first.
  168. connected_ = false;
  169. notifyStateToSendLoop(false);
  170. notifyDisconnected();
  171. }
  172. }
  173. public synchronized void takenOffline(){
  174. connected_ = false;
  175. notifyStateToSendLoop(false);
  176. notifyTakenOffline();
  177. }
  178. protected abstract void notifyStateToSendLoop(boolean state);
  179. // Methods for registration and notification of disconnection, reconnection, takenOffline listeners.
  180. public synchronized void addDisconnectionListener(ATObject listener) {
  181. if (disconnectedListeners_ == null) {
  182. disconnectedListeners_ = new Vector(1);
  183. }
  184. disconnectedListeners_.add(listener);
  185. if (!connected_) {
  186. triggerListener(listener, "when:disconnected:");
  187. }
  188. }
  189. public synchronized void addReconnectionListener(ATObject listener) {
  190. if (reconnectedListeners_ == null) {
  191. reconnectedListeners_ = new Vector(1);
  192. }
  193. reconnectedListeners_.add(listener);
  194. }
  195. public synchronized void removeDisconnectionListener(ATObject listener) {
  196. if (disconnectedListeners_ != null) {
  197. disconnectedListeners_.remove(listener);
  198. }
  199. }
  200. public synchronized void removeReconnectionListener(ATObject listener) {
  201. if (reconnectedListeners_ != null) {
  202. reconnectedListeners_.remove(listener);
  203. }
  204. }
  205. public synchronized void addTakenOfflineListener(ATObject listener) {
  206. if (takenOfflineListeners_ == null) {
  207. takenOfflineListeners_ = new Vector(1);
  208. }
  209. takenOfflineListeners_.add(listener);
  210. }
  211. public synchronized void removeTakenOfflineListener(ATObject listener) {
  212. if (takenOfflineListeners_!= null) {
  213. takenOfflineListeners_.remove(listener);
  214. }
  215. }
  216. public synchronized void notifyConnected() {
  217. if (reconnectedListeners_ != null) {
  218. Logging.RemoteRef_LOG.debug("notifyConnected for " + this.toString());
  219. for (Iterator reconnectedIter = reconnectedListeners_.iterator(); reconnectedIter.hasNext();) {
  220. triggerListener((ATObject) reconnectedIter.next(), "when:reconnected:");
  221. }
  222. }
  223. }
  224. public synchronized void notifyDisconnected(){
  225. if (disconnectedListeners_ != null) {
  226. Logging.RemoteRef_LOG.debug("notifyDisconnected for " + this.toString());
  227. for (Iterator disconnectedIter = disconnectedListeners_.iterator(); disconnectedIter.hasNext();) {
  228. triggerListener((ATObject) disconnectedIter.next(), "when:disconnected:");
  229. }
  230. }
  231. }
  232. /**
  233. * Taking offline an object results in a "logical" disconnection of the far remote reference.
  234. * This means that the ref becomes expired but also disconnected.
  235. * Thus, all disconnectedlisteners and takenOfflineListeners are notified.
  236. */
  237. public synchronized void notifyTakenOffline(){
  238. if (takenOfflineListeners_ != null) {
  239. for (Iterator expiredIter = takenOfflineListeners_.iterator(); expiredIter.hasNext();) {
  240. triggerListener((ATObject) expiredIter.next(), "when:takenOffline:");
  241. }
  242. }
  243. notifyDisconnected();
  244. }
  245. /**
  246. * After deserialization, ensure that only one unique remote reference exists for
  247. * my target.
  248. */
  249. public ATObject meta_resolve() throws InterpreterException, XObjectOffline {
  250. // it may be that the once local target object is now remote!
  251. return ELActor.currentActor().resolve(objectId_, types_, connected_);
  252. }
  253. /* ------------------------------
  254. * -- Message Sending Protocol --
  255. * ------------------------------ */
  256. public ATObject meta_receive(ATAsyncMessage message) throws InterpreterException {
  257. // this method is still called by the event loop of the actor where the reference lives
  258. // so serialization of the message is done by the ELActor sending the message.
  259. NATOutboxLetter letter = new NATOutboxLetter(outbox_, this, message);
  260. this.transmit(letter);
  261. return Evaluator.getNil();
  262. }
  263. protected abstract void transmit(ATLetter letter) throws InterpreterException;
  264. /**
  265. * The only operation that is allowed to be synchronously invoked on far references is '=='
  266. * @throws XIllegalOperation Cannot synchronously invoke a method on a far reference
  267. */
  268. public ATObject impl_invoke(ATObject delegate, ATSymbol atSelector, ATTable arguments) throws InterpreterException {
  269. if (atSelector.equals(NATNil._EQL_NAME_)) {
  270. return super.impl_invoke(delegate, atSelector, arguments);
  271. }
  272. throw new XIllegalOperation("Cannot invoke " + atSelector + " on far reference " + this);
  273. }
  274. public ATObject meta_invokeField(ATObject receiver, ATSymbol selector) throws InterpreterException {
  275. if (selector.equals(NATNil._EQL_NAME_)) {
  276. return super.meta_invokeField(receiver, selector);
  277. }
  278. throw new XIllegalOperation("Cannot synchronously access " + selector + " on far reference " + this);
  279. }
  280. /**
  281. * @return true if and only if the far object is queried for responses to basic operations such as ==
  282. */
  283. public ATBoolean meta_respondsTo(ATSymbol atSelector) throws InterpreterException {
  284. return super.meta_respondsTo(atSelector);
  285. }
  286. /**
  287. * @throws XSelectorNotFound to ensure proper semantics should the interpreter be
  288. * extended such that it allows extending a far reference in the future.
  289. */
  290. public ATClosure meta_doesNotUnderstand(ATSymbol selector) throws InterpreterException {
  291. return super.meta_doesNotUnderstand(selector);
  292. }
  293. /* ------------------------------------
  294. * -- Extension and cloning protocol --
  295. * ------------------------------------ */
  296. /**
  297. * References to objects hosted by another actor are forced to be unique. Therefore
  298. * cloning them throws an XIllegalOperation to avoid inconsistencies by performing
  299. * state updates (through sent messages) after a clone operation.
  300. *
  301. * TODO(discuss) clone: farObject may create a clone on the other actor.
  302. */
  303. public ATObject meta_clone() throws InterpreterException {
  304. throw new XIllegalOperation("Cannot clone far reference " + this);
  305. }
  306. /**
  307. * Cannot create a new instance using a farObject, this should be done either by
  308. * sending rather than invoking new(args) such that the correct method is triggered
  309. * or by invoking newInstance on a farMirror, which will send the call as well.
  310. */
  311. public ATObject meta_newInstance(ATTable initargs) throws InterpreterException {
  312. throw new XIllegalOperation("Cannot create new instance of far reference " + this);
  313. }
  314. /* ------------------------------------------
  315. * -- Slot accessing and mutating protocol --
  316. * ------------------------------------------ */
  317. /**
  318. * @throws XIllegalOperation - cannot select in objects hosted by another actor.
  319. */
  320. public ATClosure meta_select(ATObject receiver, ATSymbol atSelector) throws InterpreterException {
  321. if (atSelector.equals(NATNil._EQL_NAME_)) {
  322. return super.meta_select(receiver, atSelector);
  323. }
  324. throw new XIllegalOperation("Cannot select " + atSelector + " from far reference " + this);
  325. }
  326. /**
  327. * @throws XIllegalOperation - cannot lookup in objects hosted by another actor.
  328. */
  329. public ATObject impl_call(ATSymbol selector, ATTable arguments) throws InterpreterException {
  330. throw new XIllegalOperation("Cannot lookup " + selector + " from far reference " + this);
  331. }
  332. /**
  333. * @throws XIllegalOperation - cannot define in objects hosted by another actor.
  334. */
  335. public ATNil meta_defineField(ATSymbol name, ATObject value) throws InterpreterException {
  336. throw new XIllegalOperation("Cannot define field " + name + " in far reference " + this);
  337. }
  338. /**
  339. * @throws XIllegalOperation - cannot assign in objects hosted by another actor.
  340. */
  341. public ATNil meta_assignField(ATObject receiver, ATSymbol name, ATObject value) throws InterpreterException {
  342. throw new XIllegalOperation("Cannot assign field " + name + " in far reference " + this);
  343. }
  344. /**
  345. * @throws XIllegalOperation - cannot assign in objects hosted by another actor.
  346. */
  347. public ATNil meta_assignVariable(ATSymbol name, ATObject value) throws InterpreterException {
  348. throw new XIllegalOperation("Cannot assign variable " + name + " in far reference " + this);
  349. }
  350. /* ----------------------------------------
  351. * -- Object Relation Testing Protocol --
  352. * ---------------------------------------- */
  353. /**
  354. * @return false unless this == original
  355. */
  356. public ATBoolean meta_isCloneOf(ATObject original) throws InterpreterException {
  357. return NATBoolean.atValue(this == original);
  358. }
  359. /**
  360. * @return false unless this == original
  361. */
  362. public ATBoolean meta_isRelatedTo(ATObject object) throws InterpreterException {
  363. return this.meta_isCloneOf(object);
  364. }
  365. /* ---------------------------------
  366. * -- Structural Access Protocol --
  367. * --------------------------------- */
  368. /**
  369. * @throws XIllegalOperation - cannot add fields to an object in another actor.
  370. */
  371. public ATNil meta_addField(ATField field) throws InterpreterException {
  372. return super.meta_addField(field);
  373. }
  374. /**
  375. * @throws XIllegalOperation - cannot add methods to an object in another actor.
  376. */
  377. public ATNil meta_addMethod(ATMethod method) throws InterpreterException {
  378. return super.meta_addMethod(method);
  379. }
  380. /**
  381. * @throws XSelectorNotFound - as the far object has no fields of its own
  382. */
  383. public ATField meta_grabField(ATSymbol fieldName) throws InterpreterException {
  384. return super.meta_grabField(fieldName);
  385. }
  386. /**
  387. * @return a method if and only if the requested selector is a default operator such as ==
  388. * @throws XSelectorNotFound otherwise
  389. */
  390. public ATMethod meta_grabMethod(ATSymbol methodName) throws InterpreterException {
  391. return super.meta_grabMethod(methodName);
  392. }
  393. /**
  394. * @return an empty table
  395. */
  396. public ATTable meta_listFields() throws InterpreterException {
  397. return super.meta_listFields();
  398. }
  399. /**
  400. * @return a table of default methods
  401. */
  402. public ATTable meta_listMethods() throws InterpreterException {
  403. return super.meta_listMethods();
  404. }
  405. /* ----------------------
  406. * -- Output Protocol --
  407. * ---------------------- */
  408. public NATText meta_print() throws InterpreterException {
  409. return NATText.atValue("<far ref:"+objectId_.getDescription()+">");
  410. }
  411. /* --------------------
  412. * -- Mirror Fields --
  413. * -------------------- */
  414. /**
  415. * The types of a far reference are the types of the remote object
  416. * it points to, plus the FarReference type.
  417. */
  418. public ATTable meta_typeTags() throws InterpreterException {
  419. return NATTable.atValue(types_);
  420. }
  421. /**
  422. * Two far references are equal if and only if they point to the same object Id.
  423. */
  424. public ATBoolean base__opeql__opeql_(ATObject other) throws InterpreterException {
  425. if (this == other) {
  426. return NATBoolean._TRUE_;
  427. } else if (other.isNativeFarReference()) {
  428. ATObjectID otherId = other.asNativeFarReference().impl_getObjectId();
  429. return NATBoolean.atValue(objectId_.equals(otherId));
  430. } else {
  431. return NATBoolean._FALSE_;
  432. }
  433. }
  434. /**
  435. * Performs listener&lt;-apply([ [] ])
  436. *
  437. * @param type the kind of listener, used for logging/debugging purposes only
  438. */
  439. private void triggerListener(ATObject listener, String type) {
  440. // listener<-apply([ [] ])
  441. Evaluator.trigger(owner_, listener, NATTable.EMPTY, type);
  442. }
  443. public static class NATDisconnectionSubscription extends NATObject {
  444. private static final AGSymbol _REFERENCE_ = AGSymbol.jAlloc("reference");
  445. private static final AGSymbol _HANDLER_ = AGSymbol.jAlloc("handler");
  446. private static final AGSymbol _CANCEL_ = AGSymbol.jAlloc("cancel");
  447. public NATDisconnectionSubscription(final NATFarReference reference, ATClosure handler) throws InterpreterException {
  448. this.meta_defineField(_REFERENCE_, reference);
  449. this.meta_defineField(_HANDLER_, handler);
  450. this.meta_addMethod(new PrimitiveMethod(_CANCEL_, NATTable.EMPTY) {
  451. public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException {
  452. int arity = arguments.base_length().asNativeNumber().javaValue;
  453. if (arity != 0) {
  454. throw new XArityMismatch("cancel", 0, arity);
  455. }
  456. NATDisconnectionSubscription scope = NATDisconnectionSubscription.this;
  457. NATFarReference reference = scope.impl_invokeAccessor(scope, _REFERENCE_, NATTable.EMPTY).asNativeFarReference();
  458. if(reference instanceof NATFarReference) {
  459. NATFarReference remote = (NATFarReference)reference;
  460. ATObject handler = scope.impl_callField(_HANDLER_);
  461. remote.removeDisconnectionListener(handler.asClosure());
  462. }
  463. return Evaluator.getNil();
  464. }
  465. });
  466. }
  467. public NATText meta_print() throws InterpreterException {
  468. return NATText.atValue("<disconnection subscription:"+ this.impl_invokeAccessor(this, _REFERENCE_,NATTable.EMPTY)+">");
  469. }
  470. }
  471. public static class NATReconnectionSubscription extends NATObject {
  472. private static final AGSymbol _REFERENCE_ = AGSymbol.jAlloc("reference");
  473. private static final AGSymbol _HANDLER_ = AGSymbol.jAlloc("handler");
  474. private static final AGSymbol _CANCEL_ = AGSymbol.jAlloc("cancel");
  475. public NATReconnectionSubscription(final NATFarReference reference, ATClosure handler) throws InterpreterException {
  476. this.meta_defineField(_REFERENCE_, reference);
  477. this.meta_defineField(_HANDLER_, handler);
  478. this.meta_addMethod(new PrimitiveMethod(_CANCEL_, NATTable.EMPTY) {
  479. public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException {
  480. int arity = arguments.base_length().asNativeNumber().javaValue;
  481. if (arity != 0) {
  482. throw new XArityMismatch("cancel", 0, arity);
  483. }
  484. NATReconnectionSubscription scope = NATReconnectionSubscription.this;
  485. NATFarReference reference = scope.impl_invokeAccessor(scope, _REFERENCE_,NATTable.EMPTY).asNativeFarReference();
  486. if(reference instanceof NATFarReference) {
  487. NATFarReference remote = (NATFarReference)reference;
  488. ATObject handler = scope.impl_callField(_HANDLER_);
  489. remote.removeReconnectionListener(handler.asClosure());
  490. }
  491. return Evaluator.getNil();
  492. }
  493. });
  494. }
  495. public NATText meta_print() throws InterpreterException {
  496. return NATText.atValue("<reconnection subscription:"+ this.impl_invokeAccessor(this, _REFERENCE_,NATTable.EMPTY)+">");
  497. }
  498. }
  499. public static class NATExpiredSubscription extends NATObject {
  500. private static final AGSymbol _REFERENCE_ = AGSymbol.jAlloc("reference");
  501. private static final AGSymbol _HANDLER_ = AGSymbol.jAlloc("handler");
  502. private static final AGSymbol _CANCEL_ = AGSymbol.jAlloc("cancel");
  503. public NATExpiredSubscription(final NATFarReference reference, ATClosure handler) throws InterpreterException {
  504. this.meta_defineField(_REFERENCE_, reference);
  505. this.meta_defineField(_HANDLER_, handler);
  506. this.meta_addMethod(new PrimitiveMethod(_CANCEL_, NATTable.EMPTY) {
  507. public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException {
  508. int arity = arguments.base_length().asNativeNumber().javaValue;
  509. if (arity != 0) {
  510. throw new XArityMismatch("cancel", 0, arity);
  511. }
  512. NATExpiredSubscription scope = NATExpiredSubscription.this;
  513. NATFarReference reference = scope.impl_invokeAccessor(scope, _REFERENCE_,NATTable.EMPTY).asNativeFarReference();
  514. if(reference instanceof NATFarReference) {
  515. NATFarReference remote = (NATFarReference)reference;
  516. ATObject handler = scope.impl_callField(_HANDLER_);
  517. remote.removeTakenOfflineListener(handler.asClosure());
  518. }
  519. return Evaluator.getNil();
  520. }
  521. });
  522. }
  523. public NATText meta_print() throws InterpreterException {
  524. return NATText.atValue("<expired subscription:"+ this.impl_invokeAccessor(this, _REFERENCE_,NATTable.EMPTY)+">");
  525. }
  526. }
  527. /**
  528. * An outbox letter is a named subclass of NATLetter (See {@link ATLetter} interface),
  529. * wrapping the AmbientTalk message that is going to be transmitted and its serialized version.
  530. * The constructor is still executed by the {@link ELActor} that schedules a message to be transmitted.
  531. * This ensures that the serialization of the message happens in the correct thread.
  532. */
  533. public static class NATOutboxLetter extends NATLetter {
  534. /** the serialized version of the original message to be sent.
  535. * Used to be able to retract a message without involving
  536. * serialization/desearialization because sometimes o != resolve(pass(o))
  537. */
  538. private final Packet serializedMessage_;
  539. public NATOutboxLetter(LinkedList outbox, ATObject receiver,
  540. ATObject message) throws InterpreterException {
  541. super(outbox, receiver, message);
  542. serializedMessage_ = new Packet(message.toString(),NATTable.of(receiver, message));
  543. }
  544. public ATLetter asLetter() { return this; }
  545. public NATOutboxLetter asNativeOutboxLetter() { return this; }
  546. public Packet impl_getSerializedMessage() {
  547. return serializedMessage_;
  548. }
  549. }
  550. // both local and far references retract messages in the same way.
  551. // however, it won't be the same thread calling this method:
  552. // in local far references it is called by the owner ELActor thread, while
  553. // in remote far references, it is called by a thread of the FarReferencesThreadPool.
  554. public ATTable impl_retractUnsentMessages() throws InterpreterException{
  555. synchronized (this) {
  556. if (outbox_.size() > 0 ) {
  557. // def messages := [];
  558. // outbox.each: { |letter| letter.cancel(); messages := messages + [letter.message] };
  559. // messages;
  560. ATObject[] messages = new ATObject[outbox_.size()];
  561. int i = 0;
  562. for (Iterator iterator = outbox_.iterator(); iterator.hasNext();) {
  563. ATLetter letter = (ATLetter) iterator.next();
  564. // no need to call cancel(), just clear the outbox at the end
  565. // since we'll be removing all letters. Also, calling cancel()
  566. // leads to a ConcurrentModificationException since it will try
  567. // to remove the letter while we are iterating over the list
  568. // letter.base_cancel();
  569. messages[i] = letter.base_message().asAsyncMessage();
  570. i = i + 1;
  571. }
  572. outbox_.clear(); // empty the outbox
  573. return NATTable.atValue(messages);
  574. }
  575. }
  576. // if you arrive here outbox_.size == 0 thus it returns [];
  577. return NATTable.EMPTY;
  578. }
  579. }