PageRenderTime 30ms CodeModel.GetById 7ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://ambienttalk.googlecode.com/
Java | 379 lines | 215 code | 27 blank | 137 comment | 8 complexity | 4b05de168ca298fb84a02a45d1958522 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * ELDiscoveryActor.java created on 23-feb-2007 at 11:45:46
  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 java.util.HashSet;
 31import java.util.Iterator;
 32import java.util.Set;
 33
 34import edu.vub.at.actors.eventloops.Callable;
 35import edu.vub.at.actors.eventloops.Event;
 36import edu.vub.at.actors.natives.DiscoveryManager.Publication;
 37import edu.vub.at.actors.natives.DiscoveryManager.Subscription;
 38import edu.vub.at.actors.net.cmd.CMDInitRequireServices;
 39import edu.vub.at.actors.net.cmd.CMDJoinServices;
 40import edu.vub.at.actors.net.cmd.CMDProvideService;
 41import edu.vub.at.actors.net.cmd.CMDRequireService;
 42import edu.vub.at.actors.net.comm.Address;
 43import edu.vub.at.exceptions.InterpreterException;
 44import edu.vub.at.objects.ATObject;
 45import edu.vub.at.objects.ATTypeTag;
 46import edu.vub.at.util.logging.Logging;
 47import edu.vub.util.MultiMap;
 48
 49/**
 50 * Every VM has an associated DiscoveryBus Actor. This is a regular actor (with a native Actor Mirror)
 51 * which is responsible for matching local publications with local and remote subscriptions.
 52 *
 53 * @author tvcutsem
 54 */
 55public final class ELDiscoveryActor extends ELActor {
 56
 57	/** manages subscriptions and publications */
 58	private final DiscoveryManager discoveryManager_;
 59	
 60	public ELDiscoveryActor(ELVirtualMachine host) {
 61		super(host);
 62		discoveryManager_ = new DiscoveryManager();
 63	}
 64	
 65	/**
 66	 * A dedicated initialization procedure for the discovery actor
 67	 */
 68	protected void event_init() {
 69		receive(new Event("initDiscovery("+this+")") {
 70			public void process(Object byMyself) {
 71				try {
 72					// !! CODE DUPLICATED FROM ELActor's event_init !!
 73					
 74					// initialize lexically visible fields
 75					initSharedFields();
 76
 77					// go on to initialize the root and all lexically visible fields
 78					initRootObject();
 79				} catch (InterpreterException e) {
 80					Logging.Actor_LOG.error("error while initializing discovery actor", e);
 81				}
 82			}
 83		});
 84	}
 85	
 86	/**
 87     * This event is fired whenever an object
 88     * is being offered as a service provide using the provide: language construct. 
 89     * The discovery actor keeps track of such services and is responsible for the 
 90     * matching between services and clients. When such matches are detected the VM
 91     * will send a foundResolution event to both involved actors. If the VM detects 
 92     * that one partner has become unavailable it will send the lostResolution event
 93     * 
 94     * @param pub - a publication containing the serialized forms of the topic and the exported service object
 95     */
 96	public void event_servicePublished(final Publication pub) {
 97		this.receive(new Event("servicePublished("+pub.providedTypeTag_+")") {
 98			public void process(Object myself) {
 99				try {
100					pub.deserializedTopic_ = pub.providedTypeTag_.unpack().asTypeTag();
101					discoveryManager_.addLocalPublication(pub);
102					// broadcast the new publication to all currently connected VMs
103					new CMDProvideService(pub.providedTypeTag_, pub.exportedService_).send(host_.communicationBus_);
104				} catch (InterpreterException e) {
105					Logging.VirtualMachine_LOG.error("error while publishing service " + pub.providedTypeTag_,e);
106				}
107			}
108		});
109	}
110	
111    /**
112     * This event is fired whenever an object
113     * requests a service using the require: language construct. The discovery manager 
114     * keeps track of such requests and is responsible matching services and clients. 
115     * When such matches are detected the VM will send a foundResolution event to both
116     * involved actors. If the VM detects that one partner has become unavailable it 
117     * will send the lostResolution event
118     * 
119     * @param sub - a subscription containing the serialized forms of the topic and the subscription handler
120     */
121	public void event_clientSubscribed(final Subscription sub) {
122		this.receive(new Event("clientSubscribed("+sub.requiredTypeTag_+")") {
123			public void process(Object myself) {
124				try {
125					sub.deserializedTopic_ = sub.requiredTypeTag_.unpack().asTypeTag();
126					sub.deserializedHandler_ = sub.registeredHandler_.unpack();
127					discoveryManager_.addLocalSubscription(sub);
128					// broadcast the new subscription to all currently connected VMs
129					new CMDRequireService(sub.requiredTypeTag_).send(host_.communicationBus_);
130				} catch (InterpreterException e) {
131					Logging.VirtualMachine_LOG.error("error while subscribing to service " + sub.requiredTypeTag_,e);
132				}
133			}
134		});
135	}
136
137    /**
138     * This event is fired whenever a service
139     * offer is being revoked. In this case, the discovery manager ensures that the 
140     * object is no longer discoverable to new clients. However, it will not send 
141     * disconnected events as these signal that an object has become unreachable.
142     * In other words: remote objects that had already discovered the object linked
143     * to this publication will maintain their connection.
144     * 
145     * @param pub - the original publication object to cancel
146     */
147	public void event_cancelPublication(final Publication pub) {
148		this.receive(new Event("cancelPublication("+pub.providedTypeTag_+")") {
149			public void process(Object myself) {
150				discoveryManager_.deleteLocalPublication(pub);
151			}
152		});
153	}
154	
155	/**
156	 * This event is fired whenever a service
157	 * offer is being disconnected. In this case, the discovery manager ensures that the 
158	 * object is no longer discoverable to new clients.
159	 * 
160	 * @param obj - the object whose publications should be disconnected
161	 */
162	public void event_disconnectPublications(final ATObject obj) {
163		this.receive(new Event("disconnectPublications("+obj+")") {
164			public void process(Object myself) {
165				discoveryManager_.disconnectLocalPublications(obj);
166			}
167		});
168	}
169	
170	/**
171	 * This event is fired to reconnect disconnected publications. The discovery manager
172	 * will make the object discoverable to new clients. All other VMs are signaled the re-publication
173	 * of publications associated with this object.
174	 * 
175	 * @param obj
176	 */
177	public void event_reconnectPublications(final ATObject obj) {
178		this.receive(new Event("reconnectPublications("+obj+")") {
179			public void process(Object myself) {
180				Set matchingPubs = discoveryManager_.getLocalDisconnectedPublications(obj);
181				// broadcast the new publication to all currently connected VMs
182				for (Iterator iter = matchingPubs.iterator(); iter.hasNext();) {
183					Publication pub = (Publication) iter.next();
184					try {
185						pub.deserializedTopic_ = pub.providedTypeTag_.unpack().asTypeTag();
186						// put disconnected publications back in the local publications list.
187						discoveryManager_.addLocalPublication(pub);
188						// broadcast the new publication to all currently connected VMs
189						new CMDProvideService(pub.providedTypeTag_, pub.exportedService_).send(host_.communicationBus_);
190						Logging.VirtualMachine_LOG.debug("reconnected "+matchingPubs.size()+" publications");
191					} catch (InterpreterException e) {
192						Logging.VirtualMachine_LOG.error("error while publishing service " + pub.providedTypeTag_ + "of a reconnected object " + obj,e );
193					}
194				}
195			}
196		});
197	}
198
199    /**
200     * This event is fired whenever a service
201     * request is being revoked. In this case, the discovery manager ensures that the 
202     * object will no longer discover new services. However, it will not send 
203     * lostResolution events as these signal that the client has become unreachable.
204     * 
205     * @param sub - the original subscription object to cancel
206     */
207	public void event_cancelSubscription(final Subscription sub) {
208		this.receive(new Event("cancelSubscription("+sub.requiredTypeTag_+")") {
209			public void process(Object myself) {
210				discoveryManager_.deleteLocalSubscription(sub);
211			}
212		});
213	}
214	
215	
216    /**
217     * Received in response to the CMDProvideService command of a remote VM
218     */
219	public void event_remotePublication(final Packet serializedProvidedTopic, final Packet serializedProvidedService) {
220		this.receive(new Event("remotePublication("+serializedProvidedTopic+")") {
221			public void process(Object myself) {
222				try {
223					ATTypeTag providedTopic = serializedProvidedTopic.unpack().asTypeTag();
224					ATObject providedService = serializedProvidedService.unpack();
225					// notify subscribers of the new provided service
226					Logging.VirtualMachine_LOG.debug("notifyOfExternalPublication("+providedTopic+","+providedService+")");
227					discoveryManager_.notifyOfExternalPublication(providedTopic, providedService);
228				} catch (InterpreterException e) {
229					Logging.VirtualMachine_LOG.error("error while unserializing remote published service",e);
230				}
231			}
232		});
233	}
234	
235    /**
236     * Received in response to the CMDJoinServices command of a remote VM
237     * 
238     * @param matchingPublications - a map from serialized ATTypeTag topics to Sets of serialized
239     * ATObjects that provide the serialized topic.
240     */
241	public void event_batchRemotePublications(final MultiMap matchingPublications) {
242		this.receive(new Event("batchRemotePublications") {
243			public void process(Object myself) {
244				Set topics = matchingPublications.keySet();
245				Logging.VirtualMachine_LOG.debug("batchRemotePublications: incoming topics = "+topics+" ("+topics.size()+" items)");
246				// for each topic in the map
247				for (Iterator iter = topics.iterator(); iter.hasNext();) {
248					try {
249						Packet serializedTopic = (Packet) iter.next();
250						ATTypeTag unserializedTopic = serializedTopic.unpack().asTypeTag();
251						Set matchingServices = (Set) matchingPublications.get(serializedTopic);
252						Logging.VirtualMachine_LOG.debug("matchingPublications.get("+serializedTopic+") = "+matchingServices);
253						// for each serialized object exported under the topic
254						for (Iterator iterator = matchingServices.iterator(); iterator.hasNext();) {
255							Packet serializedService = (Packet) iterator.next();
256							ATObject unserializedService = serializedService.unpack();
257							Logging.VirtualMachine_LOG.debug("notifyOfExternalPublication("+unserializedTopic+","+unserializedService+")");
258							discoveryManager_.notifyOfExternalPublication(unserializedTopic, unserializedService);
259						}
260					} catch (InterpreterException e) {
261						Logging.VirtualMachine_LOG.error("error while unserializing remote published service",e);
262					}
263				}	
264			}
265		});
266	}
267	
268	
269    /**
270     * Received in response to the CMDRequireService command of a remote VM
271     * 
272     * TODO: perhaps transform this into a sync_event and let CMDRequireService perform the reply
273     */
274	public void event_remoteSubscription(final Packet serializedRequiredTopic, final Address replyTo) {
275		this.receive(new Event("remoteSubscription("+serializedRequiredTopic+")") {
276			public void process(Object myself) {
277				try {
278					ATTypeTag requiredTopic = serializedRequiredTopic.unpack().asTypeTag();
279					// query local discoverymanager for matching topic
280			    	Set matchingServices = discoveryManager_.getLocalPublishedServicesMatching(requiredTopic);
281			    	Logging.VirtualMachine_LOG.debug("getLocalPubServMatching("+requiredTopic+") = "+matchingServices+" ("+matchingServices.size()+" items)");
282					if (!matchingServices.isEmpty()) {
283						// maps serialized topics to sets of serialized objects that are published under this topic
284						MultiMap matchingTopics = new MultiMap();
285						matchingTopics.putValues(serializedRequiredTopic, matchingServices);
286			    		// send all matching topics back to the requestor
287			    		new CMDJoinServices(matchingTopics).send(host_.communicationBus_, replyTo);	
288			    	}
289				} catch (InterpreterException e) {
290					Logging.VirtualMachine_LOG.error("error while unserializing remote subscription topic",e);
291				}
292			}
293		});
294	}
295	
296	
297    /**
298     * When a new VM has been discovered, the discovery agent is responsible for sending
299     * all outstanding subscription topics to that VM, such that it can be checked whether
300     * the newcomer has some publications that can resolve outstanding requests.
301     */
302	public void event_sendAllSubscriptionsTo(final Address newMember) {
303		this.receive(new Event("sendAllSubscriptionsTo("+newMember+")") {
304			public void process(Object myself) {
305				// check if this VM has some outstanding subscriptions
306				Set subscriptionTopics = discoveryManager_.getAllLocalSubscriptionTopics();
307				Logging.VirtualMachine_LOG.debug("getAllLocalSubTopics() ="+subscriptionTopics+" ("+subscriptionTopics.size()+" items)");
308				// only send a discovery query if this VM requires some services
309				if (!subscriptionTopics.isEmpty()) {
310					// send a discovery query message to the remote VM
311					new CMDInitRequireServices(subscriptionTopics).send(host_.communicationBus_, newMember);
312				}
313			}
314		});
315	}
316	
317    /**
318     * When a VM is discovered by another VM, that VM can send its outstanding subscriptions
319     * to this VM. This event is received by an incoming CMDInitRequireServices command.
320     * The local discovery manager should, for each incoming subscription topic, assemble all matching
321     * local publication objects. A map of topic -> Set of publication objects is then returned
322     * to the sender VM.
323     * 
324     * @param subscriptionTopics - a Set of Packet objects representing serialized ATTypeTag topics
325     */
326	public void event_receiveNewSubscriptionsFrom(final Set subscriptionTopics, final Address fromMember) {
327		this.receive(new Event("receiveNewSubscriptionsFrom("+fromMember+")") {
328			public void process(Object myself) {
329				// maps topics to sets of objects that are published under this topic
330				MultiMap matchingTopics = new MultiMap();
331				
332				// query local discoverymanager for matching topics
333		    	for (Iterator iter = subscriptionTopics.iterator(); iter.hasNext();) {
334					try {
335						Packet serializedTopic = (Packet) iter.next();
336						ATTypeTag topic = serializedTopic.unpack().asTypeTag();
337						Set matchingServices = discoveryManager_.getLocalPublishedServicesMatching(topic);
338						Logging.VirtualMachine_LOG.debug("getLocalPubServMatching("+topic+") ="+matchingServices+" ("+matchingServices.size()+" items)");
339						if (!matchingServices.isEmpty()) {
340							matchingTopics.putValues(serializedTopic, matchingServices);
341						}
342					} catch (InterpreterException e) {
343						Logging.VirtualMachine_LOG.error("error while unserializing remote subscription topic",e);
344					}
345				}
346				
347		    	if (!matchingTopics.isEmpty()) {
348		    		// send all matching topics back to the requestor
349		    		new CMDJoinServices(matchingTopics).send(host_.communicationBus_, fromMember);	
350		    	}
351			}
352		});
353	}
354
355	public Publication[] sync_event_listPublications(final ELActor actor) throws InterpreterException {
356		try {
357			return (Publication[]) this.receiveAndWait("currentPublications("+actor+")", new Callable() {
358				public Object call(Object argument) {
359					return discoveryManager_.listPublications(actor);
360				}
361			});
362		} catch (Exception e) {
363			throw (InterpreterException) e;
364		}
365	}
366
367	public Subscription[] sync_event_listSubscriptions(final ELActor actor) throws InterpreterException {
368		try {
369			return (Subscription[]) this.receiveAndWait("currentPublications("+actor+")", new Callable() {
370				public Object call(Object argument) {
371					return discoveryManager_.listSubscriptions(actor);
372				}
373			});
374		} catch (Exception e) {
375			throw (InterpreterException) e;
376		}
377	}
378	
379}