PageRenderTime 73ms CodeModel.GetById 16ms app.highlight 52ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/reactive-pattern-matching/src/edu/vub/at/actors/natives/NATFarReference.java

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