PageRenderTime 68ms CodeModel.GetById 22ms app.highlight 39ms RepoModel.GetById 2ms app.codeStats 0ms

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