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