PageRenderTime 55ms CodeModel.GetById 16ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 0ms

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

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