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

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