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