PageRenderTime 55ms CodeModel.GetById 2ms app.highlight 45ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://ambienttalk.googlecode.com/
Java | 627 lines | 334 code | 49 blank | 244 comment | 16 complexity | b2ceba55cbd7bea004fd849cb9c8c8a0 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * ELActor.java created on 27-dec-2006 at 16:17:23
  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
 30
 31import java.lang.reflect.InvocationTargetException;
 32import java.lang.reflect.Method;
 33import java.util.EventListener;
 34
 35import edu.vub.at.actors.ATActorMirror;
 36import edu.vub.at.actors.ATAsyncMessage;
 37import edu.vub.at.actors.ATFarReference;
 38import edu.vub.at.actors.eventloops.BlockingFuture;
 39import edu.vub.at.actors.eventloops.Callable;
 40import edu.vub.at.actors.eventloops.Event;
 41import edu.vub.at.actors.eventloops.EventLoop;
 42import edu.vub.at.actors.id.ATObjectID;
 43import edu.vub.at.actors.id.ActorID;
 44import edu.vub.at.actors.net.comm.Address;
 45import edu.vub.at.eval.Evaluator;
 46import edu.vub.at.exceptions.InterpreterException;
 47import edu.vub.at.exceptions.XClassNotFound;
 48import edu.vub.at.exceptions.XIOProblem;
 49import edu.vub.at.exceptions.XIllegalOperation;
 50import edu.vub.at.exceptions.XObjectOffline;
 51import edu.vub.at.objects.ATAbstractGrammar;
 52import edu.vub.at.objects.ATClosure;
 53import edu.vub.at.objects.ATMethod;
 54import edu.vub.at.objects.ATObject;
 55import edu.vub.at.objects.ATTable;
 56import edu.vub.at.objects.ATTypeTag;
 57import edu.vub.at.objects.mirrors.Reflection;
 58import edu.vub.at.objects.natives.NATContext;
 59import edu.vub.at.objects.natives.NATObject;
 60import edu.vub.at.objects.natives.NATTable;
 61import edu.vub.at.objects.natives.OBJLexicalRoot;
 62import edu.vub.at.objects.symbiosis.Symbiosis;
 63import edu.vub.at.util.logging.Logging;
 64
 65/**
 66 * An instance of the class ELActor represents a programmer-defined
 67 * AmbientTalk/2 actor. The event queue of the actor event loop serves as the
 68 * actor's 'meta-level' queue.
 69 *
 70 * The events in the 'meta-level' queue are handled by the actor's mirror object.
 71 * This mirror is normally an instance of NATActorMirror, but it can be any
 72 * programmer-defined object that adheres to the ATActorMirror interface.
 73 *
 74 * @author tvcutsem
 75 */
 76public class ELActor extends EventLoop {
 77	
 78	/**
 79	 * A thread-local variable that contains the 'default actor' to use
 80	 * when there is currently no ELActor event loop thread running.
 81	 * This is primarily useful for performing unit tests where an actor
 82	 * is automatically created when actor semantics is required.
 83	 * 
 84	 * A warning is printed to the log because using the default actor should
 85	 * only be used for testing purposes.
 86	 */
 87	private static final ThreadLocal _DEFAULT_ACTOR_ = new ThreadLocal() {
 88		protected synchronized Object initialValue() {
 89			Logging.Actor_LOG.warn("Creating a default actor for thread " + Thread.currentThread());
 90			try {
 91				ELVirtualMachine host = new ELVirtualMachine(
 92						Evaluator.getNil(),
 93						new SharedActorField[] { },
 94						ELVirtualMachine._DEFAULT_GROUP_NAME_);
 95				return host.createEmptyActor().getFarHost();
 96			} catch (InterpreterException e) {
 97				throw new RuntimeException("Failed to initialize default actor: " + e.getMessage());
 98			}
 99		}
100	};
101	
102	/**
103	 * Retrieves the currently running actor. If there is no running actor thread,
104	 * this returns the value stored in the thread-local default actor field.
105	 */
106	public static final ELActor currentActor() {
107		try {
108			return ((ELActor) EventLoop.currentEventLoop());
109		} catch (ClassCastException e) {
110			// current event loop is not an actor event loop
111		} catch (IllegalStateException e) {
112			// current thread is not an event loop
113		}
114		Logging.Actor_LOG.warn("Asked for an actor in non-actor thread " + Thread.currentThread());
115		return (ELActor) _DEFAULT_ACTOR_.get();
116	}
117
118	private ATActorMirror mirror_;
119	private final ActorID id_;
120	protected final ELVirtualMachine host_;
121	protected final ReceptionistsSet receptionists_;
122	
123	/*
124	 * This object is created when the actor is initialized: i.e. it is the passed
125	 * version of the isolate that was passed to the actor: primitive by the creating actor.
126	 */
127	private NATObject behaviour_;
128	
129	public ELActor(ATActorMirror mirror, ELVirtualMachine host) {
130		super("actor " + mirror.toString());
131		this.start();
132		id_ = new ActorID();
133		mirror_ = mirror;
134		host_ = host;
135		receptionists_ = new ReceptionistsSet(this);
136	}
137	
138	/** constructor dedicated to initialization of discovery actor */
139	protected ELActor(ELVirtualMachine host) {
140		super("discovery actor");
141		this.start();
142		id_ = new ActorID();
143		NATActorMirror mirror = new NATActorMirror(host);
144		mirror.setActor(this);
145		mirror_ = mirror;
146		host_ = host;
147		receptionists_ = new ReceptionistsSet(this);
148	}
149
150	/**
151	 * Actor event loops handle events by allowing the meta-level events to
152	 * process themselves.
153	 */
154	public void handle(Event event) {
155		event.process(mirror_);
156	}
157	
158	public ATActorMirror getImplicitActorMirror() { return mirror_; }
159
160	public void setActorMirror(ATActorMirror mirror) { mirror_ = mirror; }
161	
162	public ELVirtualMachine getHost() {
163		return host_;
164	}
165	
166	public ActorID getActorID() {
167		return id_;
168	}
169	
170	public Thread getExecutor() {
171		return processor_;
172	}
173	
174	/**
175	 * Takes offline a given local object such that it is no longer remotely accessible.
176	 * @param object a near reference to the local object to unexport
177	 * @throws XIllegalOperation if the passed object is not part of the export table - i.e. non-remotely accessible.
178	 */
179	public void takeOffline(ATObject object) throws InterpreterException {
180		// receptionist set will check whether ATObject is really remote to me
181		receptionists_.takeOfflineObject(object);
182	}
183	
184	/**
185	 * Disconnects a given local object such that it is no longer remotely accessible.
186	 * @param object a near reference to the local object to disconnect
187	 * @throws XIllegalOperation if the passed object is not part of the export table - i.e. non-remotely accessible.
188	 */
189	public ATObject disconnect(final ATObject object) throws InterpreterException {
190		// receptionist set will check whether ATObject is really remote to me
191		return receptionists_.disconnect(object);
192	}
193	
194	/**
195	 * Resolve the given object id into a local reference. There are three cases to
196	 * consider:
197	 *  A) The given id designates an object local to this actor: the returned object
198	 *     will be a **near** reference to the object (i.e. the object itself)
199	 *  B) The given id designates a far (non-local) object that lives in the same
200	 *     address space as this actor: the returned object will be a **far** reference
201	 *     to the object.
202	 *  C) The given id designates a far object that lives on a remote machine: the
203	 *     returned object will be a **far** and **remote** reference to the object.
204	 *     
205	 * @param id the identifier of the object to resolve
206	 * @return a near or far reference to the object, depending on where the designated object lives
207	 */
208	public ATObject resolve(ATObjectID id, ATTypeTag[] types, boolean isConnected) throws XObjectOffline {
209		return receptionists_.resolveObject(id, types, isConnected);
210	}
211	
212	/* -----------------------------
213	 * -- Initialisation Protocol --
214	 * ----------------------------- */
215
216	/**
217	 * Initialises the root using the contents of the init file stored by
218	 * the hosting virtual machine.
219	 * @throws InterpreterException
220	 */
221	protected void initRootObject() throws InterpreterException {
222		ATAbstractGrammar initialisationCode = host_.getInitialisationCode();
223		
224		// evaluate the initialization code in the context of the global scope
225		NATObject globalScope = Evaluator.getGlobalLexicalScope();
226		NATContext initCtx = new NATContext(globalScope, globalScope);
227		
228		initialisationCode.meta_eval(initCtx);
229	}
230	
231	/**
232	 * Initialises various fields in the lexical root of the actor, which are defined in the 
233	 * context of every actor. Candidates are a "system" field which allows the program to 
234	 * perform IO operations or a "~" field denoting the current working directory.
235	 * 
236	 * @throws InterpreterException when initialisation of a field fails
237	 */
238	protected void initSharedFields() throws InterpreterException {
239		SharedActorField[] fields = host_.getFieldsToInitialize();
240		NATObject globalScope = Evaluator.getGlobalLexicalScope();
241		
242		for (int i = 0; i < fields.length; i++) {
243			SharedActorField field = fields[i];
244			ATObject value = field.initialize();
245			if (value != null) {
246				globalScope.meta_defineField(field.getName(), value);
247			}
248		}
249	}
250	
251	// Events to be processed by the actor event loop
252	
253	/**
254	 * The initial event sent by the actor mirror to its event loop to intialize itself.
255	 * @param future the synchronization point with the creating actor, needs to be fulfilled with a far ref to the behaviour.
256	 * @param parametersPkt the serialized parameters for the initialization code
257	 * @param initcodePkt the serialized initialization code (e.g. the code in 'actor: { code }')
258	 */
259	protected void event_init(final BlockingFuture future, final Packet parametersPkt, final Packet initcodePkt) {
260		receive(new Event("init("+this+")") {
261			public void process(Object byMyself) {
262				try {
263					behaviour_ = new NATObject();
264					
265					// pass far ref to behaviour to creator actor who is waiting for this
266					future.resolve(receptionists_.exportObject(behaviour_,"behaviour of "+byMyself));
267					
268					// !! WARNING: the following code is also duplicated in
269					// ELDiscoveryActor's event_init. If this code is modified, don't
270					// forget to modify that of the discovery actor as well !!
271					
272					// initialize lexically visible fields
273					initSharedFields();
274
275					// go on to initialize the root and all lexically visible fields
276					initRootObject();
277
278					ATObject params = parametersPkt.unpack();
279					ATMethod initCode = initcodePkt.unpack().asMethod();					
280				
281					if (!params.isTable()) {
282						// actor initialized as actor: { ... } => free vars automatically added to a private lexical scope
283						// in this case, params refers to an object that will play the role of lexical scope of the actor's behaviour
284						params.asAmbientTalkObject().setLexicalParent(Evaluator.getGlobalLexicalScope());
285						behaviour_.setLexicalParent(params);
286						params = NATTable.EMPTY;
287					}/* else {
288						// actor initialized as actor: { |vars| ... } => vars become publicly accessible in the actor
289					}*/
290					
291					// initialize the behaviour using the parameters and the code
292					try {
293						initCode.base_applyInScope(params.asTable(), new NATContext(behaviour_, behaviour_));
294					} catch (InterpreterException e) {
295						System.out.println(">>> Exception while initializing actor " + Evaluator.trunc(initCode.base_bodyExpression().toString(),20) + ":\n"+e.getMessage());
296						e.printAmbientTalkStackTrace(System.out);
297						Logging.Actor_LOG.error(behaviour_ + ": could not initialize actor behaviour", e);
298					}
299				} catch (InterpreterException e) {
300					System.out.println(">>> Exception while creating actor: " + e.getMessage());
301					e.printAmbientTalkStackTrace(System.out);
302					Logging.Actor_LOG.error(behaviour_ + ": could not initialize actor behaviour", e);
303				}
304			}
305		});
306	}
307	
308	/**
309	 * The main entry point for any asynchronous self-sends.
310	 * Asynchronous self-sends (i.e. intra-actor sends) do not undergo any form of parameter passing,
311	 * there is no need to serialize and deserialize the message parameter in a Packet.
312	 * 
313	 * When an actor receives an asynchronous message for a given receiver, it delegates control
314	 * to the message itself by means of the message's <tt>process</tt> method.
315	 * 
316	 * This method should only be invoked directly this actor's event loop thread.
317	 * 
318	 * @throws InterpreterException 
319	 */
320	public void acceptSelfSend(final ATObject receiver, final ATAsyncMessage msg) throws InterpreterException {
321		// This is the only place where messages are scheduled
322		// The receiver is always a local object, receive has
323		// already been invoked.
324    	mirror_.base_schedule(receiver, msg);
325	}
326	
327	/**
328	 * This method makes the actor perform:
329	 * <code>closure&lt;-apply(arguments)@[]</code>
330	 * This receiver actor must be the owner of the closure.
331	 */
332	public void event_trigger(final ATObject closure, final ATTable arguments, final String type) {
333		final ELActor owner = this;
334		receive(new Event("trigger("+closure+")") {
335			public void process(Object myActorMirror) {
336				try {
337					owner.acceptSelfSend(closure,
338							       new NATAsyncMessage(Evaluator._APPLY_,
339										               NATTable.of(arguments),
340										               NATTable.EMPTY));
341				} catch (InterpreterException e) {
342					Logging.Actor_LOG.error(myActorMirror + ": error triggering "+ type + " handler with args " + arguments, e);
343				}	
344			}
345		});
346	}
347	
348	/**
349	 * The main entry point for any asynchronous messages sent to this actor
350	 * by external sources.
351	 * @param sender address of the sending actor, used to notify when the receiver has gone offline.
352	 * @param serializedMessage the asynchronous AmbientTalk base-level message to enqueue
353	 */
354	public void event_remoteAccept(final Address sender, final Packet serializedMessage) {
355		receive(new Event("remoteAccept("+serializedMessage+")") {
356			public void process(Object myActorMirror) {
357			  try {
358				// receive a pair [receiver, message]
359				ATObject[] pair = serializedMessage.unpack().asNativeTable().elements_;
360				ATObject receiver = pair[0];
361				ATAsyncMessage msg = pair[1].asAsyncMessage();
362				performAccept(receiver, msg);
363			  } catch (XObjectOffline e) {
364				 host_.event_objectTakenOffline(e.getObjectId(), sender);
365				Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
366			  } catch (InterpreterException e) {
367				Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
368			  } 
369		    }
370		});
371	}
372	
373	/**
374	 * The main entry point for any asynchronous messages sent to this actor 
375	 * by local actors.
376	 * @param ref the local reference of the sending actor, used to notify when the receiver has gone offline.
377	 * @param serializedMessage the asynchronous AmbientTalk base-level message to enqueue
378	 */
379	public void event_localAccept(final NATLocalFarRef ref, final Packet serializedMessage) {
380		receive(new Event("localAccept("+serializedMessage+")") {
381			public void process(Object myActorMirror) {
382			  try {
383				// receive a pair [receiver, message]
384				ATObject[] pair = serializedMessage.unpack().asNativeTable().elements_;
385				ATObject receiver = pair[0];
386				ATAsyncMessage msg = pair[1].asAsyncMessage();
387				performAccept(receiver, msg);
388			  } catch (XObjectOffline e) {
389				  ref.notifyTakenOffline();
390				  Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
391			  } catch (InterpreterException e) {
392				  Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
393			  } 
394		    }
395		});
396	}
397	
398	public void event_serve() {
399		receive(new Event("serve()") {
400			public void process(Object myActorMirror) {
401				try {
402					ATObject result = mirror_.base_serve();
403					Logging.Actor_LOG.debug(mirror_ + ": serve() returned " + result);
404				} catch (InterpreterException e) {
405					System.out.println(">>> Exception in actor " + myActorMirror + ": "+e.getMessage());
406					e.printAmbientTalkStackTrace(System.out);
407					Logging.Actor_LOG.error(mirror_ + ": serve() failed ", e);
408				}
409			}
410		});
411	}
412	
413	private void performAccept(ATObject receiver, ATAsyncMessage msg) {
414		try {
415			ATObject result = mirror_.base_receive(receiver, msg);
416			Logging.Actor_LOG.debug(mirror_ + ": scheduling "+ msg + " returned " + result);
417			
418			// signal a serve event for every message that is accepted
419			// event_serve();
420		} catch (InterpreterException e) {
421			System.out.println(">>> Exception in actor " + getImplicitActorMirror() + ": "+e.getMessage());
422			e.printAmbientTalkStackTrace(System.out);
423			Logging.Actor_LOG.error(mirror_ + ": scheduling "+ msg + " failed ", e);
424		}
425	}
426	
427	/**
428	 * This method is invoked by a coercer in order to schedule a purely asynchronous symbiotic invocation
429	 * from the Java world.
430	 * 
431	 * This method schedules the call for asynchronous execution. Its return value and or raised exceptions
432	 * are ignored. This method should only be used for {@link Method} objects whose return type is <tt>void</tt>
433	 * and whose declaring class is a subtype of {@link EventListener}. It represents asynchronous method
434	 * invocations from the Java world to the AmbientTalk world.
435	 * 
436	 * @param principal the AmbientTalk object owned by this actor on which to invoke the method
437	 * @param method the Java method that was symbiotically invoked on the principal
438	 * @param args the arguments to the Java method call, already converted into AmbientTalk values
439	 */
440	public void event_symbioticInvocation(final ATObject principal, final Method method, final ATObject[] args) {
441		receive(new Event("asyncSymbioticInv of "+method.getName()) {
442			public void process(Object actorMirror) {
443				try {
444					Reflection.downInvocation(principal, method, args);
445				} catch (InterpreterException e) {
446					System.out.println(">>> Exception in actor " + actorMirror + ": "+e.getMessage());
447					e.printAmbientTalkStackTrace(System.out);
448					Logging.Actor_LOG.error("asynchronous symbiotic invocation of "+method.getName()+" failed", e);
449				}
450			}
451		});
452	}
453	
454	/**
455	 * This method is invoked by a coercer in order to schedule a symbiotic invocation
456	 * from the Java world, which should be synchronous to the Java thread, but which
457	 * must be scheduled asynchronously to comply with the AT/2 actor model.
458	 * 
459	 * The future returned by this method makes the calling (Java) thread <b>block</b> upon
460	 * accessing its value, waiting until the actor has processed the symbiotic invocation.
461	 * 
462	 * @param principal the AmbientTalk object owned by this actor on which to invoke the method
463	 * @param meth the Java method that was symbiotically invoked on the principal
464	 * @param args the arguments to the Java method call, already converted into AmbientTalk values
465	 * @return a Java future that is resolved with the result of the symbiotic invocation
466	 * @throws Exception if the symbiotic invocation fails
467	 */
468	public BlockingFuture sync_event_symbioticInvocation(final ATObject principal, final Method meth, final ATObject[] args) throws Exception {
469		return receiveAndReturnFuture("syncSymbioticInv of " + meth.getName(), new Callable() {
470			public Object call(Object actorMirror) throws Exception {
471				Class targetType = meth.getReturnType();
472				ATObject[] actualArgs = args;
473				// if the return type is BlockingFuture, the first argument should specify the type
474				// of the value with which BlockingFuture will be resolved
475				if (targetType.equals(BlockingFuture.class)) {
476					if ((meth.getParameterTypes().length > 0) && (meth.getParameterTypes()[0].equals(Class.class))) {
477						targetType = args[0].asJavaClassUnderSymbiosis().getWrappedClass();
478						// drop first argument, it only exists to specify the targetType
479						ATObject[] newArgs = new ATObject[args.length-1];
480						System.arraycopy(args, 1, newArgs, 0, newArgs.length);
481						actualArgs = newArgs;
482					}
483				}
484				
485				ATObject result = Reflection.downInvocation(principal, meth, actualArgs);
486				// SUPPORT FOR FUTURES
487				if (Symbiosis.isAmbientTalkFuture(result)) {
488					Logging.Actor_LOG.debug("Symbiotic futures: symbiotic call to " + meth.getName() + " returned an AT future");
489					return Symbiosis.ambientTalkFutureToJavaFuture(result, targetType);
490				} else {
491					// return the proper value immediately
492					return Symbiosis.ambientTalkToJava(result, targetType);
493				}
494			}
495		});
496	}
497	
498	/**
499	 * This method is invoked by a coercer in order to schedule a symbiotic invocation
500	 * of a method from java.lang.Object from the Java world, which should be synchronous
501	 * to the Java thread, but which
502	 * must be scheduled asynchronously to comply with the AT/2 actor model.
503	 * 
504	 * The future returned by this method makes the calling (Java) thread <b>block</b> upon
505	 * accessing its value, waiting until the actor has processed the symbiotic invocation.
506	 * 
507	 * Note: the parameter meth must be a method declared on the class java.lang.Object
508	 * (i.e. toString, hashCode and equals). The invocation is simply forwarded directly
509	 * to the principal with no conversion to an AmbientTalk invocation.
510	 * 
511	 * @param principal the AmbientTalk object owned by this actor on which to invoke the method
512	 * @param meth the Java method that was symbiotically invoked on the principal
513	 * @param args the arguments to the Java method call, already converted into AmbientTalk values
514	 * @return a Java future that is resolved with the result of the symbiotic invocation
515	 * @throws Exception if the symbiotic invocation fails
516	 */
517	public BlockingFuture sync_event_symbioticForwardInvocation(final ATObject principal, final Method meth, final Object[] args) throws Exception {
518		return receiveAndReturnFuture("syncSymbioticInv of " + meth.getName(), new Callable() {
519			public Object call(Object actorMirror) throws Exception {
520				try {
521					return meth.invoke(principal, args);		
522				} catch (InvocationTargetException e) {
523					if (e instanceof Exception) { 
524						throw (Exception) e.getTargetException();
525					} else {
526						throw e;
527					}
528				}
529			}
530		});
531	}
532	
533	/**
534	 * This method should only be used for purposes such as the IAT shell or unit testing.
535	 * It allows an external thread to make this actor evaluate an arbitrary expression.
536	 * 
537	 * @param ast an abstract syntax tree to be evaluated by the receiving actor (in the
538	 * scope of its behaviour).
539	 * @return the result of the evaluation
540	 * @throws InterpreterException if the evaluation fails
541	 */
542	public ATObject sync_event_eval(final ATAbstractGrammar ast) throws InterpreterException {
543		try {
544			return (ATObject) receiveAndWait("nativeEval("+ast+")", new Callable() {
545				public Object call(Object inActor) throws Exception {
546				    return OBJLexicalRoot._INSTANCE_.base_eval_in_(ast, behaviour_);
547				}
548			});
549		} catch (Exception e) {
550			if (e instanceof InterpreterException) {
551				throw (InterpreterException) e;
552			} else {
553				Logging.Actor_LOG.fatal("Unexpected Java exception: "+e.getMessage(), e);
554				throw new RuntimeException("Unexpected exception: "+e);
555			}
556		}
557	}
558	
559	/**
560	 * This method should only be used for purposes such as the IAT shell or unit testing.
561	 * It allows an external thread to make this actor evaluate an arbitrary expression and to
562	 * print that expression (into a String).
563	 * 
564	 * @param ast an abstract syntax tree to be evaluated by the receiving actor (in the
565	 * scope of its behaviour).
566	 * @return the printed result of the evaluation as a String
567	 * @throws InterpreterException if the evaluation fails
568	 */
569	public String sync_event_evalAndPrint(final ATAbstractGrammar ast) throws InterpreterException {
570		try {
571			return (String) receiveAndWait("nativeEval("+ast+")", new Callable() {
572				public Object call(Object inActor) throws Exception {
573				    return OBJLexicalRoot._INSTANCE_.base_eval_in_(ast, behaviour_).toString();
574				}
575			});
576		} catch (Exception e) {
577			if (e instanceof InterpreterException) {
578				throw (InterpreterException) e;
579			} else {
580				Logging.Actor_LOG.fatal("Unexpected Java exception: "+e.getMessage(), e);
581				throw new RuntimeException("Unexpected exception: "+e);
582			}
583		}
584	}
585	
586	/**
587	 * This method should only be used for purposes of unit testing. It allows
588	 * arbitary code to be scheduled by external threads such as unit testing frameworks.
589	 */
590	public Object sync_event_performTest(Callable c) throws Exception {
591		return (ATObject) receiveAndWait("performTest("+c+")", c);
592	}
593	
594	/**
595	 * When the discovery manager receives a publication from another local actor or
596	 * another remote VM, the actor is asked to compare the incoming publication against
597	 * a subscription that it had announced previously.
598	 * 
599	 * @param requiredTypePkt serialized form of the type attached to the actor's subscription
600	 * @param myHandler the closure specified as a handler for the actor's subscription
601	 * @param discoveredTypePkt serialized form of the type attached to the new publication
602	 * @param remoteServicePkt serialized form of the reference to the remote discovered service
603	 */
604	public void event_serviceJoined(final Packet requiredTypePkt, final ATFarReference myHandler,
605			                        final Packet discoveredTypePkt, final Packet remoteServicePkt) {
606		receive(new Event("serviceJoined") {
607			public void process(Object myActorMirror) {
608				try {
609					ATTypeTag requiredType = requiredTypePkt.unpack().asTypeTag();
610					ATTypeTag discoveredType = discoveredTypePkt.unpack().asTypeTag();
611					// is there a match?
612					if (discoveredType.base_isSubtypeOf(requiredType).asNativeBoolean().javaValue) {
613						ATObject remoteService = remoteServicePkt.unpack();
614						// myhandler<-apply([remoteService])@[]
615						Evaluator.trigger(myHandler, NATTable.of(remoteService));
616					}
617				} catch (XIOProblem e) {
618					Logging.Actor_LOG.error("Error deserializing joined types or services: ", e.getCause());
619				} catch (XClassNotFound e) {
620					Logging.Actor_LOG.fatal("Could not find class while deserializing joined types or services: ", e.getCause());
621				} catch (InterpreterException e) {
622					Logging.Actor_LOG.error("Error while joining services: ", e);
623				}
624			}
625		});
626	}
627}