PageRenderTime 33ms CodeModel.GetById 18ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/at_build150307/src/edu/vub/at/actors/natives/ELFarReference.java

http://ambienttalk.googlecode.com/
Java | 332 lines | 196 code | 52 blank | 84 comment | 33 complexity | e6d1158569a668a45b002125da0a2a9e MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * ELFarReference.java created on 28-dec-2006 at 10:45:41
  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.ATAsyncMessage;
 31import edu.vub.at.actors.eventloops.BlockingFuture;
 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.net.ConnectionListener;
 36import edu.vub.at.actors.net.Logging;
 37import edu.vub.at.actors.net.cmd.CMDTransmitATMessage;
 38import edu.vub.at.eval.Evaluator;
 39import edu.vub.at.exceptions.InterpreterException;
 40import edu.vub.at.exceptions.XIOProblem;
 41import edu.vub.at.objects.ATObject;
 42import edu.vub.at.objects.ATTable;
 43import edu.vub.at.objects.natives.NATTable;
 44
 45import java.util.Iterator;
 46import java.util.Vector;
 47
 48import org.jgroups.Address;
 49import org.jgroups.SuspectedException;
 50import org.jgroups.TimeoutException;
 51import org.jgroups.blocks.MessageDispatcher;
 52
 53/**
 54 * An instance of the class ELFarReference represents the event loop processor for
 55 * a remote far reference. That is, the event queue of this event loop serves as 
 56 * an 'outbox' which is dedicated to a certain receiver object hosted by a remote virtual machine.
 57 * 
 58 * This event loop handles event from its event queue by trying to transmit them to a remote virtual machine.
 59 * 
 60 * @author tvcutsem
 61 */
 62public final class ELFarReference extends EventLoop implements ConnectionListener {
 63	
 64	// When the Far Reference needs to be interrupted, this field will be set to a non-null value
 65	// The handle tests for the presence of such an interrupt and will call the handleInterrupt() 
 66	// method if necessary
 67	private BlockingFuture outboxFuture_ = null;
 68	
 69	/**
 70	 * Signals the far reference that its owning actor has requested to retract unsent messages.
 71	 * The interrupt will be handled as soon as the processing of the current event has finished
 72	 * @return a blocking future the ELActor thread can wait on.
 73	 */
 74	public BlockingFuture setRetractingFuture() {
 75		outboxFuture_ = new BlockingFuture();
 76		
 77		// the reception of a new interrupt may awaken a sleeping ELFarReference 
 78		// thread, so we interrupt them, forcing them to reevaluate their conditions
 79		processor_.interrupt();
 80		
 81		return outboxFuture_;
 82	}
 83	
 84	/**
 85	 * Resolves the current interrupt's future with the vector of transmission events that are 
 86	 * in the event queue of the far reference. This is used to retrieve copies of all messages
 87	 * being sent through this far reference. Note that this method performs no deserialisation
 88	 * of the events into (copied) messages. Such deserialisation needs to be done by an ELActor,
 89	 * not the ELFarReference which will execute this method.
 90	 */
 91	public void handleRetractRequest() {
 92		outboxFuture_.resolve(eventQueue_.flush());
 93	}
 94	
 95	private final ELActor owner_;
 96	private final ATObjectID destination_;
 97	private final MessageDispatcher dispatcher_;
 98	
 99	private boolean connected_;
100	
101	private Vector disconnectedListeners_; // lazy initialization
102	private Vector reconnectedListeners_; // lazy initialization
103	
104	public synchronized void addDisconnectionListener(ATObject listener) {
105		if (disconnectedListeners_ == null) {
106			disconnectedListeners_ = new Vector(1);
107		}
108		disconnectedListeners_.add(listener);
109		
110		if (!connected_) {
111			try {
112
113				owner_.event_acceptSelfSend(NATAsyncMessage.createAsyncMessage(listener,
114						listener, Evaluator._APPLY_, NATTable.atValue(new ATObject[] { NATTable.EMPTY })));
115			} catch (InterpreterException e) {
116				Logging.RemoteRef_LOG.error(
117						"error invoking when:disconnected: listener", e);
118			}
119		}
120	}
121	
122	public synchronized void addReconnectionListener(ATObject listener) {
123		if (reconnectedListeners_ == null) {
124			reconnectedListeners_ = new Vector(1);
125		}
126		reconnectedListeners_.add(listener);
127	}
128
129	public synchronized void removeDisconnectionListener(ATObject listener) {
130		if (disconnectedListeners_ != null) {
131			disconnectedListeners_.remove(listener);
132		}
133	}
134	
135	public synchronized void removeReconnectionListener(ATObject listener) {
136		if (reconnectedListeners_ != null) {
137			reconnectedListeners_.remove(listener);
138		}
139	}
140		
141	public ELFarReference(ATObjectID destination, ELActor owner) {
142		super("far reference " + destination);
143		
144		destination_ = destination;
145		owner_ = owner;
146		
147		connected_ = true;
148		// register the remote reference with the MembershipNotifier to keep track
149		// of the state of the connection with the remote VM
150		owner_.getHost().membershipNotifier_.addConnectionListener(destination_.getVirtualMachineId(), this);
151		
152		dispatcher_ = owner_.getHost().messageDispatcher_;
153	}
154	
155	/**
156	 * Process message transmission events only when the remote reference
157	 * is connected. Otherwise, wait until notified by the <tt>connected</tt> callback.
158	 */
159	public void handle(Event event) {
160		synchronized (this) {
161			while (!connected_ && outboxFuture_ == null) {
162				try {
163					this.wait();
164				} catch (InterruptedException e) { }
165			}
166			
167			if(outboxFuture_ != null) {
168				// re-enqueue the current event in its proper position
169				receivePrioritized(event);
170				
171				// flush the queue
172				handleRetractRequest();
173				
174			// else is strictly necessary as the handleInterrupt method has side effects, 
175			// removing the current event from the queue, such that it would be incorrect 
176			// to still send it 
177			} else { // if (connected_) {
178				event.process(this);
179			}
180		}
181	}
182	
183	/**
184	 * TransmissionEvent is a named subclass of event, which allows access to the message the
185	 * packet it is trying to send which is used, should the event be retracted. Moreover, the
186	 * notion of an explicit constructor which is called by the ELActor scheduling it, ensures
187	 * that the serialization of the message happens in the correct thread.
188	 *
189	 * @author smostinc
190	 */
191	private class TransmissionEvent extends Event {
192		public final Packet serializedMessage_;
193		
194		// Called by ELActor
195		public TransmissionEvent(ATAsyncMessage msg) throws XIOProblem {
196			super("transmit("+msg+")");
197			serializedMessage_ = new Packet(msg.toString(), msg);
198		}
199		
200		// Called by ELFarReference
201		public void process(Object owner) {
202			Address destination = getDestinationVMAddress();
203
204			if (destination != null) {
205				try {
206					Object ack = new CMDTransmitATMessage(destination_
207							.getActorId(), serializedMessage_).send(
208							dispatcher_, destination);
209
210					// non-null return value indicates an exception
211					if (ack != null) {
212						Logging.RemoteRef_LOG.error(this
213								+ ": non-null acknowledgement: " + ack);
214					}
215				} catch (TimeoutException e) {
216					Logging.RemoteRef_LOG.warn(this
217									+ ": timeout while trying to transmit message, retrying");
218					receivePrioritized(this);
219				} catch (SuspectedException e) {
220					Logging.RemoteRef_LOG.warn(this
221							+ ": remote object suspected: " + destination_);
222					receivePrioritized(this);
223				} catch (Exception e) {
224					Logging.RemoteRef_LOG.error(this
225							+ ": error upon message transmission:", e);
226				}
227			} else {
228				Logging.RemoteRef_LOG.info(this + ": suspected a disconnection from " + destination_);
229				connected_ = false;
230			}
231		}
232	}
233	
234	public void event_transmit(final ATAsyncMessage msg) throws XIOProblem {
235		receive(new TransmissionEvent(msg));
236		
237		// the reception of a new event may awaken a sleeping ELFarReference thread, 
238		// so we interrupt the processor, forcing it to reevaluate its conditions
239		processor_.interrupt();
240	}
241	
242	public ATTable retractUnsentMessages() throws InterpreterException {
243		
244		BlockingFuture eventVectorF = setRetractingFuture();
245		
246		
247		try {
248			
249			Vector events = (Vector)eventVectorF.get();
250			ATObject[] messages = new ATObject[events.size()];
251			
252			for(int i = 0; i < events.size(); i++) {
253				TransmissionEvent current = (TransmissionEvent)events.get(i);
254				messages[i] = current.serializedMessage_.unpack();
255			}
256			
257			return NATTable.atValue(messages);
258
259		} catch (Exception e) {
260			e.printStackTrace();
261			//throw (InterpreterException) e;
262			return NATTable.EMPTY;
263		}
264	}
265	
266	/* ========================================================
267	 * == Implementation of the ConnectionListener interface ==
268	 * ========================================================
269	 */
270
271	public synchronized void connected() {
272		Logging.RemoteRef_LOG.info(this + ": reconnected to " + destination_);
273		connected_ = true;
274		this.notify();
275		
276		if (reconnectedListeners_ != null) {
277			for (Iterator reconnectedIter = reconnectedListeners_.iterator(); reconnectedIter.hasNext();) {
278				ATObject listener = (ATObject) reconnectedIter.next();
279				try {
280					owner_.event_acceptSelfSend(
281							NATAsyncMessage.createAsyncMessage(listener, listener, Evaluator._APPLY_, NATTable.atValue(new ATObject[] { NATTable.EMPTY })));
282				} catch (InterpreterException e) {
283					Logging.RemoteRef_LOG.error("error invoking when:reconnected: listener", e);
284				}
285			}	
286		}
287	}
288
289	public synchronized void disconnected() {
290		// Will only take effect when next trying to send a message
291		// If currently sending, the message will time out first.
292		Logging.RemoteRef_LOG.info(this + ": disconnected from " + destination_);
293		connected_ = false;
294		
295		if (disconnectedListeners_ != null) {
296			for (Iterator disconnectedIter = disconnectedListeners_.iterator(); disconnectedIter.hasNext();) {
297				ATObject listener = (ATObject) disconnectedIter.next();
298				try {
299					owner_.event_acceptSelfSend(
300							NATAsyncMessage.createAsyncMessage(listener, listener, Evaluator._APPLY_, NATTable.atValue(new ATObject[] { NATTable.EMPTY })));
301				} catch (InterpreterException e) {
302					Logging.RemoteRef_LOG.error("error invoking when:disconnected: listener", e);
303				}
304			}	
305		}
306	}
307	
308	private Address getDestinationVMAddress() {
309		return owner_.getHost().vmAddressBook_.getAddressOf(destination_.getVirtualMachineId());
310	}
311	
312	
313	public void execute() {
314
315		synchronized (this) {
316			while (eventQueue_.isEmpty() && outboxFuture_ == null) {
317				try {
318					this.wait();
319				} catch (InterruptedException e) { }
320			}
321
322			if (outboxFuture_ != null) {
323				handleRetractRequest();
324			} else { // if(! eventQueue_.isEmpty()) {
325				try {
326					handle(eventQueue_.dequeue());
327				} catch (InterruptedException e) { }
328			}
329		}
330	}
331
332}