PageRenderTime 46ms CodeModel.GetById 16ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 1ms

/interpreter/tags/at2dist110511/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 */
 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&lt;-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}