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