/interpreter/tags/at2dist091109/src/edu/vub/at/actors/natives/ELVirtualMachine.java

http://ambienttalk.googlecode.com/ · Java · 354 lines · 168 code · 46 blank · 140 comment · 7 complexity · f7d096f8ef66875138ba4c05b954a3cc MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * ELVirtualMachine.java created on Nov 1, 2006 at 8:32:31 PM
  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.eventloops.BlockingFuture;
  30. import edu.vub.at.actors.eventloops.Event;
  31. import edu.vub.at.actors.eventloops.EventLoop;
  32. import edu.vub.at.actors.id.ATObjectID;
  33. import edu.vub.at.actors.id.ActorID;
  34. import edu.vub.at.actors.id.VirtualMachineID;
  35. import edu.vub.at.actors.net.ConnectionListenerManager;
  36. import edu.vub.at.actors.net.VMAddressBook;
  37. import edu.vub.at.actors.net.cmd.CMDHandshake;
  38. import edu.vub.at.actors.net.cmd.CMDObjectTakenOffline;
  39. import edu.vub.at.actors.net.cmd.CMDObjectDisconnected;
  40. import edu.vub.at.actors.net.cmd.CMDObjectReconnected;
  41. import edu.vub.at.actors.net.comm.Address;
  42. import edu.vub.at.actors.net.comm.CommunicationBus;
  43. import edu.vub.at.actors.net.comm.NetworkException;
  44. import edu.vub.at.eval.Evaluator;
  45. import edu.vub.at.exceptions.InterpreterException;
  46. import edu.vub.at.objects.ATAbstractGrammar;
  47. import edu.vub.at.objects.natives.NATMethod;
  48. import edu.vub.at.objects.natives.NATTable;
  49. import edu.vub.at.objects.natives.grammar.AGBegin;
  50. import edu.vub.at.util.logging.Logging;
  51. import java.util.Hashtable;
  52. /**
  53. * A ELVirtualMachine represents a virtual machine which hosts several actors. The
  54. * virtual machine is in charge of creating connections with other virtual machines
  55. * and orchestrates the broadcasting of its presence, the exchange of service
  56. * descriptions and messages. It also contains a set of runtime parameters (such as
  57. * the objectpath and initfile) which are needed to initialise a new actor.
  58. *
  59. * @author tvcutsem
  60. * @author smostinc
  61. */
  62. public final class ELVirtualMachine extends EventLoop {
  63. public static final String _DEFAULT_GROUP_NAME_ = "AmbientTalk";
  64. /** startup parameter to the VM: the code of the init.at file to use */
  65. private final ATAbstractGrammar initialisationCode_;
  66. /** startup parameter to the VM: the list of fields to be initialized in every hosted actor */
  67. private final SharedActorField[] sharedFields_;
  68. /** the VirtualMachineID of this VM */
  69. private final VirtualMachineID vmId_;
  70. /**
  71. * A table mapping VM GUIDs to Address objects.
  72. * Each time a VM connects, it sends its VirtualMachineID and an entry
  73. * mapping that VirtualMachineID to its current Address is registered in this table. When a remote reference
  74. * needs to send a message to the remote object, the VM is contacted based on its VirtualMachineID and this
  75. * table. When a VM disconnects, the disconnecting address is removed from this table.
  76. */
  77. public final VMAddressBook vmAddressBook_;
  78. /** a table mapping actor IDs to local native actors (int -> ELActor) */
  79. private final Hashtable localActors_;
  80. /** the communication bus for this Virtual Machine */
  81. public final CommunicationBus communicationBus_;
  82. /** manager for disconnection and reconnection observers */
  83. public final ConnectionListenerManager connectionManager_;
  84. /** the actor responsible for hosting the publications and subscriptions of this VM's actors */
  85. public final ELDiscoveryActor discoveryActor_;
  86. /**
  87. * Construct a new AmbientTalk virtual machine where...
  88. * @param initCode is the code to be executed in each new created actor (the content of the init.at file)
  89. * @param fields are all of the fields that should be present in each new created actor (e.g. the 'system' object of IAT)
  90. * @param groupName is the name of the overlay network to join
  91. */
  92. public ELVirtualMachine(ATAbstractGrammar initCode, SharedActorField[] fields, String groupName) {
  93. super("virtual machine");
  94. this.start();
  95. // used to initialize actors
  96. initialisationCode_ = initCode;
  97. sharedFields_ = fields;
  98. // used to allow actors to send messages to remote vms/actors
  99. vmAddressBook_ = new VMAddressBook();
  100. vmId_ = new VirtualMachineID();
  101. localActors_ = new Hashtable();
  102. discoveryActor_ = new ELDiscoveryActor(this);
  103. localActors_.put(discoveryActor_.getActorID(), discoveryActor_);
  104. discoveryActor_.event_init();
  105. // initialize the message dispatcher using a JChannel
  106. connectionManager_ = new ConnectionListenerManager();
  107. communicationBus_ = new CommunicationBus(this, groupName);
  108. Logging.VirtualMachine_LOG.info(this + ": VM created on network " + groupName);
  109. }
  110. public static final ELVirtualMachine currentVM() {
  111. return ELActor.currentActor().getHost();
  112. }
  113. public VirtualMachineID getGUID() { return vmId_; }
  114. public ATAbstractGrammar getInitialisationCode() {
  115. return initialisationCode_;
  116. }
  117. public SharedActorField[] getFieldsToInitialize() {
  118. return sharedFields_;
  119. }
  120. public ELVirtualMachine getHost() { return this; }
  121. /**
  122. * An event loop handles events by dispatching to the event itself.
  123. */
  124. public void handle(Event event) {
  125. // make the event process itself
  126. event.process(this);
  127. }
  128. /**
  129. * returns the local actor corresponding to the given actor Id.
  130. * This method synchronizes on the localActors_ table to ensure that
  131. * insertion and lookup are properly synchronized.
  132. */
  133. public ELActor getActor(ActorID id) {
  134. ELActor entry;
  135. synchronized (localActors_) {
  136. entry = (ELActor) localActors_.get(id);
  137. }
  138. if (entry != null) {
  139. return entry;
  140. } else {
  141. throw new RuntimeException("Asked for unknown actor id: " + id);
  142. }
  143. }
  144. /**
  145. * Signals that this VM can connect to the underlying network channel
  146. * and can start distributed interaction.
  147. */
  148. public void event_goOnline() {
  149. this.receive(new Event("goOnline") {
  150. public void process(Object myself) {
  151. try {
  152. Address myAddress = communicationBus_.connect();
  153. Logging.VirtualMachine_LOG.info(this + ": interpreter online, address = " + myAddress);
  154. } catch (NetworkException e) {
  155. Logging.VirtualMachine_LOG.fatal(this + ": could not connect to network:", e);
  156. }
  157. }
  158. });
  159. }
  160. /**
  161. * Signals that this VM must disconnect from the underlying discovery channel and communication bus
  162. */
  163. public void event_goOffline() {
  164. this.receive(new Event("goOffline") {
  165. public void process(Object myself) {
  166. try {
  167. communicationBus_.disconnect();
  168. Logging.VirtualMachine_LOG.info(this + ": interpreter offline");
  169. } catch (Exception e) {
  170. Logging.VirtualMachine_LOG.fatal(this + ": error while going offline:", e);
  171. }
  172. }
  173. });
  174. }
  175. /**
  176. * Notifies the discovery manager that a VM has joined the network.
  177. * This VM may be a first-time participant or it may be a previously
  178. * disconnected VM that has become reconnected.
  179. *
  180. * This VM will handshake with the connected VM to exchange their actual
  181. * {@link VirtualMachineID}s rather than their network addresses.
  182. */
  183. public void event_memberJoined(final Address remoteVMAddress) {
  184. this.receive(new Event("memberJoined("+remoteVMAddress+")") {
  185. public void process(Object myself) {
  186. Logging.VirtualMachine_LOG.info(this + ": VM connected: " + remoteVMAddress);
  187. // send a handshake message to exchange IDs
  188. new CMDHandshake(vmId_).send(communicationBus_, remoteVMAddress);
  189. }
  190. });
  191. }
  192. public void event_memberLeft(final Address virtualMachine) {
  193. this.receive(new Event("memberLeft("+virtualMachine+")") {
  194. public void process(Object myself) {
  195. Logging.VirtualMachine_LOG.info(this + ": VM disconnected: " + virtualMachine);
  196. // Identify the VirtualMachineID that corresponds to this address
  197. VirtualMachineID disconnected = vmAddressBook_.getGUIDOf(virtualMachine);
  198. // disconnected may be null if the memberJoined event was ignored because this VM
  199. // was already offline when the event was being processed.
  200. if(disconnected != null) {
  201. // delete entries mapping to Address from the vm Address Book table first,
  202. // so sending threads may have 'premonitions' that they are no longer connected
  203. vmAddressBook_.removeEntry(virtualMachine);
  204. // properly (but synchronously) notify all remote references of a disconnection
  205. connectionManager_.notifyDisconnected(disconnected);
  206. }
  207. }
  208. });
  209. }
  210. /* ==========================
  211. * == Actor -> VM Protocol ==
  212. * ========================== */
  213. // All methods prefixed by event_ denote asynchronous message sends that will be
  214. // scheduled in the receiving event loop's event queue
  215. /**
  216. * Event that signals the deletion of an object from the export table of an
  217. * actor on this virtual machine.
  218. */
  219. public void event_objectTakenOffline(final ATObjectID objId, final Address receiver) {
  220. this.receive( new Event("objectTakenOffline(" + objId +")") {
  221. public void process(Object myself){
  222. if ( receiver == null){
  223. //notify myself in case local remote references in this machine register a listener
  224. connectionManager_.notifyObjectTakenOffline(objId);
  225. //broadcast to other virtual machines that an object has gone offline.
  226. new CMDObjectTakenOffline(objId).broadcast(communicationBus_);
  227. } else{
  228. //sending to a known virtual machine in response to an XObjectOffline exception.
  229. new CMDObjectTakenOffline(objId).send(communicationBus_, receiver);
  230. }
  231. }
  232. });
  233. }
  234. /**
  235. * Event that signals the manual disconnect of a previously exported and
  236. * object on this VM.
  237. */
  238. public void event_objectDisconnected(final ATObjectID objId) {
  239. this.receive( new Event("objectDisconnected(" + objId +")") {
  240. public void process(Object myself){
  241. //notify myself in case local remote references in this machine register a listener
  242. connectionManager_.notifyObjectDisconnected(objId);
  243. //broadcast to other virtual machines that an object has disconnected.
  244. new CMDObjectDisconnected(objId).broadcast(communicationBus_);
  245. }
  246. });
  247. }
  248. /**
  249. * Event that signals the manual reconnect of a previously exported and
  250. * disconnected object on this VM.
  251. */
  252. public void event_objectReconnected(final ATObjectID objId) {
  253. this.receive( new Event("objectReconnected(" + objId +")") {
  254. public void process(Object myself){
  255. //notify myself in case local remote references in this machine register a listener
  256. connectionManager_.notifyObjectReconnected(objId);
  257. //broadcast to other virtual machines that an object has reconnected.
  258. new CMDObjectReconnected(objId).broadcast(communicationBus_);
  259. }
  260. });
  261. }
  262. /**
  263. * Auxiliary creation method to create an actor with an empty behaviour.
  264. * Equivalent to evaluating:
  265. *
  266. * actor: { nil }
  267. */
  268. public NATLocalFarRef createEmptyActor() throws InterpreterException {
  269. Packet noParams = new Packet(NATTable.EMPTY);
  270. Packet noinitcode = new Packet(new NATMethod(Evaluator._ANON_MTH_NAM_, NATTable.EMPTY, new AGBegin(NATTable.of(Evaluator.getNil())), NATTable.EMPTY));
  271. return createActor(noParams, noinitcode);
  272. }
  273. /**
  274. * Creates a new actor on this Virtual Machine. The actor its behaviour
  275. * is intialized by means of the passed parameters and initialization code. The calling
  276. * thread is **blocked** until the actor has been constructed. However, actor behaviour
  277. * and root initialization is carried out by the newly created actor itself.
  278. *
  279. * @param parametersPkt the serialized parameters used to invoke the initialization code
  280. * @param initcodePkt the serialized initialization code used to initialize the actor behaviour
  281. * @param actorMirror this actor's mirror
  282. * @return a far reference to the behaviour of the actor
  283. * @throws InterpreterException
  284. */
  285. public NATLocalFarRef createActor(Packet parametersPkt,
  286. Packet initcodePkt) throws InterpreterException {
  287. BlockingFuture future = new BlockingFuture();
  288. NATActorMirror mirror = new NATActorMirror(this);
  289. ELActor processor = new ELActor(mirror, this);
  290. mirror.setActor(processor); // make mirror refer to the created actor
  291. // lock the localActors_ table first to ensure addition is
  292. // atomic w.r.t. lookup in getActor
  293. synchronized (localActors_) {
  294. localActors_.put(processor.getActorID(), processor);
  295. }
  296. // schedule special 'init' message which will:
  297. // A) create a new behaviour and will unblock creating actor (by passing it a far ref via the future)
  298. // B) unpack the parameters used to invoke the initialization code
  299. // C) unpack the init code to initialize the behaviour
  300. // D) initialize the root and lobby objects of this actor
  301. processor.event_init(future, parametersPkt, initcodePkt);
  302. try {
  303. return (NATLocalFarRef) future.get();
  304. } catch (Exception e) {
  305. throw (InterpreterException) e;
  306. }
  307. }
  308. }