PageRenderTime 72ms CodeModel.GetById 33ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 1ms

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

http://ambienttalk.googlecode.com/
Java | 380 lines | 217 code | 33 blank | 130 comment | 31 complexity | 5ca24f0253b93e1537f9fb3bf16622a1 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * DiscoveryManager.java created on 18-jan-2007 at 16:03:30
  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.ATFarReference;
 31import edu.vub.at.eval.Evaluator;
 32import edu.vub.at.exceptions.InterpreterException;
 33import edu.vub.at.exceptions.XIOProblem;
 34import edu.vub.at.objects.ATClosure;
 35import edu.vub.at.objects.ATObject;
 36import edu.vub.at.objects.ATTypeTag;
 37import edu.vub.at.objects.natives.NATTable;
 38import edu.vub.at.util.logging.Logging;
 39
 40import java.util.HashSet;
 41import java.util.Iterator;
 42import java.util.LinkedList;
 43import java.util.Set;
 44
 45/**
 46 * The DiscoveryManager is responsible for coupling subscriptions to
 47 * corresponding publications.
 48 *
 49 * @author tvcutsem
 50 */
 51public final class DiscoveryManager {
 52
 53	/**
 54	 * Small container class that represents an entry in the publications list.
 55	 */
 56	public static class Publication {
 57		public final ELActor providerActor_;
 58		public final Packet providedTypeTag_;
 59		public final Packet exportedService_;
 60		public final transient ATObject serviceObject_; // not to be serialized
 61		public ATTypeTag deserializedTopic_;
 62		public Publication(ELActor provider, Packet type, Packet exportedService, ATObject obj) {
 63			providerActor_ = provider;
 64			providedTypeTag_ = type;
 65			exportedService_ = exportedService;
 66			serviceObject_ = obj;
 67		}
 68		
 69		public boolean publishes(ATObject object) throws XIOProblem {
 70			return (serviceObject_.equals(object));
 71		}
 72	}
 73	
 74	/**
 75	 * Small container class that represents an entry in the subscriptions list.
 76	 */
 77	public static class Subscription {
 78		public final ELActor subscriberActor_;
 79		public final Packet requiredTypeTag_;
 80		public final Packet registeredHandler_;
 81		public final boolean isPermanentSubscription_;
 82		public ATTypeTag deserializedTopic_;
 83		public ATObject deserializedHandler_;
 84		public Subscription(ELActor subscriber, Packet type, Packet registeredHandler, boolean permanent) {
 85			subscriberActor_ = subscriber;
 86			requiredTypeTag_ = type;
 87			registeredHandler_ = registeredHandler;
 88			isPermanentSubscription_ = permanent;
 89		}
 90	}
 91	
 92	/**
 93	 * A list of Publication objects that represent locally exported service objects.
 94	 */
 95	private final LinkedList publications_;
 96	
 97	/**
 98	 * A list of disconnected Publication objects that represent locally 
 99	 * exported service objects which were manually disconnected.
100	 */
101	private final LinkedList disconnectedPublications_;
102	
103	/**
104	 * A list of Subscription objects that represent local subscription handlers.
105	 */
106	private final LinkedList subscriptions_;
107	
108	public DiscoveryManager() {
109		publications_ = new LinkedList();
110		disconnectedPublications_ = new LinkedList();
111		subscriptions_ = new LinkedList();
112	}
113	
114	/**
115	 * A new local publication:
116	 *  - is stored locally
117	 *  - is checked against local subscriptions, which fire immediately
118	 *  - is broadcast to all currently connected members (done by VM)
119	 */
120	public void addLocalPublication(Publication pub) {
121		publications_.add(pub);
122		notifyLocalSubscribers(pub);
123	}
124	
125	/**
126	 * A deleted local publication is simply deleted locally. No further actions
127	 * are required because remote VMs do not cache publications.
128	 */
129	public void deleteLocalPublication(Publication pub) {
130		publications_.remove(pub);
131	}
132	
133	/**
134	 * Remove publications for a given object from the publications list,
135	 * and store them in a separate list. They can be reconnected afterwards.
136	 * @param obj whose publications will be disconnected
137	 */
138	public void disconnectLocalPublications(ATObject obj) {
139		HashSet matchingPubs = new HashSet();
140		for (Iterator iter = publications_.iterator(); iter.hasNext();) {
141			Publication pub = (Publication) iter.next();
142			try {
143				if (pub.publishes(obj)) {
144					matchingPubs.add(pub);
145				}
146			} catch (InterpreterException e) {
147				Logging.Actor_LOG.error("error matching types while querying local publications:",e);
148			}
149		}
150		for (Iterator iter = matchingPubs.iterator(); iter.hasNext();) {
151			Publication pub = (Publication) iter.next();
152			disconnectedPublications_.add(pub);
153			publications_.remove(pub);
154		}
155		Logging.Actor_LOG.debug("disconnected "+matchingPubs.size()+" publications.");
156	}
157	
158	/**
159	 * Get disconnected publications back in the publications list.
160	 * @param obj which has disconnected publications
161	 * @return Set of all publications to reconnect
162	 */
163	public Set getLocalDisconnectedPublications(ATObject obj) {
164		HashSet matchingPubs = new HashSet();
165		for (Iterator iter = disconnectedPublications_.iterator(); iter.hasNext();) {
166			Publication pub = (Publication) iter.next();
167			try {
168				if (pub.publishes(obj)) {
169					matchingPubs.add(pub);
170				}
171			} catch (InterpreterException e) {
172				Logging.Actor_LOG.error("error matching types while querying local publications:",e);
173			}
174		}
175		for (Iterator iter = matchingPubs.iterator(); iter.hasNext();) {
176			Publication pub = (Publication) iter.next();
177			disconnectedPublications_.remove(pub);
178		}
179		return matchingPubs;
180	}
181	
182	/**
183	 * A new local subscription:
184	 *  - is stored locally
185	 *  - is checked against local publications, which may cause the subscription
186	 *    to fire immediately
187	 *  - is broadcast to all currently connected members (done by VM)
188	 */
189	public void addLocalSubscription(Subscription sub) {
190		subscriptions_.add(sub);
191		checkLocalPublishers(sub);
192	}
193	
194	/**
195	 * A deleted local subscription is simply deleted locally. No further actions
196	 * are required because remote VMs do not cache subscriptions.
197	 */
198	public void deleteLocalSubscription(Subscription sub) {
199		subscriptions_.remove(sub);
200	}
201	
202	/**
203	 * Returns all local publications matching the given topic. This method is used
204	 * when a remote VM has broadcast a subscription request or when two VMs discover
205	 * one another.
206	 * 
207	 * @return a Set of Packet objects representing the serialized form of objects
208	 * published under a topic matching the argument topic.
209	 */
210	public Set getLocalPublishedServicesMatching(ATTypeTag topic) {
211		HashSet matchingPubs = new HashSet();
212		for (Iterator iter = publications_.iterator(); iter.hasNext();) {
213			Publication pub = (Publication) iter.next();
214			try {
215				if (pub.deserializedTopic_.base_isSubtypeOf(topic).asNativeBoolean().javaValue) {
216					matchingPubs.add(pub.exportedService_);
217				}
218			} catch (InterpreterException e) {
219				Logging.Actor_LOG.error("error matching types while querying local publications:",e);
220			}
221		}
222		return matchingPubs;
223	}
224	
225	/**
226	 * @return a Set of Packet objects denoting the serialized form of all topics for which
227	 * a local subscription is still open.
228	 */
229	public Set getAllLocalSubscriptionTopics() {
230		
231		// Following Bugfix #54, in order to ensure that the returned set
232		// contains no duplicate type tags, we maintain a second hashset
233		// to filter upon deserialized type tags, because packets containing
234		// the same serialized type tag are not equal!
235		
236		HashSet openSubs = new HashSet();
237		HashSet encounteredSubTopics = new HashSet();
238		for (Iterator iter = subscriptions_.iterator(); iter.hasNext();) {
239			Subscription sub = (Subscription) iter.next();
240			if (!encounteredSubTopics.contains(sub.deserializedTopic_)) {
241				encounteredSubTopics.add(sub.deserializedTopic_);
242				openSubs.add(sub.requiredTypeTag_);
243			}
244		}
245		return openSubs;
246	}
247	
248	/**
249	 * When a remote VM hears the request of the local VM for services it requires,
250	 * it returns its own matching services, using a CMDJoinServices command. Via this
251	 * command, the local discovery manager is notified of external matches.
252	 * 
253	 * @param topic an outstanding subscription topic of this VM
254	 * @param remoteService the remote service matching the topic
255	 */
256	public void notifyOfExternalPublication(ATTypeTag pubTopic, Packet remoteServicePkt) {
257		for (Iterator iter = subscriptions_.iterator(); iter.hasNext();) {
258			Subscription sub = (Subscription) iter.next();
259			try {
260				// publication type Tp <: subscription type Ts
261				// note that calling base_isSubtypeOf can execute arbitrary ambientTalk code, and 
262				// this code executed won't be traced by the debugger!
263				if (pubTopic.base_isSubtypeOf(sub.deserializedTopic_).asNativeBoolean().javaValue) {
264					// no need to test for separate actors, publisher is remote to this VM, so surely different actors
265					sub.subscriberActor_.event_serviceJoined(sub.deserializedHandler_, remoteServicePkt);
266					// egb notify(sub.deserializedHandler_, remoteService);
267					// if the subscription is not permanent, cancel it
268					if (!sub.isPermanentSubscription_) {
269						iter.remove();
270					}
271				}
272			} catch (InterpreterException e) {
273				Logging.Actor_LOG.error("error matching types during external notification:",e);
274			}
275		}
276	}
277	
278	/**
279	 * When a new publication is added locally, it is first checked whether this publication
280	 * can already satisfy some outstanding subscriptions on this VM (but from different actors)
281	 */
282	private void notifyLocalSubscribers(Publication pub) {
283		ATObject deserializedService = null; // only deserialize once we have a match
284		for (Iterator iter = subscriptions_.iterator(); iter.hasNext();) {
285			Subscription sub = (Subscription) iter.next();
286			try {
287                // publication type Tp <: subscription type Ts
288				if (pub.deserializedTopic_.base_isSubtypeOf(sub.deserializedTopic_).asNativeBoolean().javaValue) {
289					
290					// only notify if subscriber is hosted by another actor than publisher
291					if (sub.subscriberActor_ != pub.providerActor_) {
292						if (deserializedService == null) {
293							// first deserialize publisher
294							deserializedService = pub.exportedService_.unpack();
295						}
296						
297						notify(sub.deserializedHandler_, deserializedService);
298						
299						// if the subscription is not permanent, cancel it
300						if (!sub.isPermanentSubscription_) {
301							iter.remove();
302						}
303					}
304				}
305			} catch (InterpreterException e) {
306				Logging.Actor_LOG.error("error matching types during local notification:",e);
307			}
308		}
309	}
310	
311	/**
312	 * When a new subscription is added locally, it is first checked whether this subscription
313	 * can already be satisfied by some local publications on this VM (but from different actors)
314	 */
315	private void checkLocalPublishers(Subscription sub) {
316		for (Iterator iter = publications_.iterator(); iter.hasNext();) {
317			Publication pub = (Publication) iter.next();
318			try {
319                // publication type Tp <: subscription type Ts
320				if (pub.deserializedTopic_.base_isSubtypeOf(sub.deserializedTopic_).asNativeBoolean().javaValue) {
321					
322					// only notify if subscriber is hosted by another actor than publisher
323					if (sub.subscriberActor_ != pub.providerActor_) {
324						
325						notify(sub.deserializedHandler_, pub.exportedService_.unpack());
326						
327						// if the subscription is not permanent, cancel it
328						if (!sub.isPermanentSubscription_) {
329							this.deleteLocalSubscription(sub);
330						}
331					}
332				}
333			} catch (InterpreterException e) {
334				Logging.Actor_LOG.error("error matching types during local notification:",e);
335			}
336		}
337	}
338
339	/**
340	 * Performs <code>handler&lt;-apply([ service ])@[]</code>
341	 * 
342	 * @param handler normally a far reference to a closure.
343	 */
344	private void notify(ATObject handler, ATObject service) {
345		Logging.VirtualMachine_LOG.debug("notifying: "+handler+"<-(["+service+"])");
346		try {
347			Evaluator.trigger(handler, NATTable.of(service));
348		} catch (InterpreterException e) {
349			Logging.VirtualMachine_LOG.error("DiscoveryManager: error notifying subscriber closure:", e);
350		}
351	}
352	
353	public Publication[] listPublications(ELActor actor) {
354		LinkedList matches = new LinkedList();
355        for (Iterator iter = publications_.iterator(); iter.hasNext();) {
356                Publication pub = (Publication) iter.next();
357                if (pub.providerActor_ == actor) {
358                        matches.add(pub);
359                }
360        }
361        return (Publication[]) matches.toArray(new Publication[matches.size()]);
362	}
363
364	public Subscription[] listSubscriptions(ELActor actor) {
365		LinkedList matches = new LinkedList();
366        for (Iterator iter = subscriptions_.iterator(); iter.hasNext();) {
367                Subscription sub = (Subscription) iter.next();
368                if (sub.subscriberActor_ == actor) {
369                        matches.add(sub);
370                }
371        }
372        return (Subscription[]) matches.toArray(new Subscription[matches.size()]);
373	}
374	
375	public void reset(){
376		 disconnectedPublications_.clear();
377		 publications_.clear();
378		 subscriptions_.clear();
379	}
380}