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

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