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