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