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