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