PageRenderTime 40ms CodeModel.GetById 19ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://ambienttalk.googlecode.com/
Java | 487 lines | 246 code | 44 blank | 197 comment | 4 complexity | daac210d55bd8f6003cb8dc8bb6763ff 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.natives.OBJNil;
 61import edu.vub.at.objects.symbiosis.Symbiosis;
 62import edu.vub.at.util.logging.Logging;
 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				  OBJNil._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: " + e.getMessage());
 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		// notify host VM about my creation
137		host.actorCreated(this);
138	}
139	
140	/** constructor dedicated to initialization of discovery actor */
141	protected ELActor(ELVirtualMachine host) {
142		super("discovery actor");
143		id_ = new ActorID();
144		mirror_ = new NATActorMirror(host);
145		host_ = host;
146		receptionists_ = new ReceptionistsSet(this);
147		// notify host VM about my creation
148		host.actorCreated(this);
149	}
150
151	/**
152	 * Actor event loops handle events by allowing the meta-level events to
153	 * process themselves.
154	 */
155	public void handle(Event event) {
156		event.process(mirror_);
157	}
158	
159	public ATActorMirror getActorMirror() { return mirror_; }
160
161	public void setActorMirror(ATActorMirror mirror) { mirror_ = mirror; }
162	
163	public ELVirtualMachine getHost() {
164		return host_;
165	}
166
167	public ATObject getBehaviour() {
168		return behaviour_;
169	}
170	
171	public ActorID getActorID() {
172		return id_;
173	}
174	
175	public Thread getExecutor() {
176		return processor_;
177	}
178	
179	/**
180	 * Export the given local object such that it is now remotely accessible via the
181	 * returned object id.
182	 * @param object a **near** reference to the object to export
183	 * @return a unique identifier by which this object can be retrieved via the resolve method.
184	 * @throws XObjectOffline if the passed object is a far reference, i.e. non-local
185	 */
186	public NATLocalFarRef export(ATObject object) throws InterpreterException {
187		// receptionist set will check whether ATObject is really local to me
188		return receptionists_.exportObject(object);
189	}
190	
191	/**
192	 * Takes offline a given remote object such that it is no longer remotely accessible.
193	 * @param object a **far?** reference to the object to export
194	 * @throws XIllegalOperation if the passed object is not part of the export table - i.e. non-remotely accessible.
195	 */
196	public void takeOffline(ATObject object) throws InterpreterException {
197		// receptionist set will check whether ATObject is really remote to me
198		receptionists_.takeOfflineObject(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 wil 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) throws XObjectOffline {
216		return receptionists_.resolveObject(id, types);
217	}
218	
219	/* -----------------------------
220	 * -- Initialisation Protocol --
221	 * ----------------------------- */
222
223	/**
224	 * Initialises the root using the contents of the init file stored by
225	 * the hosting virtual machine.
226	 * @throws InterpreterException
227	 */
228	protected void initRootObject() throws InterpreterException {
229		ATAbstractGrammar initialisationCode = host_.getInitialisationCode();
230		
231		// evaluate the initialization code in the context of the global scope
232		NATObject globalScope = Evaluator.getGlobalLexicalScope();
233		NATContext initCtx = new NATContext(globalScope, globalScope);
234		
235		initialisationCode.meta_eval(initCtx);
236	}
237	
238	/**
239	 * Initialises various fields in the lexical root of the actor, which are defined in the 
240	 * context of every actor. Candidates are a "system" field which allows the program to 
241	 * perform IO operations or a "~" field denoting the current working directory.
242	 * 
243	 * @throws InterpreterException when initialisation of a field fails
244	 */
245	protected void initSharedFields() throws InterpreterException {
246		SharedActorField[] fields = host_.getFieldsToInitialize();
247		NATObject globalScope = Evaluator.getGlobalLexicalScope();
248		
249		for (int i = 0; i < fields.length; i++) {
250			SharedActorField field = fields[i];
251			ATObject value = field.initialize();
252			if (value != null) {
253				globalScope.meta_defineField(field.getName(), value);
254			}
255		}
256	}
257	
258	// Events to be processed by the actor event loop
259	
260	/**
261	 * The initial event sent by the actor mirror to its event loop to intialize itself.
262	 * @param future the synchronization point with the creating actor, needs to be fulfilled with a far ref to the behaviour.
263	 * @param parametersPkt the serialized parameters for the initialization code
264	 * @param initcodePkt the serialized initialization code (e.g. the code in 'actor: { code }')
265	 */
266	protected void event_init(final BlockingFuture future, final Packet parametersPkt, final Packet initcodePkt) {
267		receive(new Event("init("+this+")") {
268			public void process(Object byMyself) {
269				try {
270					behaviour_ = new NATObject();
271					
272					// pass far ref to behaviour to creator actor who is waiting for this
273					future.resolve(receptionists_.exportObject(behaviour_));
274					
275					// !! WARNING: the following code is also duplicated in
276					// ELDiscoveryActor's event_init. If this code is modified, don't
277					// forget to modify that of the discovery actor as well !!
278					
279					// initialize lexically visible fields
280					initSharedFields();
281
282					// go on to initialize the root and all lexically visible fields
283					initRootObject();
284
285					ATTable params = parametersPkt.unpack().asTable();
286					ATMethod initCode = initcodePkt.unpack().asMethod();					
287					
288					// initialize the behaviour using the parameters and the code
289					initCode.base_applyInScope(params, new NATContext(behaviour_, behaviour_));
290				} catch (InterpreterException e) {
291					Logging.Actor_LOG.error(behaviour_ + ": could not initialize actor behaviour", e);
292				}
293			}
294		});
295	}
296	
297	/**
298	 * The main entry point for any asynchronous self-sends.
299	 * Asynchronous self-sends do not undergo any form of parameter passing, there is no need
300	 * to serialize and deserialize the message parameter in a Packet.
301	 */
302	public void event_acceptSelfSend(final ATObject receiver, final ATAsyncMessage msg) {
303		receive(new Event("selfAccept("+msg+")") {
304			public void process(Object myActorMirror) {
305				performAccept(receiver, msg);
306			}
307		});
308	}
309	
310	/**
311	 * The main entry point for any asynchronous messages sent to this actor
312	 * by external sources.
313	 * @param sender address of the sending actor, used to notify when the receiver has gone offline.
314	 * @param serializedMessage the asynchronous AmbientTalk base-level message to enqueue
315	 */
316	public void event_remoteAccept(final Address sender, final Packet serializedMessage) {
317		receive(new Event("remoteAccept("+serializedMessage+")") {
318			public void process(Object myActorMirror) {
319			  try {
320				// receive a pair [receiver, message]
321				ATObject[] pair = serializedMessage.unpack().asNativeTable().elements_;
322				ATObject receiver = pair[0];
323				ATAsyncMessage msg = pair[1].asAsyncMessage();
324				performAccept(receiver, msg);
325			  } catch (XObjectOffline e) {
326				 host_.event_objectTakenOffline(e.getObjectId(), sender);
327				Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
328			  }  catch (InterpreterException e) {
329				Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
330			  } 
331		    }
332		});
333	}
334	
335	/**
336	 * The main entry point for any asynchronous messages sent to this actor 
337	 * by local actors.
338	 * @param ref the local reference of the sending actor, used to notify when the receiver has gone offline.
339	 * @param serializedMessage the asynchronous AmbientTalk base-level message to enqueue
340	 */
341	public void event_localAccept(final NATLocalFarRef ref, final Packet serializedMessage) {
342		receive(new Event("localAccept("+serializedMessage+")") {
343			public void process(Object myActorMirror) {
344			  try {
345				// receive a pair [receiver, message]
346				ATObject[] pair = serializedMessage.unpack().asNativeTable().elements_;
347				ATObject receiver = pair[0];
348				ATAsyncMessage msg = pair[1].asAsyncMessage();
349				performAccept(receiver, msg);
350			  } catch (XObjectOffline e) {
351				  ref.notifyTakenOffline();
352				  Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
353			  } catch (InterpreterException e) {
354				  Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
355			  } 
356		    }
357		});
358	}
359	
360	private void performAccept(ATObject receiver, ATAsyncMessage msg) {
361		try {
362			ATObject result = mirror_.base_receive(receiver, msg);
363			// TODO what to do with return value?
364			Logging.Actor_LOG.debug(mirror_ + ": "+ msg + " returned " + result);
365			} catch (InterpreterException e) {
366			// TODO what to do with exception?
367			e.printAmbientTalkStackTrace(System.err);
368			Logging.Actor_LOG.error(mirror_ + ": "+ msg + " failed ", e);
369		}
370	}
371	
372	/**
373	 * This method is invoked by a coercer in order to schedule a purely asynchronous symbiotic invocation
374	 * from the Java world.
375	 * 
376	 * This method schedules the call for asynchronous execution. Its return value and or raised exceptions
377	 * are ignored. This method should only be used for {@link Method} objects whose return type is <tt>void</tt>
378	 * and whose declaring class is a subtype of {@link EventListener}. It represents asynchronous method
379	 * invocations from the Java world to the AmbientTalk world.
380	 * 
381	 * @param principal the AmbientTalk object owned by this actor on which to invoke the method
382	 * @param method the Java method that was symbiotically invoked on the principal
383	 * @param args the arguments to the Java method call, already converted into AmbientTalk values
384	 */
385	public void event_symbioticInvocation(final ATObject principal, final Method method, final ATObject[] args) {
386		receive(new Event("asyncSymbioticInv of "+method.getName()) {
387			public void process(Object actorMirror) {
388				try {
389					Reflection.downInvocation(principal, method, args);
390				} catch (InterpreterException e) {
391					e.printAmbientTalkStackTrace(System.err);
392					Logging.Actor_LOG.error("asynchronous symbiotic invocation of "+method.getName()+" failed", e);
393				}
394			}
395		});
396	}
397	
398	/**
399	 * This method is invoked by a coercer in order to schedule a symbiotic invocation
400	 * from the Java world, which should be synchronous to the Java thread, but which
401	 * must be scheduled asynchronously to comply with the AT/2 actor model.
402	 * 
403	 * This method makes the calling (Java) thread <b>block</b>, waiting until the actor
404	 * has processed the symbiotic invocation.
405	 * 
406	 * @param principal the AmbientTalk object owned by this actor on which to invoke the method
407	 * @param meth the Java method that was symbiotically invoked on the principal
408	 * @param args the arguments to the Java method call, already converted into AmbientTalk values
409	 * @return the result of the symbiotic invocation
410	 * @throws Exception if the symbiotic invocation fails
411	 */
412	public Object sync_event_symbioticInvocation(final ATObject principal, final Method meth, final ATObject[] args) throws Exception {
413		return receiveAndWait("syncSymbioticInv of " + meth.getName(), new Callable() {
414			public Object call(Object actorMirror) throws Exception {
415				ATObject result = Reflection.downInvocation(principal, meth, args);
416				return Symbiosis.ambientTalkToJava(result, meth.getReturnType());
417			}
418		});
419	}
420	
421	/**
422	 * This method should only be used for purposes such as the IAT shell or unit testing.
423	 * It allows an external thread to make this actor evaluate an arbitrary expression.
424	 * 
425	 * @param ast an abstract syntax tree to be evaluated by the receiving actor (in the
426	 * scope of its behaviour).
427	 * @return the result of the evaluation
428	 * @throws InterpreterException if the evaluation fails
429	 */
430	public ATObject sync_event_eval(final ATAbstractGrammar ast) throws InterpreterException {
431		try {
432			return (ATObject) receiveAndWait("nativeEval("+ast+")", new Callable() {
433				public Object call(Object inActor) throws Exception {
434				    return OBJLexicalRoot._INSTANCE_.base_eval_in_(ast, behaviour_);
435				}
436			});
437		} catch (Exception e) {
438			throw (InterpreterException) e;
439		}
440	}
441	
442	
443	/**
444	 * This method should only be used for purposes of unit testing. It allows
445	 * arbitary code to be scheduled by external threads such as unit testing frameworks.
446	 */
447	public Object sync_event_performTest(final Callable c) throws Exception {
448		return (ATObject) receiveAndWait("performTest("+c+")", c);
449	}
450	
451	/**
452	 * When the discovery manager receives a publication from another local actor or
453	 * another remote VM, the actor is asked to compare the incoming publication against
454	 * a subscription that it had announced previously.
455	 * 
456	 * @param requiredTypePkt serialized form of the type attached to the actor's subscription
457	 * @param myHandler the closure specified as a handler for the actor's subscription
458	 * @param discoveredTypePkt serialized form of the type attached to the new publication
459	 * @param remoteServicePkt serialized form of the reference to the remote discovered service
460	 */
461	public void event_serviceJoined(final Packet requiredTypePkt, final ATFarReference myHandler,
462			                        final Packet discoveredTypePkt, final Packet remoteServicePkt) {
463		receive(new Event("serviceJoined") {
464			public void process(Object myActorMirror) {
465				try {
466					ATTypeTag requiredType = requiredTypePkt.unpack().asTypeTag();
467					ATTypeTag discoveredType = discoveredTypePkt.unpack().asTypeTag();
468					// is there a match?
469					if (discoveredType.base_isSubtypeOf(requiredType).asNativeBoolean().javaValue) {
470						ATObject remoteService = remoteServicePkt.unpack();
471						// myhandler<-apply([remoteService])
472						myHandler.meta_receive(
473							new NATAsyncMessage(Evaluator._APPLY_,
474								NATTable.of(NATTable.of(remoteService)),
475								NATTable.EMPTY));
476					}
477				} catch (XIOProblem e) {
478					Logging.Actor_LOG.error("Error deserializing joined types or services: ", e.getCause());
479				} catch (XClassNotFound e) {
480					Logging.Actor_LOG.fatal("Could not find class while deserializing joined types or services: ", e.getCause());
481				} catch (InterpreterException e) {
482					Logging.Actor_LOG.error("Error while joining services: ", e);
483				}
484			}
485		});
486	}
487}