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