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

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

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