PageRenderTime 129ms CodeModel.GetById 66ms app.highlight 57ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/at2-build270707/src/edu/vub/at/actors/natives/NATFarReference.java

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