PageRenderTime 54ms CodeModel.GetById 25ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 1ms

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

http://ambienttalk.googlecode.com/
Java | 287 lines | 150 code | 26 blank | 111 comment | 19 complexity | a13f1f1937d5482fbf67d9c1decffd19 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.eval.Evaluator;
 31import edu.vub.at.exceptions.InterpreterException;
 32import edu.vub.at.objects.ATObject;
 33import edu.vub.at.objects.ATTypeTag;
 34import edu.vub.at.objects.natives.NATTable;
 35import edu.vub.at.util.logging.Logging;
 36
 37import java.util.HashSet;
 38import java.util.Iterator;
 39import java.util.LinkedList;
 40import java.util.Set;
 41
 42/**
 43 * The DiscoveryManager is responsible for coupling subscriptions to
 44 * corresponding publications.
 45 *
 46 * @author tvcutsem
 47 */
 48public final class DiscoveryManager {
 49
 50	/**
 51	 * Small container class that represents an entry in the publications list.
 52	 */
 53	public static class Publication {
 54		public final ELActor providerActor_;
 55		public final Packet providedTypeTag_;
 56		public final Packet exportedService_;
 57		public ATTypeTag deserializedTopic_;
 58		public Publication(ELActor provider, Packet type, Packet exportedService) {
 59			providerActor_ = provider;
 60			providedTypeTag_ = type;
 61			exportedService_ = exportedService;
 62		}
 63	}
 64	
 65	/**
 66	 * Small container class that represents an entry in the subscriptions list.
 67	 */
 68	public static class Subscription {
 69		public final ELActor subscriberActor_;
 70		public final Packet requiredTypeTag_;
 71		public final Packet registeredHandler_;
 72		public final boolean isPermanentSubscription_;
 73		public ATTypeTag deserializedTopic_;
 74		public ATObject deserializedHandler_;
 75		public Subscription(ELActor subscriber, Packet type, Packet registeredHandler, boolean permanent) {
 76			subscriberActor_ = subscriber;
 77			requiredTypeTag_ = type;
 78			registeredHandler_ = registeredHandler;
 79			isPermanentSubscription_ = permanent;
 80		}
 81	}
 82	
 83	/**
 84	 * A list of Publication objects that represent locally exported service objects.
 85	 */
 86	private final LinkedList publications_;
 87	
 88	/**
 89	 * A list of Subscription objects that represent local subscription handlers.
 90	 */
 91	private final LinkedList subscriptions_;
 92	
 93	public DiscoveryManager() {
 94		publications_ = new LinkedList();
 95		subscriptions_ = new LinkedList();
 96	}
 97	
 98	/**
 99	 * A new local publication:
100	 *  - is stored locally
101	 *  - is checked against local subscriptions, which fire immediately
102	 *  - is broadcast to all currently connected members (done by VM)
103	 */
104	public void addLocalPublication(Publication pub) {
105		publications_.add(pub);
106		notifyLocalSubscribers(pub);
107	}
108	
109	/**
110	 * A deleted local publication is simply deleted locally. No further actions
111	 * are required because remote VMs do not cache publications.
112	 */
113	public void deleteLocalPublication(Publication pub) {
114		publications_.remove(pub);
115	}
116	
117	/**
118	 * A new local subscription:
119	 *  - is stored locally
120	 *  - is checked against local publications, which may cause the subscription
121	 *    to fire immediately
122	 *  - is broadcast to all currently connected members (done by VM)
123	 */
124	public void addLocalSubscription(Subscription sub) {
125		subscriptions_.add(sub);
126		checkLocalPublishers(sub);
127	}
128	
129	/**
130	 * A deleted local subscription is simply deleted locally. No further actions
131	 * are required because remote VMs do not cache subscriptions.
132	 */
133	public void deleteLocalSubscription(Subscription sub) {
134		subscriptions_.remove(sub);
135	}
136	
137	/**
138	 * Returns all local publications matching the given topic. This method is used
139	 * when a remote VM has broadcast a subscription request or when two VMs discover
140	 * one another.
141	 * 
142	 * @return a Set of Packet objects representing the serialized form of objects
143	 * published under a topic matching the argument topic.
144	 */
145	public Set getLocalPublishedServicesMatching(ATTypeTag topic) {
146		HashSet matchingPubs = new HashSet();
147		for (Iterator iter = publications_.iterator(); iter.hasNext();) {
148			Publication pub = (Publication) iter.next();
149			try {
150				if (pub.deserializedTopic_.base_isSubtypeOf(topic).asNativeBoolean().javaValue) {
151					matchingPubs.add(pub.exportedService_);
152				}
153			} catch (InterpreterException e) {
154				Logging.Actor_LOG.error("error matching types while querying local publications:",e);
155			}
156		}
157		return matchingPubs;
158	}
159	
160	/**
161	 * @return a Set of Packet objects denoting the serialized form of all topics for which
162	 * a local subscription is still open.
163	 */
164	public Set getAllLocalSubscriptionTopics() {
165		
166		// Following Bugfix #54, in order to ensure that the returned set
167		// contains no duplicate type tags, we maintain a second hashset
168		// to filter upon deserialized type tags, because packets containing
169		// the same serialized type tag are not equal!
170		
171		HashSet openSubs = new HashSet();
172		HashSet encounteredSubTopics = new HashSet();
173		for (Iterator iter = subscriptions_.iterator(); iter.hasNext();) {
174			Subscription sub = (Subscription) iter.next();
175			if (!encounteredSubTopics.contains(sub.deserializedTopic_)) {
176				encounteredSubTopics.add(sub.deserializedTopic_);
177				openSubs.add(sub.requiredTypeTag_);
178			}
179		}
180		return openSubs;
181	}
182	
183	/**
184	 * When a remote VM hears the request of the local VM for services it requires,
185	 * it returns its own matching services, using a CMDJoinServices command. Via this
186	 * command, the local discovery manager is notified of external matches.
187	 * 
188	 * @param topic an outstanding subscription topic of this VM
189	 * @param remoteService the remote service matching the topic
190	 */
191	public void notifyOfExternalPublication(ATTypeTag pubTopic, ATObject remoteService) {
192		for (Iterator iter = subscriptions_.iterator(); iter.hasNext();) {
193			Subscription sub = (Subscription) iter.next();
194			try {
195				// publication type Tp <: subscription type Ts
196				if (pubTopic.base_isSubtypeOf(sub.deserializedTopic_).asNativeBoolean().javaValue) {
197					// no need to test for separate actors, publisher is remote to this VM, so surely different actors
198					notify(sub.deserializedHandler_, remoteService);
199					// if the subscription is not permanent, cancel it
200					if (!sub.isPermanentSubscription_) {
201						iter.remove();
202					}
203				}
204			} catch (InterpreterException e) {
205				Logging.Actor_LOG.error("error matching types during external notification:",e);
206			}
207		}
208	}
209	
210	/**
211	 * When a new publication is added locally, it is first checked whether this publication
212	 * can already satisfy some outstanding subscriptions on this VM (but from different actors)
213	 */
214	private void notifyLocalSubscribers(Publication pub) {
215		ATObject deserializedService = null; // only deserialize once we have a match
216		for (Iterator iter = subscriptions_.iterator(); iter.hasNext();) {
217			Subscription sub = (Subscription) iter.next();
218			try {
219                // publication type Tp <: subscription type Ts
220				if (pub.deserializedTopic_.base_isSubtypeOf(sub.deserializedTopic_).asNativeBoolean().javaValue) {
221					
222					// only notify if subscriber is hosted by another actor than publisher
223					if (sub.subscriberActor_ != pub.providerActor_) {
224						if (deserializedService == null) {
225							// first deserialize publisher
226							deserializedService = pub.exportedService_.unpack();
227						}
228						
229						notify(sub.deserializedHandler_, deserializedService);
230						
231						// if the subscription is not permanent, cancel it
232						if (!sub.isPermanentSubscription_) {
233							iter.remove();
234						}
235					}
236				}
237			} catch (InterpreterException e) {
238				Logging.Actor_LOG.error("error matching types during local notification:",e);
239			}
240		}
241	}
242	
243	/**
244	 * When a new subscription is added locally, it is first checked whether this subscription
245	 * can already be satisfied by some local publications on this VM (but from different actors)
246	 */
247	private void checkLocalPublishers(Subscription sub) {
248		for (Iterator iter = publications_.iterator(); iter.hasNext();) {
249			Publication pub = (Publication) iter.next();
250			try {
251                // publication type Tp <: subscription type Ts
252				if (pub.deserializedTopic_.base_isSubtypeOf(sub.deserializedTopic_).asNativeBoolean().javaValue) {
253					
254					// only notify if subscriber is hosted by another actor than publisher
255					if (sub.subscriberActor_ != pub.providerActor_) {
256						
257						notify(sub.deserializedHandler_, pub.exportedService_.unpack());
258						
259						// if the subscription is not permanent, cancel it
260						if (!sub.isPermanentSubscription_) {
261							this.deleteLocalSubscription(sub);
262						}
263					}
264				}
265			} catch (InterpreterException e) {
266				Logging.Actor_LOG.error("error matching types during local notification:",e);
267			}
268		}
269	}
270
271	/**
272	 * Performs <code>handler&lt;-apply([ service ])</code>
273	 */
274	private void notify(ATObject handler, ATObject service) {
275		Logging.VirtualMachine_LOG.debug("notifying: "+handler+"<-(["+service+"])");
276		try {
277			handler.meta_receive(
278				new NATAsyncMessage(Evaluator._APPLY_,
279							        NATTable.atValue(new ATObject[] {
280							           NATTable.atValue(new ATObject[] {service})
281							        }),
282							        NATTable.EMPTY));
283		} catch (InterpreterException e) {
284			Logging.VirtualMachine_LOG.error("DiscoveryManager: error notifying subscriber closure:", e);
285		}
286	}
287}