PageRenderTime 60ms CodeModel.GetById 44ms app.highlight 12ms RepoModel.GetById 2ms app.codeStats 0ms

/interpreter/tags/at2-build190607/src/edu/vub/at/actors/natives/DiscoveryManager.java

http://ambienttalk.googlecode.com/
Java | 283 lines | 151 code | 24 blank | 108 comment | 20 complexity | 3f9ebf990b1ee7eb35b4d13c8da41180 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 subscrption
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		HashSet openSubs = new HashSet();
166		for (Iterator iter = subscriptions_.iterator(); iter.hasNext();) {
167			Subscription sub = (Subscription) iter.next();
168			openSubs.add(sub.requiredTypeTag_);
169		}
170		return openSubs;
171	}
172	
173	/**
174	 * When a remote VM hears the request of the local VM for services it requires,
175	 * it returns its own matching services, using a CMDJoinServices command. Via this
176	 * command, the local discovery manager is notified of external matches.
177	 * 
178	 * @param topic an outstanding subscription topic of this VM
179	 * @param remoteService the remote service matching the topic
180	 */
181	public void notifyOfExternalPublication(ATTypeTag pubTopic, ATObject remoteService) {
182		for (Iterator iter = subscriptions_.iterator(); iter.hasNext();) {
183			Subscription sub = (Subscription) iter.next();
184			try {
185				// publication type Tp <: subscription type Ts
186				if (pubTopic.base_isSubtypeOf(sub.deserializedTopic_).asNativeBoolean().javaValue) {
187					// no need to test for separate actors, publisher is remote to this VM, so surely different actors
188					notify(sub.deserializedHandler_, remoteService);
189					// if the subscription is not permanent, cancel it
190					if (!sub.isPermanentSubscription_) {
191						iter.remove();
192					}
193				}
194			} catch (InterpreterException e) {
195				Logging.Actor_LOG.error("error matching types during external notification:",e);
196			}
197		}
198	}
199	
200	/**
201	 * When a new publication is added locally, it is first checked whether this publication
202	 * can already satisfy some outstanding subscriptions on this VM (but from different actors)
203	 */
204	private void notifyLocalSubscribers(Publication pub) {
205		ATObject deserializedService = null; // only deserialize once we have a match
206		for (Iterator iter = subscriptions_.iterator(); iter.hasNext();) {
207			Subscription sub = (Subscription) iter.next();
208			try {
209                // publication type Tp <: subscription type Ts
210				if (pub.deserializedTopic_.base_isSubtypeOf(sub.deserializedTopic_).asNativeBoolean().javaValue) {
211					
212					// only notify if subscriber is hosted by another actor than publisher
213					if (sub.subscriberActor_ != pub.providerActor_) {
214						if (deserializedService == null) {
215							// first deserialize publisher
216							deserializedService = pub.exportedService_.unpack();
217						}
218						
219						notify(sub.deserializedHandler_, deserializedService);
220						
221						// if the subscription is not permanent, cancel it
222						if (!sub.isPermanentSubscription_) {
223							iter.remove();
224						}
225					}
226				}
227			} catch (InterpreterException e) {
228				Logging.Actor_LOG.error("error matching types during local notification:",e);
229			}
230		}
231	}
232	
233	/**
234	 * When a new subscription is added locally, it is first checked whether this subscription
235	 * can already be satisfied by some local publications on this VM (but from different actors)
236	 */
237	private void checkLocalPublishers(Subscription sub) {
238		ATObject deserializedService = null; // only deserialize once we have a match
239		for (Iterator iter = publications_.iterator(); iter.hasNext();) {
240			Publication pub = (Publication) iter.next();
241			try {
242                // publication type Tp <: subscription type Ts
243				if (pub.deserializedTopic_.base_isSubtypeOf(sub.deserializedTopic_).asNativeBoolean().javaValue) {
244					
245					// only notify if subscriber is hosted by another actor than publisher
246					if (sub.subscriberActor_ != pub.providerActor_) {
247						if (deserializedService == null) {
248							// first deserialize publisher
249							deserializedService = pub.exportedService_.unpack();
250						}
251						
252						notify(sub.deserializedHandler_, deserializedService);
253						
254						// if the subscription is not permanent, cancel it
255						if (!sub.isPermanentSubscription_) {
256							this.deleteLocalSubscription(sub);
257						}
258					}
259				}
260			} catch (InterpreterException e) {
261				Logging.Actor_LOG.error("error matching types during local notification:",e);
262			}
263		}
264	}
265
266	/**
267	 * Performs <code>handler&lt;-apply([ service ])</code>
268	 */
269	private void notify(ATObject handler, ATObject service) {
270		Logging.VirtualMachine_LOG.debug("notifying: "+handler+"<-(["+service+"])");
271		try {
272			handler.meta_receive(
273				new NATAsyncMessage(handler,
274							        Evaluator._APPLY_,
275							        NATTable.atValue(new ATObject[] {
276							           NATTable.atValue(new ATObject[] {service})
277							        }),
278							        NATTable.EMPTY));
279		} catch (InterpreterException e) {
280			Logging.VirtualMachine_LOG.error("DiscoveryManager: error notifying subscriber closure:", e);
281		}
282	}
283}