PageRenderTime 50ms CodeModel.GetById 14ms app.highlight 29ms RepoModel.GetById 2ms app.codeStats 0ms

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

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