PageRenderTime 49ms CodeModel.GetById 24ms app.highlight 20ms RepoModel.GetById 1ms app.codeStats 0ms

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

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