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

http://ambienttalk.googlecode.com/ · Java · 634 lines · 335 code · 50 blank · 249 comment · 15 complexity · 7a6bbc447222f6821a707ad2bd3981c1 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.ATTable;
  49. import edu.vub.at.objects.ATTypeTag;
  50. import edu.vub.at.objects.mirrors.Reflection;
  51. import edu.vub.at.objects.natives.NATContext;
  52. import edu.vub.at.objects.natives.NATObject;
  53. import edu.vub.at.objects.natives.NATTable;
  54. import edu.vub.at.objects.natives.OBJLexicalRoot;
  55. import edu.vub.at.objects.symbiosis.Symbiosis;
  56. import edu.vub.at.util.logging.Logging;
  57. import java.lang.reflect.InvocationTargetException;
  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. Evaluator.getNil(),
  87. new SharedActorField[] { },
  88. ELVirtualMachine._DEFAULT_GROUP_NAME_,
  89. ELVirtualMachine._DEFAULT_IP_ADDRESS_);
  90. return host.createEmptyActor().getFarHost();
  91. } catch (InterpreterException e) {
  92. throw new RuntimeException("Failed to initialize default actor: " + e.getMessage());
  93. }
  94. }
  95. };
  96. /**
  97. * Retrieves the currently running actor. If there is no running actor thread,
  98. * this returns the value stored in the thread-local default actor field.
  99. */
  100. public static final ELActor currentActor() {
  101. try {
  102. return ((ELActor) EventLoop.currentEventLoop());
  103. } catch (ClassCastException e) {
  104. // current event loop is not an actor event loop
  105. } catch (IllegalStateException e) {
  106. // current thread is not an event loop
  107. }
  108. Logging.Actor_LOG.warn("Asked for an actor in non-actor thread " + Thread.currentThread());
  109. return (ELActor) _DEFAULT_ACTOR_.get();
  110. }
  111. private ATActorMirror mirror_;
  112. private final ActorID id_;
  113. protected final ELVirtualMachine host_;
  114. protected final ReceptionistsSet receptionists_;
  115. /*
  116. * This object is created when the actor is initialized: i.e. it is the passed
  117. * version of the isolate that was passed to the actor: primitive by the creating actor.
  118. */
  119. protected NATObject behaviour_;
  120. public ELActor(ATActorMirror mirror, ELVirtualMachine host) {
  121. super("actor " + mirror.toString());
  122. this.start();
  123. id_ = new ActorID();
  124. mirror_ = mirror;
  125. host_ = host;
  126. receptionists_ = new ReceptionistsSet(this);
  127. }
  128. /**constructor dedicated to initialization with stack size for android*/
  129. public ELActor(ATActorMirror mirror, ELVirtualMachine host, int stackSize) {
  130. super("actor " + mirror.toString(), stackSize);
  131. this.start();
  132. id_ = new ActorID();
  133. mirror_ = mirror;
  134. host_ = host;
  135. receptionists_ = new ReceptionistsSet(this);
  136. }
  137. /** constructor dedicated to initialization of discovery actor */
  138. protected ELActor(ELVirtualMachine host) {
  139. super("discovery actor");
  140. this.start();
  141. id_ = new ActorID();
  142. host_ = host;
  143. receptionists_ = new ReceptionistsSet(this);
  144. }
  145. /**
  146. * Actor event loops handle events by allowing the meta-level events to
  147. * process themselves.
  148. */
  149. public void handle(Event event) {
  150. event.process(mirror_);
  151. }
  152. public ATActorMirror getImplicitActorMirror() { return mirror_; }
  153. public void setActorMirror(ATActorMirror mirror) { mirror_ = mirror; }
  154. public ELVirtualMachine getHost() {
  155. return host_;
  156. }
  157. public ActorID getActorID() {
  158. return id_;
  159. }
  160. public Thread getExecutor() {
  161. return processor_;
  162. }
  163. /**
  164. * Takes offline a given local object such that it is no longer remotely accessible.
  165. * @param object a near reference to the local object to unexport
  166. * @throws XIllegalOperation if the passed object is not part of the export table - i.e. non-remotely accessible.
  167. */
  168. public void takeOffline(ATObject object) throws InterpreterException {
  169. // receptionist set will check whether ATObject is really remote to me
  170. receptionists_.takeOfflineObject(object);
  171. }
  172. /**
  173. * Disconnects a given local object such that it is no longer remotely accessible.
  174. * @param object a near reference to the local object to disconnect
  175. * @throws XIllegalOperation if the passed object is not part of the export table - i.e. non-remotely accessible.
  176. */
  177. public ATObject disconnect(final ATObject object) throws InterpreterException {
  178. // receptionist set will check whether ATObject is really remote to me
  179. return receptionists_.disconnect(object);
  180. }
  181. /**
  182. * Resolve the given object id into a local reference. There are three cases to
  183. * consider:
  184. * A) The given id designates an object local to this actor: the returned object
  185. * will be a **near** reference to the object (i.e. the object itself)
  186. * B) The given id designates a far (non-local) object that lives in the same
  187. * address space as this actor: the returned object will be a **far** reference
  188. * to the object.
  189. * C) The given id designates a far object that lives on a remote machine: the
  190. * returned object will be a **far** and **remote** reference to the object.
  191. *
  192. * @param id the identifier of the object to resolve
  193. * @return a near or far reference to the object, depending on where the designated object lives
  194. */
  195. public ATObject resolve(ATObjectID id, ATTypeTag[] types, boolean isConnected) throws XObjectOffline {
  196. return receptionists_.resolveObject(id, types, isConnected);
  197. }
  198. /* -----------------------------
  199. * -- Initialisation Protocol --
  200. * ----------------------------- */
  201. /**
  202. * Initialises the root using the contents of the init.at file configured in
  203. * the hosting virtual machine.
  204. *
  205. * @throws InterpreterException
  206. */
  207. protected void initRootObject() throws InterpreterException {
  208. ATAbstractGrammar initialisationCode = host_.getInitialisationCode();
  209. // evaluate the initialization code in the context of the global scope
  210. NATObject globalScope = Evaluator.getGlobalLexicalScope();
  211. NATContext initCtx = new NATContext(globalScope, globalScope);
  212. // note: the return value of the init.at file is ignored
  213. initialisationCode.meta_eval(initCtx);
  214. }
  215. /**
  216. * Initialises various fields in the lexical root of the actor, which are defined in the
  217. * context of every actor. Candidates are a "system" field which allows the program to
  218. * perform IO operations or a "~" field denoting the current working directory.
  219. *
  220. * @throws InterpreterException when initialisation of a field fails
  221. */
  222. protected void initSharedFields() throws InterpreterException {
  223. SharedActorField[] fields = host_.getFieldsToInitialize();
  224. NATObject globalScope = Evaluator.getGlobalLexicalScope();
  225. for (int i = 0; i < fields.length; i++) {
  226. SharedActorField field = fields[i];
  227. ATObject value = field.initialize();
  228. if (value != null) {
  229. globalScope.meta_defineField(field.getName(), value);
  230. }
  231. }
  232. }
  233. // Events to be processed by the actor event loop
  234. /**
  235. * The initial event sent by the actor mirror to its event loop to intialize itself.
  236. * @param future the synchronization point with the creating actor, needs to be fulfilled with a far ref to the behaviour.
  237. * @param parametersPkt the serialized parameters for the initialization code
  238. * @param initcodePkt the serialized initialization code (e.g. the code in 'actor: { code }')
  239. */
  240. protected void event_init(final BlockingFuture future, final Packet parametersPkt, final Packet initcodePkt) {
  241. receive(new Event("init("+this+")") {
  242. public void process(Object byMyself) {
  243. try {
  244. behaviour_ = new NATObject();
  245. // pass far ref to behaviour to creator actor who is waiting for this
  246. future.resolve(receptionists_.exportObject(behaviour_,"behaviour of "+byMyself));
  247. // !! WARNING: the following code is also duplicated in
  248. // ELDiscoveryActor's event_init. If this code is modified, don't
  249. // forget to modify that of the discovery actor as well !!
  250. // initialize lexically visible fields
  251. initSharedFields();
  252. // go on to initialize the root and all lexically visible fields
  253. initRootObject();
  254. ATObject params = parametersPkt.unpack();
  255. ATMethod initCode = initcodePkt.unpack().asMethod();
  256. if (!params.isTable()) {
  257. // actor initialized as actor: { ... } => free vars automatically added to a private lexical scope
  258. // in this case, params refers to an object that will play the role of lexical scope of the actor's behaviour
  259. params.asAmbientTalkObject().setLexicalParent(Evaluator.getGlobalLexicalScope());
  260. behaviour_.setLexicalParent(params);
  261. params = NATTable.EMPTY;
  262. }/* else {
  263. // actor initialized as actor: { |vars| ... } => vars become publicly accessible in the actor
  264. }*/
  265. // initialize the behaviour using the parameters and the code
  266. try {
  267. //adding location of the closure to the bhv
  268. behaviour_.impl_setLocation(initCode.impl_getLocation());
  269. initCode.base_applyInScope(params.asTable(), new NATContext(behaviour_, behaviour_));
  270. } catch (InterpreterException e) {
  271. System.out.println(">>> Exception while initializing actor " + Evaluator.trunc(initCode.base_bodyExpression().toString(),20) + ":\n"+e.getMessage());
  272. e.printAmbientTalkStackTrace(System.out);
  273. Logging.Actor_LOG.error(behaviour_ + ": could not initialize actor behaviour", e);
  274. }
  275. } catch (InterpreterException e) {
  276. System.out.println(">>> Exception while creating actor: " + e.getMessage());
  277. e.printAmbientTalkStackTrace(System.out);
  278. Logging.Actor_LOG.error(behaviour_ + ": could not initialize actor behaviour", e);
  279. }
  280. }
  281. });
  282. }
  283. /**
  284. * The main entry point for any asynchronous self-sends.
  285. * Asynchronous self-sends (i.e. intra-actor sends) do not undergo any form of parameter passing,
  286. * there is no need to serialize and deserialize the message parameter in a Packet.
  287. *
  288. * When an actor receives an asynchronous message for a given receiver, it delegates control
  289. * to the message itself by means of the message's <tt>process</tt> method.
  290. *
  291. * This method should only be invoked directly this actor's event loop thread.
  292. *
  293. * @throws InterpreterException
  294. */
  295. public void acceptSelfSend(final ATObject receiver, final ATAsyncMessage msg) throws InterpreterException {
  296. // This is the only place where messages are scheduled
  297. // The receiver is always a local object, receive has
  298. // already been invoked.
  299. mirror_.base_schedule(receiver, msg);
  300. }
  301. /**
  302. * This method makes the actor perform:
  303. * <code>closure&lt;-apply(arguments)@[]</code>
  304. * This receiver actor must be the owner of the closure.
  305. */
  306. public void event_trigger(final ATObject closure, final ATTable arguments, final String type) {
  307. final ELActor owner = this;
  308. receive(new Event("trigger("+closure+")") {
  309. public void process(Object myActorMirror) {
  310. try {
  311. owner.acceptSelfSend(closure,
  312. NATAsyncMessage.createExternalAsyncMessage(Evaluator._APPLY_,
  313. NATTable.of(arguments),
  314. NATTable.EMPTY));
  315. } catch (InterpreterException e) {
  316. Logging.Actor_LOG.error(myActorMirror + ": error triggering "+ type + " handler with args " + arguments, e);
  317. }
  318. }
  319. });
  320. }
  321. /**
  322. * The main entry point for any asynchronous messages sent to this actor
  323. * by external sources.
  324. * @param sender address of the sending actor, used to notify when the receiver has gone offline.
  325. * @param serializedMessage the asynchronous AmbientTalk base-level message to enqueue
  326. */
  327. public void event_remoteAccept(final Address sender, final Packet serializedMessage) {
  328. receive(new Event("remoteAccept("+serializedMessage+")") {
  329. public void process(Object myActorMirror) {
  330. try {
  331. // receive a pair [receiver, message]
  332. ATObject[] pair = serializedMessage.unpack().asNativeTable().elements_;
  333. ATObject receiver = pair[0];
  334. ATAsyncMessage msg = pair[1].asAsyncMessage();
  335. performAccept(receiver, msg);
  336. } catch (XObjectOffline e) {
  337. host_.event_objectTakenOffline(e.getObjectId(), sender);
  338. Logging.Actor_LOG.warn(mirror_ + ": could not process "+ serializedMessage + ", object offline: " + e.getObjectId());
  339. } catch (InterpreterException e) {
  340. Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
  341. }
  342. }
  343. });
  344. }
  345. /**
  346. * The main entry point for any asynchronous messages sent to this actor
  347. * by local actors.
  348. * @param ref the local reference of the sending actor, used to notify when the receiver has gone offline.
  349. * @param serializedMessage the asynchronous AmbientTalk base-level message to enqueue
  350. */
  351. public void event_localAccept(final NATLocalFarRef ref, final Packet serializedMessage) {
  352. receive(new Event("localAccept("+serializedMessage+")") {
  353. public void process(Object myActorMirror) {
  354. try {
  355. // receive a pair [receiver, message]
  356. ATObject[] pair = serializedMessage.unpack().asNativeTable().elements_;
  357. ATObject receiver = pair[0];
  358. ATAsyncMessage msg = pair[1].asAsyncMessage();
  359. performAccept(receiver, msg);
  360. } catch (XObjectOffline e) {
  361. ref.notifyTakenOffline();
  362. Logging.Actor_LOG.warn(mirror_ + ": could not process "+ serializedMessage + ", object offline: " + e.getObjectId());
  363. } catch (InterpreterException e) {
  364. Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
  365. }
  366. }
  367. });
  368. }
  369. public void event_serve() {
  370. receive(new Event("serve()") {
  371. public void process(Object myActorMirror) {
  372. try {
  373. ATObject result = mirror_.base_serve();
  374. Logging.Actor_LOG.debug(mirror_ + ": serve() returned " + result);
  375. } catch (InterpreterException e) {
  376. System.out.println(">>> Exception in actor " + myActorMirror + ": "+e.getMessage());
  377. e.printAmbientTalkStackTrace(System.out);
  378. Logging.Actor_LOG.error(mirror_ + ": serve() failed ", e);
  379. }
  380. }
  381. });
  382. }
  383. private void performAccept(ATObject receiver, ATAsyncMessage msg) {
  384. try {
  385. ATObject result = mirror_.base_receive(receiver, msg);
  386. Logging.Actor_LOG.debug(mirror_ + ": scheduling "+ msg + " returned " + result);
  387. // signal a serve event for every message that is accepted
  388. // event_serve();
  389. } catch (InterpreterException e) {
  390. System.out.println(">>> Exception in actor " + getImplicitActorMirror() + ": "+e.getMessage());
  391. e.printAmbientTalkStackTrace(System.out);
  392. Logging.Actor_LOG.error(mirror_ + ": scheduling "+ msg + " failed ", e);
  393. }
  394. }
  395. /**
  396. * This method is invoked by a coercer in order to schedule a purely asynchronous symbiotic invocation
  397. * from the Java world.
  398. *
  399. * This method schedules the call for asynchronous execution. Its return value and or raised exceptions
  400. * are ignored. This method should only be used for {@link Method} objects whose return type is <tt>void</tt>
  401. * and whose declaring class is a subtype of {@link EventListener}. It represents asynchronous method
  402. * invocations from the Java world to the AmbientTalk world.
  403. *
  404. * @param principal the AmbientTalk object owned by this actor on which to invoke the method
  405. * @param method the Java method that was symbiotically invoked on the principal
  406. * @param args the arguments to the Java method call, already converted into AmbientTalk values
  407. */
  408. public void event_symbioticInvocation(final ATObject principal, final Method method, final ATObject[] args) {
  409. receive(new Event("asyncSymbioticInv of "+method.getName()) {
  410. public void process(Object actorMirror) {
  411. try {
  412. Reflection.downInvocation(principal, method, args);
  413. } catch (InterpreterException e) {
  414. System.out.println(">>> Exception in actor " + actorMirror + ": "+e.getMessage());
  415. e.printAmbientTalkStackTrace(System.out);
  416. Logging.Actor_LOG.error("asynchronous symbiotic invocation of "+method.getName()+" failed", e);
  417. }
  418. }
  419. });
  420. }
  421. /**
  422. * This method is invoked by a coercer in order to schedule a symbiotic invocation
  423. * from the Java world, which should be synchronous to the Java thread, but which
  424. * must be scheduled asynchronously to comply with the AT/2 actor model.
  425. *
  426. * The future returned by this method makes the calling (Java) thread <b>block</b> upon
  427. * accessing its value, waiting until the actor has processed the symbiotic invocation.
  428. *
  429. * @param principal the AmbientTalk object owned by this actor on which to invoke the method
  430. * @param meth the Java method that was symbiotically invoked on the principal
  431. * @param args the arguments to the Java method call, already converted into AmbientTalk values
  432. * @return a Java future that is resolved with the result of the symbiotic invocation
  433. * @throws Exception if the symbiotic invocation fails
  434. */
  435. public BlockingFuture sync_event_symbioticInvocation(final ATObject principal, final Method meth, final ATObject[] args) throws Exception {
  436. return receiveAndReturnFuture("syncSymbioticInv of " + meth.getName(), new Callable() {
  437. public Object call(Object actorMirror) throws Exception {
  438. Class targetType = meth.getReturnType();
  439. ATObject[] actualArgs = args;
  440. // if the return type is BlockingFuture, the first argument should specify the type
  441. // of the value with which BlockingFuture will be resolved
  442. if (targetType.equals(BlockingFuture.class)) {
  443. if ((meth.getParameterTypes().length > 0) && (meth.getParameterTypes()[0].equals(Class.class))) {
  444. targetType = args[0].asJavaClassUnderSymbiosis().getWrappedClass();
  445. // drop first argument, it only exists to specify the targetType
  446. ATObject[] newArgs = new ATObject[args.length-1];
  447. System.arraycopy(args, 1, newArgs, 0, newArgs.length);
  448. actualArgs = newArgs;
  449. }
  450. }
  451. ATObject result = Reflection.downInvocation(principal, meth, actualArgs);
  452. // SUPPORT FOR FUTURES
  453. if (Symbiosis.isAmbientTalkFuture(result)) {
  454. Logging.Actor_LOG.debug("Symbiotic futures: symbiotic call to " + meth.getName() + " returned an AT future");
  455. return Symbiosis.ambientTalkFutureToJavaFuture(result, targetType);
  456. } else {
  457. // return the proper value immediately
  458. return Symbiosis.ambientTalkToJava(result, targetType);
  459. }
  460. }
  461. });
  462. }
  463. /**
  464. * This method is invoked by a coercer in order to schedule a symbiotic invocation
  465. * of a method from java.lang.Object from the Java world, which should be synchronous
  466. * to the Java thread, but which
  467. * must be scheduled asynchronously to comply with the AT/2 actor model.
  468. *
  469. * The future returned by this method makes the calling (Java) thread <b>block</b> upon
  470. * accessing its value, waiting until the actor has processed the symbiotic invocation.
  471. *
  472. * Note: the parameter meth must be a method declared on the class java.lang.Object
  473. * (i.e. toString, hashCode and equals). The invocation is simply forwarded directly
  474. * to the principal with no conversion to an AmbientTalk invocation.
  475. *
  476. * @param principal the AmbientTalk object owned by this actor on which to invoke the method
  477. * @param meth the Java method that was symbiotically invoked on the principal
  478. * @param args the arguments to the Java method call, already converted into AmbientTalk values
  479. * @return a Java future that is resolved with the result of the symbiotic invocation
  480. * @throws Exception if the symbiotic invocation fails
  481. */
  482. public BlockingFuture sync_event_symbioticForwardInvocation(final ATObject principal, final Method meth, final Object[] args) throws Exception {
  483. return receiveAndReturnFuture("syncSymbioticInv of " + meth.getName(), new Callable() {
  484. public Object call(Object actorMirror) throws Exception {
  485. try {
  486. return meth.invoke(principal, args);
  487. } catch (InvocationTargetException e) {
  488. if (e instanceof Exception) {
  489. throw (Exception) e.getTargetException();
  490. } else {
  491. throw e;
  492. }
  493. }
  494. }
  495. });
  496. }
  497. /**
  498. * This method should only be used for purposes such as the IAT shell or unit testing.
  499. * It allows an external thread to make this actor evaluate an arbitrary expression.
  500. *
  501. * @param ast an abstract syntax tree to be evaluated by the receiving actor (in the
  502. * scope of its behaviour).
  503. * @return the result of the evaluation
  504. * @throws InterpreterException if the evaluation fails
  505. */
  506. public ATObject sync_event_eval(final ATAbstractGrammar ast) throws InterpreterException {
  507. try {
  508. return (ATObject) receiveAndWait("nativeEval("+ast+")", new Callable() {
  509. public Object call(Object inActor) throws Exception {
  510. return OBJLexicalRoot._INSTANCE_.base_eval_in_(ast, behaviour_);
  511. }
  512. });
  513. } catch (Exception e) {
  514. if (e instanceof InterpreterException) {
  515. throw (InterpreterException) e;
  516. } else {
  517. Logging.Actor_LOG.fatal("Unexpected Java exception: "+e.getMessage(), e);
  518. throw new RuntimeException("Unexpected exception: "+e);
  519. }
  520. }
  521. }
  522. /**
  523. * This method should only be used for purposes such as the IAT shell or unit testing.
  524. * It allows an external thread to make this actor evaluate an arbitrary expression and to
  525. * print that expression (into a String).
  526. *
  527. * @param ast an abstract syntax tree to be evaluated by the receiving actor (in the
  528. * scope of its behaviour).
  529. * @return the printed result of the evaluation as a String
  530. * @throws InterpreterException if the evaluation fails
  531. */
  532. public String sync_event_evalAndPrint(final ATAbstractGrammar ast) throws InterpreterException {
  533. try {
  534. return (String) receiveAndWait("nativeEval("+ast+")", new Callable() {
  535. public Object call(Object inActor) throws Exception {
  536. return OBJLexicalRoot._INSTANCE_.base_eval_in_(ast, behaviour_).toString();
  537. }
  538. });
  539. } catch (Exception e) {
  540. if (e instanceof InterpreterException) {
  541. throw (InterpreterException) e;
  542. } else {
  543. Logging.Actor_LOG.fatal("Unexpected Java exception: "+e.getMessage(), e);
  544. throw new RuntimeException("Unexpected exception: "+e);
  545. }
  546. }
  547. }
  548. /**
  549. * This method should only be used for purposes of unit testing. It allows
  550. * arbitary code to be scheduled by external threads such as unit testing frameworks.
  551. */
  552. public Object sync_event_performTest(Callable c) throws Exception {
  553. return (ATObject) receiveAndWait("performTest("+c+")", c);
  554. }
  555. /**
  556. * When the discovery manager receives a publication from another local actor or
  557. * another remote VM, it compares the incoming publication against a subscription that
  558. * it had announced previously. For each matching subscription ELDiscoveryActor then
  559. * calls event_serviceJoined to make ELActor deserialize the remote service object and
  560. * apply the when:discovered: listeners passing a (remote/local) far reference to it.
  561. *
  562. * @param myHandler the closure specified as a handler for the actor's subscription
  563. * @param remoteServicePkt serialized form of the reference to the remote discovered service
  564. */
  565. public void event_serviceJoined(final ATObject myHandler, final Packet remoteServicePkt) {
  566. final ELActor owner = this;
  567. receive(new Event("serviceJoined") {
  568. public void process(Object myActorMirror) {
  569. ATObject unserializedRemoteService = null;
  570. try {
  571. // we unserialize the remote service in the receiver actor, so that
  572. // a remote reference gets the correct pool.
  573. unserializedRemoteService = remoteServicePkt.unpack();
  574. //make handle receive this message.
  575. Evaluator.trigger(myHandler, NATTable.of(unserializedRemoteService));
  576. } catch (XIOProblem e) {
  577. Logging.Actor_LOG.error("Error deserializing remote published service: ", e.getCause());
  578. } catch (InterpreterException e) {
  579. Logging.Actor_LOG.error(myActorMirror + ": error triggering when:discovered: handler with arg " + unserializedRemoteService, e);
  580. }
  581. }
  582. });
  583. }
  584. }