PageRenderTime 41ms CodeModel.GetById 9ms app.highlight 24ms RepoModel.GetById 2ms app.codeStats 0ms

/interpreter/tags/at2-build190607/src/edu/vub/at/actors/natives/ELActor.java

http://ambienttalk.googlecode.com/
Java | 469 lines | 237 code | 42 blank | 190 comment | 4 complexity | b689c2365520fa423ff5d9dc74a3f279 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.ATTypeTag;
 51import edu.vub.at.objects.ATTable;
 52import edu.vub.at.objects.mirrors.Reflection;
 53import edu.vub.at.objects.natives.NATContext;
 54import edu.vub.at.objects.natives.NATNil;
 55import edu.vub.at.objects.natives.NATObject;
 56import edu.vub.at.objects.natives.NATTable;
 57import edu.vub.at.objects.natives.OBJLexicalRoot;
 58import edu.vub.at.objects.symbiosis.Symbiosis;
 59import edu.vub.at.util.logging.Logging;
 60
 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				  NATNil._INSTANCE_,
 92				  new SharedActorField[] { },
 93				  ELVirtualMachine._DEFAULT_GROUP_NAME_);
 94				return NATActorMirror.createEmptyActor(host, new NATActorMirror(host)).getFarHost();
 95			} catch (InterpreterException e) {
 96			  // Backport from JDK 1.4 to 1.3
 97              // throw new RuntimeException("Failed to initialize default actor",e);
 98              throw new RuntimeException("Failed to initialize default actor");
 99			}
