PageRenderTime 68ms CodeModel.GetById 16ms app.highlight 45ms RepoModel.GetById 2ms app.codeStats 0ms

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