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

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

http://ambienttalk.googlecode.com/
Java | 376 lines | 185 code | 35 blank | 156 comment | 4 complexity | 8697659efa39c2be33bfc9993849d2ce 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
 30import edu.vub.at.actors.ATActorMirror;
 31import edu.vub.at.actors.ATAsyncMessage;
 32import edu.vub.at.actors.ATFarReference;
 33import edu.vub.at.actors.eventloops.BlockingFuture;
 34import edu.vub.at.actors.eventloops.Callable;
 35import edu.vub.at.actors.eventloops.Event;
 36import edu.vub.at.actors.eventloops.EventLoop;
 37import edu.vub.at.actors.id.ATObjectID;
 38import edu.vub.at.actors.net.Logging;
 39import edu.vub.at.eval.Evaluator;
 40import edu.vub.at.exceptions.InterpreterException;
 41import edu.vub.at.exceptions.XClassNotFound;
 42import edu.vub.at.exceptions.XIOProblem;
 43import edu.vub.at.exceptions.XIllegalOperation;
 44import edu.vub.at.exceptions.XObjectOffline;
 45import edu.vub.at.objects.ATAbstractGrammar;
 46import edu.vub.at.objects.ATMethod;
 47import edu.vub.at.objects.ATObject;
 48import edu.vub.at.objects.ATStripe;
 49import edu.vub.at.objects.ATTable;
 50import edu.vub.at.objects.natives.NATContext;
 51import edu.vub.at.objects.natives.NATNil;
 52import edu.vub.at.objects.natives.NATObject;
 53import edu.vub.at.objects.natives.NATTable;
 54import edu.vub.at.objects.natives.OBJLexicalRoot;
 55
 56/**
 57 * An instance of the class ELActor represents a programmer-defined
 58 * AmbientTalk/2 actor. The event queue of the actor event loop serves as the
 59 * actor's 'meta-level' queue.
 60 *
 61 * The events in the 'meta-level' queue are handled by the actor's mirror object.
 62 * This mirror is normally an instance of NATActorMirror, but it can be any
 63 * programmer-defined object that adheres to the ATActorMirror interface.
 64 *
 65 * @author tvcutsem
 66 */
 67public class ELActor extends EventLoop {
 68	
 69	/**
 70	 * A thread-local variable that contains the 'default actor' to use
 71	 * when there is currently no ELActor event loop thread running.
 72	 * This is primarily useful for performing unit tests where an actor
 73	 * is automatically created when actor semantics is required.
 74	 * 
 75	 * A warning is printed to the log because using the default actor should
 76	 * only be used for testing purposes.
 77	 */
 78	private static final ThreadLocal _DEFAULT_ACTOR_ = new ThreadLocal() {
 79	    protected synchronized Object initialValue() {
 80	    	Logging.Actor_LOG.warn("Creating a default actor for thread " + Thread.currentThread());
 81	    	try {
 82				ELVirtualMachine host = new ELVirtualMachine(NATNil._INSTANCE_, new SharedActorField[] { });
 83				return NATActorMirror.createEmptyActor(host, new NATActorMirror(host)).getFarHost();
 84			} catch (InterpreterException e) {
 85				throw new RuntimeException("Failed to initialize default actor",e);
 86			}
 87	    }
 88	};
 89	
 90	/**
 91	 * Retrieves the currently running actor. If there is no running actor thread,
 92	 * this returns the value stored in the thread-local default actor field.
 93	 */
 94	public static final ELActor currentActor() {
 95		try {
 96			return ((ELActor) EventLoop.currentEventLoop());
 97		} catch (ClassCastException e) {
 98			// current event loop is not an actor event loop
 99		} catch (IllegalStateException e) {
100			// current thread is not an event loop
101		}
102		Logging.Actor_LOG.warn("Asked for an actor in non-actor thread " + Thread.currentThread());
103		return (ELActor) _DEFAULT_ACTOR_.get();
104	}
105
106	private ATActorMirror mirror_;
107	protected final ELVirtualMachine host_;
108	protected final ReceptionistsSet receptionists_;
109	
110	/*
111	 * This object is created when the actor is initialized: i.e. it is the passed
112	 * version of the isolate that was passed to the actor: primitive by the creating actor.
113	 */
114	private ATObject behaviour_;
115	
116	public ELActor(ATActorMirror mirror, ELVirtualMachine host) {
117		super("actor " + mirror.toString());
118		mirror_ = mirror;
119		host_ = host;
120		receptionists_ = new ReceptionistsSet(this);
121	}
122	
123	/** constructor dedicated to initialization of discovery actor */
124	protected ELActor(ELVirtualMachine host) {
125		super("discovery actor");
126		mirror_ = new NATActorMirror(host);
127		host_ = host;
128		receptionists_ = new ReceptionistsSet(this);
129	}
130
131	/**
132	 * Actor event loops handle events by allowing the meta-level events to
133	 * process themselves.
134	 */
135	public void handle(Event event) {
136		event.process(mirror_);
137	}
138	
139	public ATActorMirror getActorMirror() { return mirror_; }
140
141	public void setActorMirror(ATActorMirror mirror) { mirror_ = mirror; }
142	
143	public ELVirtualMachine getHost() {
144		return host_;
145	}
146	
147	/**
148	 * Export the given local object such that it is now remotely accessible via the
149	 * returned object id.
150	 * @param object a **near** reference to the object to export
151	 * @return a unique identifier by which this object can be retrieved via the resolve method.
152	 * @throws XIllegalOperation if the passed object is a far reference, i.e. non-local
153	 */
154	public NATLocalFarRef export(ATObject object) throws InterpreterException {
155		// receptionist set will check whether ATObject is really local to me
156		return receptionists_.exportObject(object);
157	}
158	
159	/**
160	 * Resolve the given object id into a local reference. There are three cases to
161	 * consider:
162	 *  A) The given id designates an object local to this actor: the returned object
163	 *     will be a **near** reference to the object (i.e. the object itself)
164	 *  B) The given id designates a far (non-local) object that lives in the same
165	 *     address space as this actor: the returned object wil be a **far** reference
166	 *     to the object.
167	 *  C) The given id designates a far object that lives on a remote machine: the
168	 *     returned object will be a **far** and **remote** reference to the object.
169	 *     
170	 * @param id the identifier of the object to resolve
171	 * @return a near or far reference to the object, depending on where the designated object lives
172	 */
173	public ATObject resolve(ATObjectID id, ATStripe[] stripes) throws XObjectOffline {
174		return receptionists_.resolveObject(id, stripes);
175	}
176	
177	/* -----------------------------
178	 * -- Initialisation Protocol --
179	 * ----------------------------- */
180
181	/**
182	 * Initialises the root using the contents of the init file stored by
183	 * the hosting virtual machine.
184	 * @throws InterpreterException
185	 */
186	protected void initRootObject() throws InterpreterException {
187		ATAbstractGrammar initialisationCode = host_.getInitialisationCode();
188		
189		// evaluate the initialization code in the context of the global scope
190		NATObject globalScope = Evaluator.getGlobalLexicalScope();
191		NATContext initCtx = new NATContext(globalScope, globalScope);
192		
193		initialisationCode.meta_eval(initCtx);
194	}
195	
196	/**
197	 * Initialises various fields in the lexical root of the actor, which are defined in the 
198	 * context of every actor. Candidates are a "system" field which allows the program to 
199	 * perform IO operations or a "~" field denoting the current working directory.
200	 * 
201	 * @throws InterpreterException when initialisation of a field fails
202	 */
203	protected void initSharedFields() throws InterpreterException {
204		SharedActorField[] fields = host_.getFieldsToInitialize();
205		NATObject globalScope = Evaluator.getGlobalLexicalScope();
206		
207		for (int i = 0; i < fields.length; i++) {
208			SharedActorField field = fields[i];
209			ATObject value = field.initialize();
210			if (value != null) {
211				globalScope.meta_defineField(field.getName(), value);
212			}
213		}
214	}
215	
216	// Events to be processed by the actor event loop
217	
218	/**
219	 * The initial event sent by the actor mirror to its event loop to intialize itself.
220	 * @param future the synchronization point with the creating actor, needs to be fulfilled with a far ref to the behaviour.
221	 * @param parametersPkt the serialized parameters for the initialization code
222	 * @param initcodePkt the serialized initialization code
223	 */
224	protected void event_init(final BlockingFuture future, final Packet parametersPkt, final Packet initcodePkt) {
225		receive(new Event("init("+this+")") {
226			public void process(Object byMyself) {
227				try {
228					behaviour_ = new NATObject();
229					
230					// pass far ref to behaviour to creator actor who is waiting for this
231					future.resolve(receptionists_.exportObject(behaviour_));
232					
233					// initialize lexically visible fields
234					initSharedFields();
235
236					ATTable params = parametersPkt.unpack().asTable();
237					ATMethod initCode = initcodePkt.unpack().asMethod();
238					
239					// initialize the behaviour using the parameters and the code
240					initCode.base_applyInScope(params, new NATContext(behaviour_, behaviour_));
241					
242					// go on to initialize the root and all lexically visible fields
243					initRootObject();
244				} catch (InterpreterException e) {
245					Logging.Actor_LOG.error(behaviour_ + ": could not initialize actor behaviour", e);
246				}
247			}
248		});
249	}
250	
251	/**
252	 * The main entry point for any asynchronous self-sends.
253	 * Asynchronous self-sends do not undergo any form of parameter passing, there is no need
254	 * to serialize and deserialize the message parameter in a Packet.
255	 */
256	public void event_acceptSelfSend(final ATAsyncMessage msg) {
257		receive(new Event("accept("+msg+")") {
258			public void process(Object myActorMirror) {
259				performAccept(msg);
260			}
261		});
262	}
263	
264	/**
265	 * The main entry point for any asynchronous messages sent to this actor
266	 * by external sources (e.g. the VM or other local actors).
267	 * @param msg the asynchronous AmbientTalk base-level message to enqueue
268	 */
269	public void event_accept(final Packet serializedMessage) {
270		receive(new Event("accept("+serializedMessage+")") {
271			public void process(Object myActorMirror) {
272			  try {
273				ATAsyncMessage msg = serializedMessage.unpack().asAsyncMessage();
274				performAccept(msg);
275			  } catch (InterpreterException e) {
276				Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
277			  } catch (RuntimeException e) {
278				  throw e;
279			  }
280		    }
281		});
282	}
283	
284	private void performAccept(ATAsyncMessage msg) {
285		try {
286			ATObject result = msg.base_getReceiver().meta_receive(msg);
287			// TODO what to do with return value?
288			Logging.Actor_LOG.info(mirror_ + ": "+ msg + " returned " + result);
289		} catch (InterpreterException e) {
290			// TODO what to do with exception?
291			Logging.Actor_LOG.error(mirror_ + ": "+ msg + " failed ", e);
292		}
293	}
294	
295	/**
296	 * This method is invoked by a coercer in order to schedule a symbiotic invocation
297	 * from the Java world, which should be synchronous to the Java thread, but which
298	 * must be scheduled asynchronously to comply with the AT/2 actor model.
299	 * @param invocation a functor object that will perform the symbiotic invocation
300	 * @return the result of the symbiotic invocation
301	 * @throws Exception if the symbiotic invocation fails
302	 */
303	public Object sync_event_symbioticInvocation(Callable invocation) throws Exception {
304		return receiveAndWait("symbioticInvocation", invocation);
305	}
306	
307	/**
308	 * This method should only be used for purposes such as the IAT shell or unit testing.
309	 * It allows an external thread to make this actor evaluate an arbitrary expression.
310	 * 
311	 * @param ast an abstract syntax tree to be evaluated by the receiving actor (in the
312	 * scope of its behaviour).
313	 * @return the result of the evaluation
314	 * @throws InterpreterException if the evaluation fails
315	 */
316	public ATObject sync_event_eval(final ATAbstractGrammar ast) throws InterpreterException {
317		try {
318			return (ATObject) receiveAndWait("nativeEval("+ast+")", new Callable() {
319				public Object call(Object inActor) throws Exception {
320				    return OBJLexicalRoot._INSTANCE_.base_eval_in_(ast, behaviour_);
321				}
322			});
323		} catch (Exception e) {
324			throw (InterpreterException) e;
325		}
326	}
327	
328	
329	/**
330	 * This method should only be used for purposes of unit testing. It allows
331	 * arbitary code to be scheduled by external threads such as unit testing frameworks.
332	 */
333	public Object sync_event_performTest(final Callable c) throws Exception {
334		return (ATObject) receiveAndWait("performTest("+c+")", c);
335	}
336	
337	/**
338	 * When the discovery manager receives a publication from another local actor or
339	 * another remote VM, the actor is asked to compare the incoming publication against
340	 * a subscription that it had announced previously.
341	 * 
342	 * @param requiredStripe serialized form of the stripe attached to the actor's subscription
343	 * @param myHandler the closure specified as a handler for the actor's subscription
344	 * @param discoveredStripe serialized form of the stripe attached to the new publication
345	 * @param remoteService serialized form of the reference to the remote discovered service
346	 */
347	public void event_serviceJoined(final Packet requiredStripePkt, final ATFarReference myHandler,
348			                        final Packet discoveredStripePkt, final Packet remoteServicePkt) {
349		receive(new Event("serviceJoined") {
350			public void process(Object myActorMirror) {
351				try {
352					ATStripe requiredStripe = requiredStripePkt.unpack().asStripe();
353					ATStripe discoveredStripe = discoveredStripePkt.unpack().asStripe();
354					// is there a match?
355					if (discoveredStripe.base_isSubstripeOf(requiredStripe).asNativeBoolean().javaValue) {
356						ATObject remoteService = remoteServicePkt.unpack();
357						// myhandler<-apply([remoteService])
358						myHandler.meta_receive(
359							NATAsyncMessage.createAsyncMessage(myHandler, myHandler, Evaluator._APPLY_,
360								NATTable.atValue(new ATObject[] {
361									NATTable.atValue(new ATObject[] { remoteService })
362								})
363							)
364						);
365					}
366				} catch (XIOProblem e) {
367					Logging.Actor_LOG.error("Error deserializing joined stripes or services: ", e.getCause());
368				} catch (XClassNotFound e) {
369					Logging.Actor_LOG.fatal("Could not find class while deserializing joined stripes or services: ", e.getCause());
370				} catch (InterpreterException e) {
371					Logging.Actor_LOG.error("Error while joining services: ", e);
372				}
373			}
374		});
375	}
376}