/interpreter/tags/at2dist091109/src/edu/vub/at/actors/natives/ELDiscoveryActor.java

http://ambienttalk.googlecode.com/ · Java · 379 lines · 215 code · 27 blank · 137 comment · 8 complexity · 4b05de168ca298fb84a02a45d1958522 MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * ELDiscoveryActor.java created on 23-feb-2007 at 11:45:46
  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 java.util.HashSet;
  30. import java.util.Iterator;
  31. import java.util.Set;
  32. import edu.vub.at.actors.eventloops.Callable;
  33. import edu.vub.at.actors.eventloops.Event;
  34. import edu.vub.at.actors.natives.DiscoveryManager.Publication;
  35. import edu.vub.at.actors.natives.DiscoveryManager.Subscription;
  36. import edu.vub.at.actors.net.cmd.CMDInitRequireServices;
  37. import edu.vub.at.actors.net.cmd.CMDJoinServices;
  38. import edu.vub.at.actors.net.cmd.CMDProvideService;
  39. import edu.vub.at.actors.net.cmd.CMDRequireService;
  40. import edu.vub.at.actors.net.comm.Address;
  41. import edu.vub.at.exceptions.InterpreterException;
  42. import edu.vub.at.objects.ATObject;
  43. import edu.vub.at.objects.ATTypeTag;
  44. import edu.vub.at.util.logging.Logging;
  45. import edu.vub.util.MultiMap;
  46. /**
  47. * Every VM has an associated DiscoveryBus Actor. This is a regular actor (with a native Actor Mirror)
  48. * which is responsible for matching local publications with local and remote subscriptions.
  49. *
  50. * @author tvcutsem
  51. */
  52. public final class ELDiscoveryActor extends ELActor {
  53. /** manages subscriptions and publications */
  54. private final DiscoveryManager discoveryManager_;
  55. public ELDiscoveryActor(ELVirtualMachine host) {
  56. super(host);
  57. discoveryManager_ = new DiscoveryManager();
  58. }
  59. /**
  60. * A dedicated initialization procedure for the discovery actor
  61. */
  62. protected void event_init() {
  63. receive(new Event("initDiscovery("+this+")") {
  64. public void process(Object byMyself) {
  65. try {
  66. // !! CODE DUPLICATED FROM ELActor's event_init !!
  67. // initialize lexically visible fields
  68. initSharedFields();
  69. // go on to initialize the root and all lexically visible fields
  70. initRootObject();
  71. } catch (InterpreterException e) {
  72. Logging.Actor_LOG.error("error while initializing discovery actor", e);
  73. }
  74. }
  75. });
  76. }
  77. /**
  78. * This event is fired whenever an object
  79. * is being offered as a service provide using the provide: language construct.
  80. * The discovery actor keeps track of such services and is responsible for the
  81. * matching between services and clients. When such matches are detected the VM
  82. * will send a foundResolution event to both involved actors. If the VM detects
  83. * that one partner has become unavailable it will send the lostResolution event
  84. *
  85. * @param pub - a publication containing the serialized forms of the topic and the exported service object
  86. */
  87. public void event_servicePublished(final Publication pub) {
  88. this.receive(new Event("servicePublished("+pub.providedTypeTag_+")") {
  89. public void process(Object myself) {
  90. try {
  91. pub.deserializedTopic_ = pub.providedTypeTag_.unpack().asTypeTag();
  92. discoveryManager_.addLocalPublication(pub);
  93. // broadcast the new publication to all currently connected VMs
  94. new CMDProvideService(pub.providedTypeTag_, pub.exportedService_).send(host_.communicationBus_);
  95. } catch (InterpreterException e) {
  96. Logging.VirtualMachine_LOG.error("error while publishing service " + pub.providedTypeTag_,e);
  97. }
  98. }
  99. });
  100. }
  101. /**
  102. * This event is fired whenever an object
  103. * requests a service using the require: language construct. The discovery manager
  104. * keeps track of such requests and is responsible matching services and clients.
  105. * When such matches are detected the VM will send a foundResolution event to both
  106. * involved actors. If the VM detects that one partner has become unavailable it
  107. * will send the lostResolution event
  108. *
  109. * @param sub - a subscription containing the serialized forms of the topic and the subscription handler
  110. */
  111. public void event_clientSubscribed(final Subscription sub) {
  112. this.receive(new Event("clientSubscribed("+sub.requiredTypeTag_+")") {
  113. public void process(Object myself) {
  114. try {
  115. sub.deserializedTopic_ = sub.requiredTypeTag_.unpack().asTypeTag();
  116. sub.deserializedHandler_ = sub.registeredHandler_.unpack();
  117. discoveryManager_.addLocalSubscription(sub);
  118. // broadcast the new subscription to all currently connected VMs
  119. new CMDRequireService(sub.requiredTypeTag_).send(host_.communicationBus_);
  120. } catch (InterpreterException e) {
  121. Logging.VirtualMachine_LOG.error("error while subscribing to service " + sub.requiredTypeTag_,e);
  122. }
  123. }
  124. });
  125. }
  126. /**
  127. * This event is fired whenever a service
  128. * offer is being revoked. In this case, the discovery manager ensures that the
  129. * object is no longer discoverable to new clients. However, it will not send
  130. * disconnected events as these signal that an object has become unreachable.
  131. * In other words: remote objects that had already discovered the object linked
  132. * to this publication will maintain their connection.
  133. *
  134. * @param pub - the original publication object to cancel
  135. */
  136. public void event_cancelPublication(final Publication pub) {
  137. this.receive(new Event("cancelPublication("+pub.providedTypeTag_+")") {
  138. public void process(Object myself) {
  139. discoveryManager_.deleteLocalPublication(pub);
  140. }
  141. });
  142. }
  143. /**
  144. * This event is fired whenever a service
  145. * offer is being disconnected. In this case, the discovery manager ensures that the
  146. * object is no longer discoverable to new clients.
  147. *
  148. * @param obj - the object whose publications should be disconnected
  149. */
  150. public void event_disconnectPublications(final ATObject obj) {
  151. this.receive(new Event("disconnectPublications("+obj+")") {
  152. public void process(Object myself) {
  153. discoveryManager_.disconnectLocalPublications(obj);
  154. }
  155. });
  156. }
  157. /**
  158. * This event is fired to reconnect disconnected publications. The discovery manager
  159. * will make the object discoverable to new clients. All other VMs are signaled the re-publication
  160. * of publications associated with this object.
  161. *
  162. * @param obj
  163. */
  164. public void event_reconnectPublications(final ATObject obj) {
  165. this.receive(new Event("reconnectPublications("+obj+")") {
  166. public void process(Object myself) {
  167. Set matchingPubs = discoveryManager_.getLocalDisconnectedPublications(obj);
  168. // broadcast the new publication to all currently connected VMs
  169. for (Iterator iter = matchingPubs.iterator(); iter.hasNext();) {
  170. Publication pub = (Publication) iter.next();
  171. try {
  172. pub.deserializedTopic_ = pub.providedTypeTag_.unpack().asTypeTag();
  173. // put disconnected publications back in the local publications list.
  174. discoveryManager_.addLocalPublication(pub);
  175. // broadcast the new publication to all currently connected VMs
  176. new CMDProvideService(pub.providedTypeTag_, pub.exportedService_).send(host_.communicationBus_);
  177. Logging.VirtualMachine_LOG.debug("reconnected "+matchingPubs.size()+" publications");
  178. } catch (InterpreterException e) {
  179. Logging.VirtualMachine_LOG.error("error while publishing service " + pub.providedTypeTag_ + "of a reconnected object " + obj,e );
  180. }
  181. }
  182. }
  183. });
  184. }
  185. /**
  186. * This event is fired whenever a service
  187. * request is being revoked. In this case, the discovery manager ensures that the
  188. * object will no longer discover new services. However, it will not send
  189. * lostResolution events as these signal that the client has become unreachable.
  190. *
  191. * @param sub - the original subscription object to cancel
  192. */
  193. public void event_cancelSubscription(final Subscription sub) {
  194. this.receive(new Event("cancelSubscription("+sub.requiredTypeTag_+")") {
  195. public void process(Object myself) {
  196. discoveryManager_.deleteLocalSubscription(sub);
  197. }
  198. });
  199. }
  200. /**
  201. * Received in response to the CMDProvideService command of a remote VM
  202. */
  203. public void event_remotePublication(final Packet serializedProvidedTopic, final Packet serializedProvidedService) {
  204. this.receive(new Event("remotePublication("+serializedProvidedTopic+")") {
  205. public void process(Object myself) {
  206. try {
  207. ATTypeTag providedTopic = serializedProvidedTopic.unpack().asTypeTag();
  208. ATObject providedService = serializedProvidedService.unpack();
  209. // notify subscribers of the new provided service
  210. Logging.VirtualMachine_LOG.debug("notifyOfExternalPublication("+providedTopic+","+providedService+")");
  211. discoveryManager_.notifyOfExternalPublication(providedTopic, providedService);
  212. } catch (InterpreterException e) {
  213. Logging.VirtualMachine_LOG.error("error while unserializing remote published service",e);
  214. }
  215. }
  216. });
  217. }
  218. /**
  219. * Received in response to the CMDJoinServices command of a remote VM
  220. *
  221. * @param matchingPublications - a map from serialized ATTypeTag topics to Sets of serialized
  222. * ATObjects that provide the serialized topic.
  223. */
  224. public void event_batchRemotePublications(final MultiMap matchingPublications) {
  225. this.receive(new Event("batchRemotePublications") {
  226. public void process(Object myself) {
  227. Set topics = matchingPublications.keySet();
  228. Logging.VirtualMachine_LOG.debug("batchRemotePublications: incoming topics = "+topics+" ("+topics.size()+" items)");
  229. // for each topic in the map
  230. for (Iterator iter = topics.iterator(); iter.hasNext();) {
  231. try {
  232. Packet serializedTopic = (Packet) iter.next();
  233. ATTypeTag unserializedTopic = serializedTopic.unpack().asTypeTag();
  234. Set matchingServices = (Set) matchingPublications.get(serializedTopic);
  235. Logging.VirtualMachine_LOG.debug("matchingPublications.get("+serializedTopic+") = "+matchingServices);
  236. // for each serialized object exported under the topic
  237. for (Iterator iterator = matchingServices.iterator(); iterator.hasNext();) {
  238. Packet serializedService = (Packet) iterator.next();
  239. ATObject unserializedService = serializedService.unpack();
  240. Logging.VirtualMachine_LOG.debug("notifyOfExternalPublication("+unserializedTopic+","+unserializedService+")");
  241. discoveryManager_.notifyOfExternalPublication(unserializedTopic, unserializedService);
  242. }
  243. } catch (InterpreterException e) {
  244. Logging.VirtualMachine_LOG.error("error while unserializing remote published service",e);
  245. }
  246. }
  247. }
  248. });
  249. }
  250. /**
  251. * Received in response to the CMDRequireService command of a remote VM
  252. *
  253. * TODO: perhaps transform this into a sync_event and let CMDRequireService perform the reply
  254. */
  255. public void event_remoteSubscription(final Packet serializedRequiredTopic, final Address replyTo) {
  256. this.receive(new Event("remoteSubscription("+serializedRequiredTopic+")") {
  257. public void process(Object myself) {
  258. try {
  259. ATTypeTag requiredTopic = serializedRequiredTopic.unpack().asTypeTag();
  260. // query local discoverymanager for matching topic
  261. Set matchingServices = discoveryManager_.getLocalPublishedServicesMatching(requiredTopic);
  262. Logging.VirtualMachine_LOG.debug("getLocalPubServMatching("+requiredTopic+") = "+matchingServices+" ("+matchingServices.size()+" items)");
  263. if (!matchingServices.isEmpty()) {
  264. // maps serialized topics to sets of serialized objects that are published under this topic
  265. MultiMap matchingTopics = new MultiMap();
  266. matchingTopics.putValues(serializedRequiredTopic, matchingServices);
  267. // send all matching topics back to the requestor
  268. new CMDJoinServices(matchingTopics).send(host_.communicationBus_, replyTo);
  269. }
  270. } catch (InterpreterException e) {
  271. Logging.VirtualMachine_LOG.error("error while unserializing remote subscription topic",e);
  272. }
  273. }
  274. });
  275. }
  276. /**
  277. * When a new VM has been discovered, the discovery agent is responsible for sending
  278. * all outstanding subscription topics to that VM, such that it can be checked whether
  279. * the newcomer has some publications that can resolve outstanding requests.
  280. */
  281. public void event_sendAllSubscriptionsTo(final Address newMember) {
  282. this.receive(new Event("sendAllSubscriptionsTo("+newMember+")") {
  283. public void process(Object myself) {
  284. // check if this VM has some outstanding subscriptions
  285. Set subscriptionTopics = discoveryManager_.getAllLocalSubscriptionTopics();
  286. Logging.VirtualMachine_LOG.debug("getAllLocalSubTopics() ="+subscriptionTopics+" ("+subscriptionTopics.size()+" items)");
  287. // only send a discovery query if this VM requires some services
  288. if (!subscriptionTopics.isEmpty()) {
  289. // send a discovery query message to the remote VM
  290. new CMDInitRequireServices(subscriptionTopics).send(host_.communicationBus_, newMember);
  291. }
  292. }
  293. });
  294. }
  295. /**
  296. * When a VM is discovered by another VM, that VM can send its outstanding subscriptions
  297. * to this VM. This event is received by an incoming CMDInitRequireServices command.
  298. * The local discovery manager should, for each incoming subscription topic, assemble all matching
  299. * local publication objects. A map of topic -> Set of publication objects is then returned
  300. * to the sender VM.
  301. *
  302. * @param subscriptionTopics - a Set of Packet objects representing serialized ATTypeTag topics
  303. */
  304. public void event_receiveNewSubscriptionsFrom(final Set subscriptionTopics, final Address fromMember) {
  305. this.receive(new Event("receiveNewSubscriptionsFrom("+fromMember+")") {
  306. public void process(Object myself) {
  307. // maps topics to sets of objects that are published under this topic
  308. MultiMap matchingTopics = new MultiMap();
  309. // query local discoverymanager for matching topics
  310. for (Iterator iter = subscriptionTopics.iterator(); iter.hasNext();) {
  311. try {
  312. Packet serializedTopic = (Packet) iter.next();
  313. ATTypeTag topic = serializedTopic.unpack().asTypeTag();
  314. Set matchingServices = discoveryManager_.getLocalPublishedServicesMatching(topic);
  315. Logging.VirtualMachine_LOG.debug("getLocalPubServMatching("+topic+") ="+matchingServices+" ("+matchingServices.size()+" items)");
  316. if (!matchingServices.isEmpty()) {
  317. matchingTopics.putValues(serializedTopic, matchingServices);
  318. }
  319. } catch (InterpreterException e) {
  320. Logging.VirtualMachine_LOG.error("error while unserializing remote subscription topic",e);
  321. }
  322. }
  323. if (!matchingTopics.isEmpty()) {
  324. // send all matching topics back to the requestor
  325. new CMDJoinServices(matchingTopics).send(host_.communicationBus_, fromMember);
  326. }
  327. }
  328. });
  329. }
  330. public Publication[] sync_event_listPublications(final ELActor actor) throws InterpreterException {
  331. try {
  332. return (Publication[]) this.receiveAndWait("currentPublications("+actor+")", new Callable() {
  333. public Object call(Object argument) {
  334. return discoveryManager_.listPublications(actor);
  335. }
  336. });
  337. } catch (Exception e) {
  338. throw (InterpreterException) e;
  339. }
  340. }
  341. public Subscription[] sync_event_listSubscriptions(final ELActor actor) throws InterpreterException {
  342. try {
  343. return (Subscription[]) this.receiveAndWait("currentPublications("+actor+")", new Callable() {
  344. public Object call(Object argument) {
  345. return discoveryManager_.listSubscriptions(actor);
  346. }
  347. });
  348. } catch (Exception e) {
  349. throw (InterpreterException) e;
  350. }
  351. }
  352. }