100	    }
101	};
102	
103	/**
104	 * Retrieves the currently running actor. If there is no running actor thread,
105	 * this returns the value stored in the thread-local default actor field.
106	 */
107	public static final ELActor currentActor() {
108		try {
109			return ((ELActor) EventLoop.currentEventLoop());
110		} catch (ClassCastException e) {
111			// current event loop is not an actor event loop
112		} catch (IllegalStateException e) {
113			// current thread is not an event loop
114		}
115		Logging.Actor_LOG.warn("Asked for an actor in non-actor thread " + Thread.currentThread());
116		return (ELActor) _DEFAULT_ACTOR_.get();
117	}
118
119	private ATActorMirror mirror_;
120	private final ActorID id_;
121	protected final ELVirtualMachine host_;
122	protected final ReceptionistsSet receptionists_;
123	
124	/*
125	 * This object is created when the actor is initialized: i.e. it is the passed
126	 * version of the isolate that was passed to the actor: primitive by the creating actor.
127	 */
128	private ATObject behaviour_;
129	
130	public ELActor(ATActorMirror mirror, ELVirtualMachine host) {
131		super("actor " + mirror.toString());
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		id_ = new ActorID();
142		mirror_ = new NATActorMirror(host);
143		host_ = host;
144		receptionists_ = new ReceptionistsSet(this);
145	}
146
147	/**
148	 * Actor event loops handle events by allowing the meta-level events to
149	 * process themselves.
150	 */
151	public void handle(Event event) {
152		event.process(mirror_);
153	}
154	
155	public ATActorMirror getActorMirror() { return mirror_; }
156
157	public void setActorMirror(ATActorMirror mirror) { mirror_ = mirror; }
158	
159	public ELVirtualMachine getHost() {
160		return host_;
161	}
162
163	public ATObject getBehaviour() {
164		return behaviour_;
165	}
166	
167	public ActorID getActorID() {
168		return id_;
169	}
170	
171	/**
172	 * Export the given local object such that it is now remotely accessible via the
173	 * returned object id.
174	 * @param object a **near** reference to the object to export
175	 * @return a unique identifier by which this object can be retrieved via the resolve method.
176	 * @throws XObjectOffline if the passed object is a far reference, i.e. non-local
177	 */
178	public NATLocalFarRef export(ATObject object) throws InterpreterException {
179		// receptionist set will check whether ATObject is really local to me
180		return receptionists_.exportObject(object);
181	}
182	
183	/**
184	 * Takes offline a given remote object such that it is no longer remotely accessible.
185	 * @param object a **far?** reference to the object to export
186	 * @throws XIllegalOperation if the passed object is not part of the export table - i.e. non-remotely accessible.
187	 */
188	public void takeOffline(ATObject object) throws InterpreterException {
189		// receptionist set will check whether ATObject is really remote to me
190		receptionists_.takeOfflineObject(object);
191	}
192	
193	/**
194	 * Resolve the given object id into a local reference. There are three cases to
195	 * consider:
196	 *  A) The given id designates an object local to this actor: the returned object
197	 *     will be a **near** reference to the object (i.e. the object itself)
198	 *  B) The given id designates a far (non-local) object that lives in the same
199	 *     address space as this actor: the returned object wil be a **far** reference
200	 *     to the object.
201	 *  C) The given id designates a far object that lives on a remote machine: the
202	 *     returned object will be a **far** and **remote** reference to the object.
203	 *     
204	 * @param id the identifier of the object to resolve
205	 * @return a near or far reference to the object, depending on where the designated object lives
206	 */
207	public ATObject resolve(ATObjectID id, ATTypeTag[] types) throws XObjectOffline {
208		return receptionists_.resolveObject(id, types);
209	}
210	
211	/* -----------------------------
212	 * -- Initialisation Protocol --
213	 * ----------------------------- */
214
215	/**
216	 * Initialises the root using the contents of the init file stored by
217	 * the hosting virtual machine.
218	 * @throws InterpreterException
219	 */
220	protected void initRootObject() throws InterpreterException {
221		ATAbstractGrammar initialisationCode = host_.getInitialisationCode();
222		
223		// evaluate the initialization code in the context of the global scope
224		NATObject globalScope = Evaluator.getGlobalLexicalScope();
225		NATContext initCtx = new NATContext(globalScope, globalScope);
226		
227		initialisationCode.meta_eval(initCtx);
228	}
229	
230	/**
231	 * Initialises various fields in the lexical root of the actor, which are defined in the 
232	 * context of every actor. Candidates are a "system" field which allows the program to 
233	 * perform IO operations or a "~" field denoting the current working directory.
234	 * 
235	 * @throws InterpreterException when initialisation of a field fails
236	 */
237	protected void initSharedFields() throws InterpreterException {
238		SharedActorField[] fields = host_.getFieldsToInitialize();
239		NATObject globalScope = Evaluator.getGlobalLexicalScope();
240		
241		for (int i = 0; i < fields.length; i++) {
242			SharedActorField field = fields[i];
243			ATObject value = field.initialize();
244			if (value != null) {
245				globalScope.meta_defineField(field.getName(), value);
246			}
247		}
248	}
249	
250	// Events to be processed by the actor event loop
251	
252	/**
253	 * The initial event sent by the actor mirror to its event loop to intialize itself.
254	 * @param future the synchronization point with the creating actor, needs to be fulfilled with a far ref to the behaviour.
255	 * @param parametersPkt the serialized parameters for the initialization code
256	 * @param initcodePkt the serialized initialization code (e.g. the code in 'actor: { code }')
257	 */
258	protected void event_init(final BlockingFuture future, final Packet parametersPkt, final Packet initcodePkt) {
259		receive(new Event("init("+this+")") {
260			public void process(Object byMyself) {
261				try {
262					behaviour_ = new NATObject();
263					
264					// pass far ref to behaviour to creator actor who is waiting for this
265					future.resolve(receptionists_.exportObject(behaviour_));
266					
267					// initialize lexically visible fields
268					initSharedFields();
269
270					// go on to initialize the root and all lexically visible fields
271					initRootObject();
272
273					ATTable params = parametersPkt.unpack().asTable();
274					ATMethod initCode = initcodePkt.unpack().asMethod();					
275					
276					// initialize the behaviour using the parameters and the code
277					initCode.base_applyInScope(params, new NATContext(behaviour_, behaviour_));
278				} catch (InterpreterException e) {
279					Logging.Actor_LOG.error(behaviour_ + ": could not initialize actor behaviour", e);
280				}
281			}
282		});
283	}
284	
285	/**
286	 * The main entry point for any asynchronous self-sends.
287	 * Asynchronous self-sends do not undergo any form of parameter passing, there is no need
288	 * to serialize and deserialize the message parameter in a Packet.
289	 */
290	public void event_acceptSelfSend(final ATAsyncMessage msg) {
291		receive(new Event("selfAccept("+msg+")") {
292			public void process(Object myActorMirror) {
293				performAccept(msg);
294			}
295		});
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 msg 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				ATAsyncMessage msg = serializedMessage.unpack().asAsyncMessage();
309				performAccept(msg);
310			  } catch (XObjectOffline e) {
311				 host_.event_objectTakenOffline(e.getObjectId(), sender);
312				Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
313			  }  catch (InterpreterException e) {
314				Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
315			  } 
316		    }
317		});
318	}
319	
320	/**
321	 * The main entry point for any asynchronous messages sent to this actor 
322	 * by local actors.
323	 * @param ref the local reference of the sending actor, used to notify when the receiver has gone offline.
324	 * @param serializedMessage the asynchronous AmbientTalk base-level message to enqueue
325	 */
326	public void event_localAccept(final NATLocalFarRef ref, final Packet serializedMessage) {
327		receive(new Event("localAccept("+serializedMessage+")") {
328			public void process(Object myActorMirror) {
329			  try {
330				ATAsyncMessage msg = serializedMessage.unpack().asAsyncMessage();
331				performAccept(msg);
332			  } catch (XObjectOffline e) {
333				  ref.notifyTakenOffline();
334				  Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
335			  } catch (InterpreterException e) {
336				  Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
337			  } 
338		    }
339		});
340	}
341	
342	private void performAccept(ATAsyncMessage msg) {
343		try {
344			ATObject result = msg.base_getReceiver().meta_receive(msg);
345			// TODO what to do with return value?
346			Logging.Actor_LOG.info(mirror_ + ": "+ msg + " returned " + result);
347			} catch (InterpreterException e) {
348			// TODO what to do with exception?
349			Logging.Actor_LOG.error(mirror_ + ": "+ msg + " failed ", e);
350		}
351	}
352	
353	/**
354	 * This method is invoked by a coercer in order to schedule a purely asynchronous symbiotic invocation
355	 * from the Java world.
356	 * 
357	 * This method schedules the call for asynchronous execution. Its return value and or raised exceptions
358	 * are ignored. This method should only be used for {@link Method} objects whose return type is <tt>void</tt>
359	 * and whose declaring class is a subtype of {@link EventListener}. It represents asynchronous method
360	 * invocations from the Java world to the AmbientTalk world.
361	 * 
362	 * @param principal the AmbientTalk object owned by this actor on which to invoke the method
363	 * @param meth the Java method that was symbiotically invoked on the principal
364	 * @param args the arguments to the Java method call, already converted into AmbientTalk values
365	 */
366	public void event_symbioticInvocation(final ATObject principal, final Method method, final ATObject[] args) {
367		receive(new Event("asyncSymbioticInv of "+method.getName()) {
368			public void process(Object actorMirror) {
369				try {
370					Reflection.downInvocation(principal, method, args);
371				} catch (InterpreterException e) {
372					Logging.Actor_LOG.error("asynchronous symbiotic invocation of "+method.getName()+" failed", e);
373				}
374			}
375		});
376	}
377	
378	/**
379	 * This method is invoked by a coercer in order to schedule a symbiotic invocation
380	 * from the Java world, which should be synchronous to the Java thread, but which
381	 * must be scheduled asynchronously to comply with the AT/2 actor model.
382	 * 
383	 * This method makes the calling (Java) thread <b>block</b>, waiting until the actor
384	 * has processed the symbiotic invocation.
385	 * 
386	 * @param principal the AmbientTalk object owned by this actor on which to invoke the method
387	 * @param meth 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	 * @return the result of the symbiotic invocation
390	 * @throws Exception if the symbiotic invocation fails
391	 */
392	public Object sync_event_symbioticInvocation(final ATObject principal, final Method meth, final ATObject[] args) throws Exception {
393		return receiveAndWait("syncSymbioticInv of " + meth.getName(), new Callable() {
394			public Object call(Object actorMirror) throws Exception {
395				ATObject result = Reflection.downInvocation(principal, meth, args);
396				return Symbiosis.ambientTalkToJava(result, meth.getReturnType());
397			}
398		});
399	}
400	
401	/**
402	 * This method should only be used for purposes such as the IAT shell or unit testing.
403	 * It allows an external thread to make this actor evaluate an arbitrary expression.
404	 * 
405	 * @param ast an abstract syntax tree to be evaluated by the receiving actor (in the
406	 * scope of its behaviour).
407	 * @return the result of the evaluation
408	 * @throws InterpreterException if the evaluation fails
409	 */
410	public ATObject sync_event_eval(final ATAbstractGrammar ast) throws InterpreterException {
411		try {
412			return (ATObject) receiveAndWait("nativeEval("+ast+")", new Callable() {
413				public Object call(Object inActor) throws Exception {
414				    return OBJLexicalRoot._INSTANCE_.base_eval_in_(ast, behaviour_);
415				}
416			});
417		} catch (Exception e) {
418			throw (InterpreterException) e;
419		}
420	}
421	
422	
423	/**
424	 * This method should only be used for purposes of unit testing. It allows
425	 * arbitary code to be scheduled by external threads such as unit testing frameworks.
426	 */
427	public Object sync_event_performTest(final Callable c) throws Exception {
428		return (ATObject) receiveAndWait("performTest("+c+")", c);
429	}
430	
431	/**
432	 * When the discovery manager receives a publication from another local actor or
433	 * another remote VM, the actor is asked to compare the incoming publication against
434	 * a subscription that it had announced previously.
435	 * 
436	 * @param requiredType serialized form of the type attached to the actor's subscription
437	 * @param myHandler the closure specified as a handler for the actor's subscription
438	 * @param discoveredType serialized form of the type attached to the new publication
439	 * @param remoteService serialized form of the reference to the remote discovered service
440	 */
441	public void event_serviceJoined(final Packet requiredTypePkt, final ATFarReference myHandler,
442			                        final Packet discoveredTypePkt, final Packet remoteServicePkt) {
443		receive(new Event("serviceJoined") {
444			public void process(Object myActorMirror) {
445				try {
446					ATTypeTag requiredType = requiredTypePkt.unpack().asTypeTag();
447					ATTypeTag discoveredType = discoveredTypePkt.unpack().asTypeTag();
448					// is there a match?
449					if (discoveredType.base_isSubtypeOf(requiredType).asNativeBoolean().javaValue) {
450						ATObject remoteService = remoteServicePkt.unpack();
451						// myhandler<-apply([remoteService])
452						myHandler.meta_receive(
453							new NATAsyncMessage(myHandler, Evaluator._APPLY_,
454								NATTable.atValue(new ATObject[] {
455									NATTable.atValue(new ATObject[] { remoteService })
456								}),
457								NATTable.EMPTY));
458					}
459				} catch (XIOProblem e) {
460					Logging.Actor_LOG.error("Error deserializing joined types or services: ", e.getCause());
461				} catch (XClassNotFound e) {
462					Logging.Actor_LOG.fatal("Could not find class while deserializing joined types or services: ", e.getCause());
463				} catch (InterpreterException e) {
464					Logging.Actor_LOG.error("Error while joining services: ", e);
465				}
466			}
467		});
468	}
469}