PageRenderTime 72ms CodeModel.GetById 29ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 1ms

/interpreter/tags/reactive-pattern-matching/src/edu/vub/at/actors/natives/ELActor.java

http://ambienttalk.googlecode.com/
Java | 515 lines | 267 code | 47 blank | 201 comment | 9 complexity | a12349f3ae13e656c675020735867135 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		Evaluator.getDataflowEngine().propagatePendingUpdates();
152	}
153	
154	public ATActorMirror getImplicitActorMirror() { return mirror_; }
155
156	public void setActorMirror(ATActorMirror mirror) { mirror_ = mirror; }
157	
158	public ELVirtualMachine getHost() {
159		return host_;
160	}
161
162	public ATObject getBehaviour() {
163		return behaviour_;
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 remote object such that it is no longer remotely accessible.
176	 * @param object a **far?** reference to the object to export
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	 * Resolve the given object id into a local reference. There are three cases to
186	 * consider:
187	 *  A) The given id designates an object local to this actor: the returned object
188	 *     will be a **near** reference to the object (i.e. the object itself)
189	 *  B) The given id designates a far (non-local) object that lives in the same
190	 *     address space as this actor: the returned object wil be a **far** reference
191	 *     to the object.
192	 *  C) The given id designates a far object that lives on a remote machine: the
193	 *     returned object will be a **far** and **remote** reference to the object.
194	 *     
195	 * @param id the identifier of the object to resolve
196	 * @return a near or far reference to the object, depending on where the designated object lives
197	 */
198	public ATObject resolve(ATObjectID id, ATTypeTag[] types) throws XObjectOffline {
199		return receptionists_.resolveObject(id, types);
200	}
201	
202	/* -----------------------------
203	 * -- Initialisation Protocol --
204	 * ----------------------------- */
205
206	/**
207	 * Initialises the root using the contents of the init file stored by
208	 * the hosting virtual machine.
209	 * @throws InterpreterException
210	 */
211	protected void initRootObject() throws InterpreterException {
212		ATAbstractGrammar initialisationCode = host_.getInitialisationCode();
213		
214		// evaluate the initialization code in the context of the global scope
215		NATObject globalScope = Evaluator.getGlobalLexicalScope();
216		NATContext initCtx = new NATContext(globalScope, globalScope);
217		
218		initialisationCode.meta_eval(initCtx);
219	}
220	
221	/**
222	 * Initialises various fields in the lexical root of the actor, which are defined in the 
223	 * context of every actor. Candidates are a "system" field which allows the program to 
224	 * perform IO operations or a "~" field denoting the current working directory.
225	 * 
226	 * @throws InterpreterException when initialisation of a field fails
227	 */
228	protected void initSharedFields() throws InterpreterException {
229		SharedActorField[] fields = host_.getFieldsToInitialize();
230		NATObject globalScope = Evaluator.getGlobalLexicalScope();
231		
232		for (int i = 0; i < fields.length; i++) {
233			SharedActorField field = fields[i];
234			ATObject value = field.initialize();
235			if (value != null) {
236				globalScope.meta_defineField(field.getName(), value);
237			}
238		}
239	}
240	
241	// Events to be processed by the actor event loop
242	
243	/**
244	 * The initial event sent by the actor mirror to its event loop to intialize itself.
245	 * @param future the synchronization point with the creating actor, needs to be fulfilled with a far ref to the behaviour.
246	 * @param parametersPkt the serialized parameters for the initialization code
247	 * @param initcodePkt the serialized initialization code (e.g. the code in 'actor: { code }')
248	 */
249	protected void event_init(final BlockingFuture future, final Packet parametersPkt, final Packet initcodePkt) {
250		receive(new Event("init("+this+")") {
251			public void process(Object byMyself) {
252				try {
253					behaviour_ = new NATObject();
254					
255					// pass far ref to behaviour to creator actor who is waiting for this
256					future.resolve(receptionists_.exportObject(behaviour_));
257					
258					// !! WARNING: the following code is also duplicated in
259					// ELDiscoveryActor's event_init. If this code is modified, don't
260					// forget to modify that of the discovery actor as well !!
261					
262					// initialize lexically visible fields
263					initSharedFields();
264
265					// go on to initialize the root and all lexically visible fields
266					initRootObject();
267
268					ATTable params = parametersPkt.unpack().asTable();
269					ATMethod initCode = initcodePkt.unpack().asMethod();					
270					
271					// initialize the behaviour using the parameters and the code
272					initCode.base_applyInScope(params, new NATContext(behaviour_, behaviour_));
273				} catch (InterpreterException e) {
274					Logging.Actor_LOG.error(behaviour_ + ": could not initialize actor behaviour", e);
275				}
276			}
277		});
278	}
279	
280	/**
281	 * The main entry point for any asynchronous self-sends.
282	 * Asynchronous self-sends (i.e. intra-actor sends) do not undergo any form of parameter passing,
283	 * there is no need to serialize and deserialize the message parameter in a Packet.
284	 * 
285	 * When an actor receives an asynchronous message for a given receiver, it delegates control
286	 * to the message itself by means of the message's <tt>process</tt> method.
287	 * @throws InterpreterException 
288	 */
289	public void acceptSelfSend(final ATObject receiver, final ATAsyncMessage msg) throws InterpreterException {
290		// This is the only place where messages are scheduled
291		// The receiver is always a local object, receive has
292		// already been invoked.
293    	mirror_.base_schedule(receiver, msg);
294    	// signal a serve event for every message that is accepted
295    	this.event_serve();
296	}
297	
298	/**
299	 * The main entry point for any asynchronous messages sent to this actor
300	 * by external sources.
301	 * @param sender address of the sending actor, used to notify when the receiver has gone offline.
302	 * @param serializedMessage the asynchronous AmbientTalk base-level message to enqueue
303	 */
304	public void event_remoteAccept(final Address sender, final Packet serializedMessage) {
305		receive(new Event("remoteAccept("+serializedMessage+")") {
306			public void process(Object myActorMirror) {
307			  try {
308				// receive a pair [receiver, message]
309				ATObject[] pair = serializedMessage.unpack().asNativeTable().elements_;
310				ATObject receiver = pair[0];
311				ATAsyncMessage msg = pair[1].asAsyncMessage();
312				performAccept(receiver, msg);
313			  } catch (XObjectOffline e) {
314				 host_.event_objectTakenOffline(e.getObjectId(), sender);
315				Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
316			  }  catch (InterpreterException e) {
317				Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
318			  } 
319		    }
320		});
321	}
322	
323	/**
324	 * The main entry point for any asynchronous messages sent to this actor 
325	 * by local actors.
326	 * @param ref the local reference of the sending actor, used to notify when the receiver has gone offline.
327	 * @param serializedMessage the asynchronous AmbientTalk base-level message to enqueue
328	 */
329	public void event_localAccept(final NATLocalFarRef ref, final Packet serializedMessage) {
330		receive(new Event("localAccept("+serializedMessage+")") {
331			public void process(Object myActorMirror) {
332			  try {
333				// receive a pair [receiver, message]
334				ATObject[] pair = serializedMessage.unpack().asNativeTable().elements_;
335				ATObject receiver = pair[0];
336				ATAsyncMessage msg = pair[1].asAsyncMessage();
337				performAccept(receiver, msg);
338			  } catch (XObjectOffline e) {
339				  ref.notifyTakenOffline();
340				  Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
341			  } catch (InterpreterException e) {
342				  Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
343			  } 
344		    }
345		});
346	}
347	
348	public void event_serve() {
349		receive(new Event("serve()") {
350			public void process(Object myActorMirror) {
351				try {
352					ATObject result = mirror_.base_serve();
353					// TODO what to do with return value?
354					Logging.Actor_LOG.debug(mirror_ + ": serve() returned " + result);
355				} catch (InterpreterException e) {
356					// TODO what to do with exception?
357					e.printAmbientTalkStackTrace(System.err);
358					Logging.Actor_LOG.error(mirror_ + ": serve() failed ", e);
359				}
360			}
361		});
362	}
363	
364	private void performAccept(ATObject receiver, ATAsyncMessage msg) {
365		try {
366			ATObject result = mirror_.base_receive(receiver, msg);
367			// TODO what to do with return value?
368			Logging.Actor_LOG.debug(mirror_ + ": scheduling "+ msg + " returned " + result);
369			
370			// signal a serve event for every message that is accepted
371			event_serve();
372		} catch (InterpreterException e) {
373			// TODO what to do with exception?
374			e.printAmbientTalkStackTrace(System.err);
375			Logging.Actor_LOG.error(mirror_ + ": scheduling "+ msg + " failed ", e);
376		}
377	}
378	
379	/**
380	 * This method is invoked by a coercer in order to schedule a purely asynchronous symbiotic invocation
381	 * from the Java world.
382	 * 
383	 * This method schedules the call for asynchronous execution. Its return value and or raised exceptions
384	 * are ignored. This method should only be used for {@link Method} objects whose return type is <tt>void</tt>
385	 * and whose declaring class is a subtype of {@link EventListener}. It represents asynchronous method
386	 * invocations from the Java world to the AmbientTalk world.
387	 * 
388	 * @param principal the AmbientTalk object owned by this actor on which to invoke the method
389	 * @param method the Java method that was symbiotically invoked on the principal
390	 * @param args the arguments to the Java method call, already converted into AmbientTalk values
391	 */
392	public void event_symbioticInvocation(final ATObject principal, final Method method, final ATObject[] args) {
393		receive(new Event("asyncSymbioticInv of "+method.getName()) {
394			public void process(Object actorMirror) {
395				try {
396					Reflection.downInvocation(principal, method, args);
397				} catch (InterpreterException e) {
398					e.printAmbientTalkStackTrace(System.err);
399					Logging.Actor_LOG.error("asynchronous symbiotic invocation of "+method.getName()+" failed", e);
400				}
401			}
402		});
403	}
404	
405	/**
406	 * This method is invoked by a coercer in order to schedule a symbiotic invocation
407	 * from the Java world, which should be synchronous to the Java thread, but which
408	 * must be scheduled asynchronously to comply with the AT/2 actor model.
409	 * 
410	 * The future returned by this method makes the calling (Java) thread <b>block</b> upon
411	 * accessing its value, waiting until the actor has processed the symbiotic invocation.
412	 * 
413	 * @param principal the AmbientTalk object owned by this actor on which to invoke the method
414	 * @param meth the Java method that was symbiotically invoked on the principal
415	 * @param args the arguments to the Java method call, already converted into AmbientTalk values
416	 * @return a Java future that is resolved with the result of the symbiotic invocation
417	 * @throws Exception if the symbiotic invocation fails
418	 */
419	public BlockingFuture sync_event_symbioticInvocation(final ATObject principal, final Method meth, final ATObject[] args) throws Exception {
420		return receiveAndReturnFuture("syncSymbioticInv of " + meth.getName(), new Callable() {
421			public Object call(Object actorMirror) throws Exception {
422				Class targetType = meth.getReturnType();
423				ATObject[] actualArgs = args;
424				// if the return type is BlockingFuture, the first argument should specify the type
425				// of the value with which BlockingFuture will be resolved
426				if (targetType.equals(BlockingFuture.class)) {
427					if ((meth.getParameterTypes().length > 0) && (meth.getParameterTypes()[0].equals(Class.class))) {
428						targetType = args[0].asJavaClassUnderSymbiosis().getWrappedClass();
429						// drop first argument, it only exists to specify the targetType
430						ATObject[] newArgs = new ATObject[args.length-1];
431						System.arraycopy(args, 1, newArgs, 0, newArgs.length);
432						actualArgs = newArgs;
433					}
434				}
435				
436				ATObject result = Reflection.downInvocation(principal, meth, actualArgs);
437				// SUPPORT FOR FUTURES
438				if (Symbiosis.isAmbientTalkFuture(result)) {
439					Logging.Actor_LOG.debug("Symbiotic futures: symbiotic call to " + meth.getName() + " returned an AT future");
440					return Symbiosis.ambientTalkFutureToJavaFuture(result, targetType);
441				} else {
442					// return the proper value immediately
443					return Symbiosis.ambientTalkToJava(result, targetType);
444				}
445			}
446		});
447	}
448	
449	/**
450	 * This method should only be used for purposes such as the IAT shell or unit testing.
451	 * It allows an external thread to make this actor evaluate an arbitrary expression.
452	 * 
453	 * @param ast an abstract syntax tree to be evaluated by the receiving actor (in the
454	 * scope of its behaviour).
455	 * @return the result of the evaluation
456	 * @throws InterpreterException if the evaluation fails
457	 */
458	public ATObject sync_event_eval(final ATAbstractGrammar ast) throws InterpreterException {
459		try {
460			return (ATObject) receiveAndWait("nativeEval("+ast+")", new Callable() {
461				public Object call(Object inActor) throws Exception {
462				    return OBJLexicalRoot._INSTANCE_.base_eval_in_(ast, behaviour_);
463				}
464			});
465		} catch (Exception e) {
466			throw (InterpreterException) e;
467		}
468	}
469	
470	
471	/**
472	 * This method should only be used for purposes of unit testing. It allows
473	 * arbitary code to be scheduled by external threads such as unit testing frameworks.
474	 */
475	public Object sync_event_performTest(final Callable c) throws Exception {
476		return (ATObject) receiveAndWait("performTest("+c+")", c);
477	}
478	
479	/**
480	 * When the discovery manager receives a publication from another local actor or
481	 * another remote VM, the actor is asked to compare the incoming publication against
482	 * a subscription that it had announced previously.
483	 * 
484	 * @param requiredTypePkt serialized form of the type attached to the actor's subscription
485	 * @param myHandler the closure specified as a handler for the actor's subscription
486	 * @param discoveredTypePkt serialized form of the type attached to the new publication
487	 * @param remoteServicePkt serialized form of the reference to the remote discovered service
488	 */
489	public void event_serviceJoined(final Packet requiredTypePkt, final ATFarReference myHandler,
490			                        final Packet discoveredTypePkt, final Packet remoteServicePkt) {
491		receive(new Event("serviceJoined") {
492			public void process(Object myActorMirror) {
493				try {
494					ATTypeTag requiredType = requiredTypePkt.unpack().asTypeTag();
495					ATTypeTag discoveredType = discoveredTypePkt.unpack().asTypeTag();
496					// is there a match?
497					if (discoveredType.base_isSubtypeOf(requiredType).asNativeBoolean().javaValue) {
498						ATObject remoteService = remoteServicePkt.unpack();
499						// myhandler<-apply([remoteService])@[]
500						myHandler.meta_receive(
501							new NATAsyncMessage(Evaluator._APPLY_,
502								NATTable.of(NATTable.of(remoteService)),
503								NATTable.EMPTY));
504					}
505				} catch (XIOProblem e) {
506					Logging.Actor_LOG.error("Error deserializing joined types or services: ", e.getCause());
507				} catch (XClassNotFound e) {
508					Logging.Actor_LOG.fatal("Could not find class while deserializing joined types or services: ", e.getCause());
509				} catch (InterpreterException e) {
510					Logging.Actor_LOG.error("Error while joining services: ", e);
511				}
512			}
513		});
514	}
515}