/// <summary> /// Replace an existing set. /// </summary> /// <param name="location">Set resource location uri postfix.</param> /// <param name="setDoc">New set document.</param> /// <returns>Updated set.</returns> public PubSubSubscriptionSet ReplaceSet(string location, XDoc setDoc) { lock (_subscriptionsByOwner) { PubSubSubscriptionSet oldSet; if (!_subscriptionByLocation.TryGetValue(location, out oldSet)) { return(null); } PubSubSubscriptionSet set = oldSet.Derive(setDoc); if (set == oldSet) { return(oldSet); } foreach (DreamCookie cookie in set.Cookies) { _cookieJar.Update(cookie, null); } if (set.Owner != oldSet.Owner) { _log.WarnFormat("subscription set owner mispatch: {0} vs. {1}", oldSet.Owner, set.Owner); throw new ArgumentException("owner of new set does not match existing owner"); } _subscriptionByLocation[location] = set; _subscriptionsByOwner[set.Owner] = set; Update(); return(set); } }
public IPubSubDispatchQueue this[PubSubSubscriptionSet set] { get { PersistentPubSubDispatchQueue queue; return _repository.TryGetValue(set.Location, out queue) ? queue : null; } }
//--- Constructors --- /// <summary> /// Create a new dispatcher. /// </summary> /// <param name="config">Configuration instance injected from pub sub service.</param> public Dispatcher(DispatcherConfig config) { _owner = config.ServiceUri.AsServerUri(); _serviceKeySetCookie = config.ServiceAccessCookie; _combinedSet = new PubSubSubscriptionSet(_owner, 0, _serviceKeySetCookie); _dispatchQueue = new ProcessingQueue <DispatcherEvent>(DispatchFromQueue, 10); }
public IPubSubDispatchQueue this[PubSubSubscriptionSet set] { get { lock(_repository) { MemoryPubSubDispatchQueue queue; return _repository.TryGetValue(set.Location, out queue) ? queue : null; } } }
public void Delete(PubSubSubscriptionSet set) { lock(_repository) { MemoryPubSubDispatchQueue queue; if(!_repository.TryGetValue(set.Location, out queue)) { return; } _repository.Remove(set.Location); queue.Dispose(); } }
//--- Constructors --- private PubSubSubscription(XUri channel, XUri resource, PubSubSubscriptionSet owner, DreamCookie cookie, PubSubSubscription first, PubSubSubscription second) { if (channel == null) { throw new ArgumentNullException("channel"); } Channels = new[] { channel }; Resources = resource == null ? new XUri[0] : new[] { resource }; Id = Guid.NewGuid().ToString(); Owner = owner; Destination = Owner.Owner.At("publish"); Cookie = cookie; Recipients = ArrayUtil.Union(first.Recipients, (second == null) ? new DispatcherRecipient[0] : second.Recipients); _isProxy = true; }
private void Update_Helper() { lock (_subscriptionsByOwner) { PubSubSubscription[] allSubs = CalculateCombinedSubscriptions(); _combinedSetVersion++; _combinedSet = new PubSubSubscriptionSet(_owner, _combinedSetVersion, _serviceKeySetCookie, allSubs); } // this is outside the lock, since it may be an expensive operation and shouldn't block lookups DispatcherEvent ev = new DispatcherEvent(_combinedSet.AsDocument(), new XUri("pubsub:///set/update"), null, _owner); _log.DebugFormat("updated combined set, dispatching as id '{0}'", ev.Id); Dispatch(ev); if (CombinedSetUpdated != null) { CombinedSetUpdated(this, EventArgs.Empty); } }
/// <summary> /// Register a subscription set /// </summary> /// <param name="setDoc">Xml formatted subscription set.</param> /// <returns>Tuple of subscription set and <see langword="True"/> if the set was newly created, or <see langword="False"/> if the set existed (does not update the set).</returns> public Tuplet <PubSubSubscriptionSet, bool> RegisterSet(XDoc setDoc) { PubSubSubscriptionSet set = new PubSubSubscriptionSet(setDoc); foreach (DreamCookie cookie in set.Cookies) { _cookieJar.Update(cookie, null); } lock (_subscriptionsByOwner) { PubSubSubscriptionSet existing; if (_subscriptionsByOwner.TryGetValue(set.Owner, out existing)) { return(new Tuplet <PubSubSubscriptionSet, bool>(existing, true)); } _subscriptionByLocation.Add(set.Location, set); _subscriptionsByOwner.Add(set.Owner, set); Update(); return(new Tuplet <PubSubSubscriptionSet, bool>(set, false)); } }
//--- Constructors --- public PersistentPubSubDispatchQueueRepository(string queueRootPath, TaskTimerFactory taskTimerFactory, TimeSpan retryTime) { _queueRootPath = queueRootPath; _taskTimerFactory = taskTimerFactory; _retryTime = retryTime; if(!Directory.Exists(_queueRootPath)) { Directory.CreateDirectory(_queueRootPath); } foreach(var setFile in Directory.GetFiles(_queueRootPath, "*.xml")) { PubSubSubscriptionSet set; try { var setDoc = XDocFactory.LoadFrom(setFile, MimeType.TEXT_XML); var location = setDoc["@location"].AsText; var accessKey = setDoc["@accesskey"].AsText; set = new PubSubSubscriptionSet(setDoc, location, accessKey); } catch(Exception e) { _log.Warn(string.Format("unable to retrieve and re-register subscription for location", Path.GetFileNameWithoutExtension(setFile)), e); continue; } _uninitializedSets.Add(set); } }
/// <summary> /// Create a subscription from a subscription document. /// </summary> /// <param name="sub">Subscription document.</param> /// <param name="owner">Owning set.</param> public PubSubSubscription(XDoc sub, PubSubSubscriptionSet owner) { Owner = owner; // sanity check the input XDoc channels = sub["channel"]; if(channels.IsEmpty) { throw new ArgumentException("<subscription> must have at least one <channel>"); } XDoc filter = sub["filter"]; if(filter.ListLength > 1) { throw new ArgumentException("<subscription> must have zero or one <filter>"); } XDoc proxy = sub["uri.proxy"]; if(proxy.ListLength > 1) { throw new ArgumentException("<subscription> must have zero or one <uri.proxy>"); } XDoc recipients = sub["recipient"]; if(recipients.IsEmpty) { throw new ArgumentException("<subscription> must have at least one valid <recipient>"); } if(recipients.ListLength > 1 && proxy.ListLength == 0) { throw new ArgumentException("<subscription> must include <uri.proxy> if there is more than one <recipient>"); } // create our internal representation try { Id = sub["@id"].Contents; if(string.IsNullOrEmpty(Id)) { Id = Guid.NewGuid().ToString(); } XDoc cookie = sub["set-cookie"]; if(!cookie.IsEmpty) { Cookie = DreamCookie.ParseSetCookie(cookie); } List<XUri> channelList = new List<XUri>(); foreach(XDoc c in channels) { channelList.Add(c.AsUri); } Channels = channelList.ToArray(); List<XUri> resourceList = new List<XUri>(); foreach(XDoc r in sub["uri.resource"]) { resourceList.Add(r.AsUri); } Resources = resourceList.ToArray(); if(proxy.IsEmpty) { Destination = new DispatcherRecipient(recipients).Uri; } else { Destination = proxy.AsUri; _isProxy = true; } List<DispatcherRecipient> recipientList = new List<DispatcherRecipient>(); foreach(XDoc recipient in recipients) { recipientList.Add(new DispatcherRecipient(recipient)); } Recipients = recipientList.ToArray(); } catch(Exception e) { throw new ArgumentException("Unable to parse subscription: " + e.Message, e); } }
//--- Constructors --- private PubSubSubscription(XUri channel, XUri resource, PubSubSubscriptionSet owner, DreamCookie cookie, PubSubSubscription first, PubSubSubscription second) { if(channel == null) { throw new ArgumentNullException("channel"); } Channels = new[] { channel }; Resources = resource == null ? new XUri[0] : new[] { resource }; Id = Guid.NewGuid().ToString(); Owner = owner; Destination = Owner.Owner.At("publish"); Cookie = cookie; Recipients = ArrayUtil.Union(first.Recipients, (second == null) ? new DispatcherRecipient[0] : second.Recipients); _isProxy = true; }
//--- Class Methods --- /// <summary> /// Merge two subscriptions on matchine channel and resource. /// </summary> /// <param name="channel">Common channel.</param> /// <param name="resource">Common resource.</param> /// <param name="owner">Common owner.</param> /// <param name="cookie">Subscription set cookie.</param> /// <param name="first">First subscription to merge.</param> /// <param name="second">Second subscription to merge.</param> /// <returns></returns> public static PubSubSubscription MergeForChannelAndResource(XUri channel, XUri resource, PubSubSubscriptionSet owner, DreamCookie cookie, PubSubSubscription first, PubSubSubscription second) { return new PubSubSubscription(channel, resource, owner, cookie, first, second); }
public void SubscriptionSet_derive_with_same_version_returns_existing_Set() { XDoc setDoc1 = new XDoc("subscription-set") .Attr("max-failures", 1) .Attr("version", 10) .Elem("uri.owner", "http://owner") .Start("subscription") .Attr("id", "456") .Elem("channel", "http://chanel") .Start("recipient").Attr("auth-token", "xyz").Elem("uri", "http://recipient").End() .End(); PubSubSubscriptionSet set1 = new PubSubSubscriptionSet(setDoc1); Assert.AreEqual(10, set1.Version); XDoc setDoc2 = new XDoc("subscription-set") .Attr("max-failures", 1) .Attr("version", 15) .Elem("uri.owner", "http://owner") .Start("subscription") .Attr("id", "456") .Elem("channel", "http://chanel") .Start("recipient").Attr("auth-token", "xyz").Elem("uri", "http://recipient").End() .End(); PubSubSubscriptionSet set2 = set1.Derive(setDoc2); Assert.AreEqual(15, set2.Version); Assert.AreNotSame(set1, set2); XDoc setDoc3 = new XDoc("subscription-set") .Attr("max-failures", 1) .Attr("version", 15) .Elem("uri.owner", "http://owner") .Start("subscription") .Attr("id", "456") .Elem("channel", "http://chanel") .Start("recipient").Attr("auth-token", "xyz").Elem("uri", "http://recipient").End() .End(); PubSubSubscriptionSet set3 = set2.Derive(setDoc3); Assert.AreEqual(15, set3.Version); Assert.AreSame(set2, set3); }
public void SubscriptionSet_from_XDoc_and_back() { XUri owner = new XUri("http:///owner"); XUri sub1chan1 = new XUri("channel:///foo/bar/*"); XUri sub1chan2 = new XUri("channel:///foo/baz/*"); XUri sub1resource = new XUri("http://resource/1/1"); XUri sub1proxy = new XUri("http:///proxy"); XUri sub1recep1 = new XUri("http:///recep1"); XUri sub1recep2 = new XUri("http:///recep2"); XUri sub2chan1 = new XUri("channel:///foo/bar/baz"); XUri sub2resource1 = new XUri("http://resource/2/1"); XUri sub2resource2 = new XUri("http://resource/2/2"); XUri sub2recep1 = new XUri("http:///recep1"); DreamCookie cookie = DreamCookie.NewSetCookie("foo", "bar", new XUri("http://xyz/abc/")); XDoc setDoc = new XDoc("subscription-set") .Attr("max-failures", 1) .Elem("uri.owner", owner) .Start("subscription") .Attr("id", "123") .Elem("channel", sub1chan1) .Elem("channel", sub1chan2) .Elem("uri.resource", sub1resource) .Add(cookie.AsSetCookieDocument) .Elem("uri.proxy", sub1proxy) .Start("recipient").Attr("auth-token", "abc").Elem("uri", sub1recep1).End() .Start("recipient").Attr("auth-token", "def").Elem("uri", sub1recep2).End() .End() .Start("subscription") .Attr("id", "456") .Elem("channel", sub2chan1) .Elem("uri.resource", sub2resource1) .Elem("uri.resource", sub2resource2) .Start("recipient").Attr("auth-token", "xyz").Elem("uri", sub2recep1).End() .End(); PubSubSubscriptionSet set = new PubSubSubscriptionSet(setDoc); Assert.AreEqual(1, set.MaxFailures); Assert.AreEqual(owner, set.Owner); Assert.IsFalse(string.IsNullOrEmpty(set.Location)); Assert.AreEqual(2, set.Subscriptions.Length); PubSubSubscription sub1 = set.Subscriptions[0]; Assert.IsFalse(string.IsNullOrEmpty(sub1.Id)); Assert.AreEqual(cookie, sub1.Cookie); Assert.AreEqual(sub1chan1, sub1.Channels[0]); Assert.AreEqual(sub1chan2, sub1.Channels[1]); Assert.AreEqual(sub1proxy, sub1.Destination); PubSubSubscription sub2 = set.Subscriptions[1]; Assert.IsFalse(string.IsNullOrEmpty(sub2.Id)); Assert.AreEqual(1, sub2.Channels.Length); Assert.AreEqual(sub2chan1, sub2.Channels[0]); Assert.AreEqual(sub2recep1, sub2.Destination); XDoc setDoc2 = set.AsDocument(); Assert.AreEqual(setDoc, setDoc2); }
//--- Constructors --- /// <summary> /// Create a new dispatcher. /// </summary> /// <param name="config">Configuration instance injected from pub sub service.</param> /// <param name="queueRepository">Factory for dispatch queues used by persisted (i.e. expiring) subscriptions</param> public Dispatcher(DispatcherConfig config, IPubSubDispatchQueueRepository queueRepository) { _queueRepository = queueRepository; _owner = config.ServiceUri.AsServerUri(); _serviceKeySetCookie = config.ServiceAccessCookie; _combinedSet = new PubSubSubscriptionSet(_owner, 0, _serviceKeySetCookie); _dispatchQueue = new ProcessingQueue<DispatcherEvent>(DispatchFromQueue, 10); _defaultQueue = new ImmediatePubSubDispatchQueue(TryDispatchItem); var pubSubSubscriptionSets = queueRepository.GetUninitializedSets(); // Note (arnec): only invoking lock here, so that RegisterSet and Update don't do it over and over lock(_subscriptionsByOwner) { foreach(var set in pubSubSubscriptionSets) { RegisterSet(set, true); } Update(); } queueRepository.InitializeRepository(TryDispatchItem); }
public void SubscriptionSet_derive_with_newer_version_always_creates_new_set() { var setDoc1 = new XDoc("subscription-set") .Attr("max-failures", 1) .Attr("version", 10) .Elem("uri.owner", "http://owner") .Start("subscription") .Attr("id", "456") .Elem("channel", "http://chanel") .Start("recipient").Attr("auth-token", "xyz").Elem("uri", "http://recipient").End() .End(); var set1 = new PubSubSubscriptionSet(setDoc1, "abc", "def"); var setDoc2 = new XDoc("subscription-set") .Attr("max-failures", 1) .Attr("version", 11) .Elem("uri.owner", "http://owner") .Start("subscription") .Attr("id", "456") .Elem("channel", "http://chanel") .Start("recipient").Attr("auth-token", "xyz").Elem("uri", "http://recipient").End() .End(); var set2 = set1.Derive(setDoc2, null); Assert.AreEqual(10, set1.Version); Assert.AreEqual(11, set2.Version); Assert.AreNotSame(set1, set2); Assert.AreEqual(set1.AccessKey, set2.AccessKey); }
public void RegisterOrUpdate(PubSubSubscriptionSet set) { lock(_repository) { var subscriptionDocument = set.AsDocument(); subscriptionDocument.Attr("location", set.Location).Attr("accesskey", set.AccessKey); subscriptionDocument.Save(Path.Combine(_queueRootPath, set.Location + ".xml")); if(_repository.ContainsKey(set.Location)) { return; } var queue = new PersistentPubSubDispatchQueue(Path.Combine(_queueRootPath, XUri.EncodeSegment(set.Location)), _taskTimerFactory, _retryTime, _handler); _repository[set.Location] = queue; } }
public void RegisterOrUpdate(PubSubSubscriptionSet set) { lock(_repository) { if(_repository.ContainsKey(set.Location)) { return; } var queue = new MemoryPubSubDispatchQueue(set.Location, _taskTimerFactory, _retryTime, _handler); _repository[set.Location] = queue; } }
//--- Constructors --- /// <summary> /// Create a new dispatcher. /// </summary> /// <param name="config">Configuration instance injected from pub sub service.</param> public Dispatcher(DispatcherConfig config) { _owner = config.ServiceUri.AsServerUri(); _serviceKeySetCookie = config.ServiceAccessCookie; _combinedSet = new PubSubSubscriptionSet(_owner, 0, _serviceKeySetCookie); _dispatchQueue = new ProcessingQueue<DispatcherEvent>(DispatchFromQueue, 10); }
private void Update_Helper() { lock(_subscriptionsByOwner) { PubSubSubscription[] allSubs = CalculateCombinedSubscriptions(); _combinedSetVersion++; _combinedSet = new PubSubSubscriptionSet(_owner, _combinedSetVersion, _serviceKeySetCookie, allSubs); } // this is outside the lock, since it may be an expensive operation and shouldn't block lookups DispatcherEvent ev = new DispatcherEvent(_combinedSet.AsDocument(), new XUri("pubsub:///set/update"), null, _owner); _log.DebugFormat("updated combined set, dispatching as id '{0}'", ev.Id); Dispatch(ev); if(CombinedSetUpdated != null) { CombinedSetUpdated(this, EventArgs.Empty); } }
public void SubscriptionSet_with_ttl_has_no_max_failures() { var setDoc = new XDoc("subscription-set") .Attr("max-failures", 42) .Attr("max-failure-duration", 1) .Elem("uri.owner", "http://owner") .Start("subscription") .Attr("id", "123") .Elem("channel", "http://channel") .Elem("uri.resource", "http://resource") .Elem("uri.proxy", "http://proxy") .Start("recipient").Elem("uri", "http:///recipient").End() .End(); var set = new PubSubSubscriptionSet(setDoc, "abc", "def"); Assert.AreEqual(int.MaxValue, set.MaxFailures); }
public void SubscriptionSet_with_ttl_expires() { var setDoc = new XDoc("subscription-set") .Attr("max-failure-duration", 1) .Elem("uri.owner", "http://owner") .Start("subscription") .Attr("id", "123") .Elem("channel", "http://channel") .Elem("uri.resource", "http://resource") .Elem("uri.proxy", "http://proxy") .Start("recipient").Elem("uri", "http:///recipient").End() .End(); var set = new PubSubSubscriptionSet(setDoc, "abc", "def"); Assert.IsTrue(set.UsesFailureDuration, "set should have had an expiration"); Assert.AreEqual(set.MaxFailureDuration, 1.Seconds()); }
public void SubscriptionSet_without_ttl_never_expires() { XDoc setDoc = new XDoc("subscription-set") .Attr("max-failures", 1) .Elem("uri.owner", "http://owner") .Start("subscription") .Attr("id", "123") .Elem("channel", "http://channel") .Elem("uri.resource", "http://resource") .Elem("uri.proxy", "http://proxy") .Start("recipient").Elem("uri", "http:///recipient").End() .End(); var set = new PubSubSubscriptionSet(setDoc, "abc", "def"); Assert.IsFalse(set.UsesFailureDuration, "set should not have an expiration"); }
private Tuplet<PubSubSubscriptionSet, bool> RegisterSet(PubSubSubscriptionSet set, bool init) { foreach(var cookie in set.Cookies) { _cookieJar.Update(cookie, null); } lock(_subscriptionsByOwner) { PubSubSubscriptionSet existing; if(_subscriptionsByOwner.TryGetValue(set.Owner, out existing) || _subscriptionByLocation.TryGetValue(set.Location, out existing)) { return new Tuplet<PubSubSubscriptionSet, bool>(existing, true); } _subscriptionByLocation.Add(set.Location, set); _subscriptionsByOwner.Add(set.Owner, set); if(set.UsesFailureDuration && !init) { _queueRepository.RegisterOrUpdate(set); } return new Tuplet<PubSubSubscriptionSet, bool>(set, false); } }
//--- Class Methods --- /// <summary> /// Merge two subscriptions on matchine channel and resource. /// </summary> /// <param name="channel">Common channel.</param> /// <param name="resource">Common resource.</param> /// <param name="owner">Common owner.</param> /// <param name="cookie">Subscription set cookie.</param> /// <param name="first">First subscription to merge.</param> /// <param name="second">Second subscription to merge.</param> /// <returns></returns> public static PubSubSubscription MergeForChannelAndResource(XUri channel, XUri resource, PubSubSubscriptionSet owner, DreamCookie cookie, PubSubSubscription first, PubSubSubscription second) { return(new PubSubSubscription(channel, resource, owner, cookie, first, second)); }
/// <summary> /// Register a subscription set /// </summary> /// <param name="location">location id.</param> /// <param name="setDoc">Xml formatted subscription set.</param> /// <param name="accessKey">secret key for accessing the set.</param> /// <returns>Tuple of subscription set and <see langword="True"/> if the set was newly created, or <see langword="False"/> if the set existed (does not update the set).</returns> public Tuplet<PubSubSubscriptionSet, bool> RegisterSet(string location, XDoc setDoc, string accessKey) { var set = new PubSubSubscriptionSet(setDoc, location, accessKey); var result = RegisterSet(set, false); Update(); return result; }
public void SubscriptionSet_derive_with_no_version_always_creates_new_set() { XDoc setDoc1 = new XDoc("subscription-set") .Attr("max-failures", 1) .Attr("version", 10) .Elem("uri.owner", "http://owner") .Start("subscription") .Attr("id", "456") .Elem("channel", "http://chanel") .Start("recipient").Attr("auth-token", "xyz").Elem("uri", "http://recipient").End() .End(); PubSubSubscriptionSet set1 = new PubSubSubscriptionSet(setDoc1); Assert.AreEqual(10, set1.Version); XDoc setDoc2 = new XDoc("subscription-set") .Attr("max-failures", 1) .Elem("uri.owner", "http://owner") .Start("subscription") .Attr("id", "456") .Elem("channel", "http://chanel") .Start("recipient").Attr("auth-token", "xyz").Elem("uri", "http://recipient").End() .End(); PubSubSubscriptionSet set2 = set1.Derive(setDoc2); Assert.IsFalse(set2.Version.HasValue); Assert.AreNotSame(set1, set2); }
public void SubscriptionSet_combination_merges_subs_for_same_channel() { XUri owner = new XUri("http:///owner"); XUri c1 = new XUri("channel:///c1"); XUri c2 = new XUri("channel:///c2"); XUri c3 = new XUri("channel:///c3"); XDoc x1 = new XDoc("rule").Value("v1"); XDoc x2 = new XDoc("rule").Value("v2"); XDoc x3 = new XDoc("super-custom-filter").Elem("foo", "bar"); XUri r1 = new XUri("http:///r1"); XUri r2 = new XUri("http:///r2"); PubSubSubscription sub1 = new PubSubSubscription( new XDoc("subscription") .Attr("id", "123") .Elem("channel", c1) .Elem("channel", c2) .Elem("uri.proxy", "http:///proxy") .Start("recipient").Attr("auth-token", "abc").Elem("uri", r1).End() , null ); PubSubSubscription sub2 = new PubSubSubscription( new XDoc("subscription") .Attr("id", "123") .Elem("channel", c1) .Elem("channel", c3) .Elem("uri.proxy", "http:///proxy") .Start("recipient").Attr("auth-token", "abc").Elem("uri", r2).End() , null ); DreamCookie cookie = DreamCookie.NewSetCookie("foo", "bar", new XUri("http://xyz/abc/")); PubSubSubscriptionSet combinedSet = new PubSubSubscriptionSet(owner, 0, cookie, sub1, sub2); Assert.AreEqual(3, combinedSet.Subscriptions.Length); foreach(PubSubSubscription subx in combinedSet.Subscriptions) { switch(subx.Channels[0].ToString()) { case "channel:///c1": Assert.AreEqual(owner.At("publish"), subx.Destination); Assert.AreEqual(1, subx.Channels.Length); Assert.AreEqual(c1, subx.Channels[0]); Assert.AreEqual(2, subx.Recipients.Length); bool foundR1 = false; bool foundR2 = false; foreach(DispatcherRecipient r in subx.Recipients) { if(r.Uri == r1) { foundR1 = true; } else if(r.Uri == r2) { foundR2 = true; } } Assert.IsTrue(foundR1 && foundR2); break; case "channel:///c2": Assert.AreEqual(owner.At("publish"), subx.Destination); Assert.AreEqual(1, subx.Channels.Length); Assert.AreEqual(c2, subx.Channels[0]); Assert.AreEqual(1, subx.Recipients.Length); Assert.AreEqual(r1, subx.Recipients[0].Uri); break; case "channel:///c3": Assert.AreEqual(owner.At("publish"), subx.Destination); Assert.AreEqual(1, subx.Channels.Length); Assert.AreEqual(c3, subx.Channels[0]); Assert.AreEqual(1, subx.Recipients.Length); Assert.AreEqual(r2, subx.Recipients[0].Uri); break; default: Assert.Fail(); break; } } }
public void SubscriptionSet_derive_with_access_key_changes_accesskey() { var setDoc1 = new XDoc("subscription-set") .Attr("max-failures", 1) .Elem("uri.owner", "http://owner") .Start("subscription") .Attr("id", "456") .Elem("channel", "http://chanel") .Start("recipient").Attr("auth-token", "xyz").Elem("uri", "http://recipient").End() .End(); var set1 = new PubSubSubscriptionSet(setDoc1, "abc", "def"); var set2 = set1.Derive(setDoc1, "bob"); Assert.AreNotSame(set1, set2); Assert.AreNotEqual(set1.AccessKey, set2.AccessKey); }
/// <summary> /// Register a subscription set /// </summary> /// <param name="setDoc">Xml formatted subscription set.</param> /// <returns>Tuple of subscription set and <see langword="True"/> if the set was newly created, or <see langword="False"/> if the set existed (does not update the set).</returns> public Tuplet<PubSubSubscriptionSet, bool> RegisterSet(XDoc setDoc) { PubSubSubscriptionSet set = new PubSubSubscriptionSet(setDoc); foreach(DreamCookie cookie in set.Cookies) { _cookieJar.Update(cookie, null); } lock(_subscriptionsByOwner) { PubSubSubscriptionSet existing; if(_subscriptionsByOwner.TryGetValue(set.Owner, out existing)) { return new Tuplet<PubSubSubscriptionSet, bool>(existing, true); } _subscriptionByLocation.Add(set.Location, set); _subscriptionsByOwner.Add(set.Owner, set); Update(); return new Tuplet<PubSubSubscriptionSet, bool>(set, false); } }
public void SubscriptionSet_combination_splits_multichannel_subs() { XUri owner = new XUri("http:///owner"); XUri c1 = new XUri("channel:///c1"); XUri c2 = new XUri("channel:///c2"); XUri c3 = new XUri("channel:///c3"); XUri r1 = new XUri("http:///r1"); PubSubSubscription sub = new PubSubSubscription( new XDoc("subscription") .Attr("id", "123") .Elem("channel", c1) .Elem("channel", c2) .Elem("channel", c3) .Elem("uri.proxy", "http:///proxy") .Start("recipient").Attr("auth-token", "abc").Elem("uri", r1).End() .Start("recipient").Attr("auth-token", "def").Elem("uri", "http:///r2").End() , null ); DreamCookie cookie = DreamCookie.NewSetCookie("foo", "bar", new XUri("http://xyz/abc/")); PubSubSubscriptionSet combinedSet = new PubSubSubscriptionSet(owner, 0, cookie, sub); Assert.AreEqual(3, combinedSet.Subscriptions.Length); PubSubSubscription subx = combinedSet.Subscriptions[0]; Assert.AreEqual(owner.At("publish"), subx.Destination); Assert.AreEqual(1, subx.Channels.Length); Assert.AreEqual(c1, subx.Channels[0]); Assert.AreEqual(2, subx.Recipients.Length); Assert.AreEqual(r1, subx.Recipients[0].Uri); subx = combinedSet.Subscriptions[1]; Assert.AreEqual(owner.At("publish"), subx.Destination); Assert.AreEqual(1, subx.Channels.Length); Assert.AreEqual(c2, subx.Channels[0]); Assert.AreEqual(2, subx.Recipients.Length); Assert.AreEqual(r1, subx.Recipients[0].Uri); }
public void CombinedSet_of_service_combines_all_registered_subs() { XUri c1 = new XUri("channel:///c1"); XUri c2 = new XUri("channel:///c2"); XUri c3 = new XUri("channel:///c3"); XUri r1 = new XUri("http:///r1"); XUri r2 = new XUri("http:///r2"); XDoc set1 = new XDoc("subscription-set") .Attr("max-failures", 1) .Elem("uri.owner", "http:///owner1") .Start("subscription") .Attr("id", "123") .Elem("channel", c1) .Elem("channel", c2) .Elem("uri.proxy", "http:///proxy") .Start("recipient").Attr("auth-token", "abc").Elem("uri", r1).End() .EndAll(); XDoc set2 = new XDoc("subscription-set") .Attr("max-failures", 1) .Elem("uri.owner", "http:///owner2") .Start("subscription") .Attr("id", "123") .Elem("channel", c1) .Elem("channel", c3) .Elem("uri.proxy", "http:///proxy") .Start("recipient").Attr("auth-token", "abc").Elem("uri", r2).End() .EndAll(); Plug pubsub = CreatePubSubService().WithInternalKey().AtLocalHost; DreamMessage response = pubsub.At("subscribers").PostAsync(set1).Wait(); Assert.IsTrue(response.IsSuccessful); Assert.AreEqual(DreamStatus.Created, response.Status); response = pubsub.At("subscribers").PostAsync(set2).Wait(); Assert.IsTrue(response.IsSuccessful); Assert.AreEqual(DreamStatus.Created, response.Status); Thread.Sleep(1000); response = pubsub.At("subscribers").GetAsync().Wait(); Assert.IsTrue(response.IsSuccessful); Assert.AreEqual(DreamStatus.Ok, response.Status); PubSubSubscriptionSet combinedSet = new PubSubSubscriptionSet(response.ToDocument(), "abc", "def"); Assert.AreEqual(3, combinedSet.Subscriptions.Length); XUri owner = pubsub.Uri.WithoutQuery(); foreach(PubSubSubscription subx in combinedSet.Subscriptions) { switch(subx.Channels[0].ToString()) { case "channel:///c1": Assert.AreEqual(owner.At("publish"), subx.Destination); Assert.AreEqual(1, subx.Channels.Length); Assert.AreEqual(c1, subx.Channels[0]); Assert.AreEqual(2, subx.Recipients.Length); bool foundR1 = false; bool foundR2 = false; foreach(DispatcherRecipient r in subx.Recipients) { if(r.Uri == r1) { foundR1 = true; } else if(r.Uri == r2) { foundR2 = true; } } Assert.IsTrue(foundR1 && foundR2); break; case "channel:///c2": Assert.AreEqual(owner.At("publish"), subx.Destination); Assert.AreEqual(1, subx.Channels.Length); Assert.AreEqual(c2, subx.Channels[0]); Assert.AreEqual(1, subx.Recipients.Length); Assert.AreEqual(r1, subx.Recipients[0].Uri); break; case "channel:///c3": Assert.AreEqual(owner.At("publish"), subx.Destination); Assert.AreEqual(1, subx.Channels.Length); Assert.AreEqual(c3, subx.Channels[0]); Assert.AreEqual(1, subx.Recipients.Length); Assert.AreEqual(r2, subx.Recipients[0].Uri); break; default: Assert.Fail(); break; } } }
public void SubscriptionSet_combination_uses_provided_cookie_for_all() { DreamCookie cookie1 = DreamCookie.NewSetCookie("foo", "bar", new XUri("http://xyz/abc/"), DateTime.MaxValue); DreamCookie cookie2 = DreamCookie.NewSetCookie("foop", "baz", new XUri("http://xyz/abc/"), DateTime.MaxValue); DreamCookie cookie3 = DreamCookie.NewSetCookie("foox", "barxx", new XUri("http://xyz/abc/"), DateTime.MaxValue); DreamCookie cookie4 = DreamCookie.NewSetCookie("foopx", "bazxx", new XUri("http://xyz/abc/"), DateTime.MaxValue); XDoc setDoc1 = new XDoc("subscription-set") .Elem("uri.owner", "http:///owner1") .Start("subscription") .Elem("channel", "channel:///foo1") .Add(cookie1.AsSetCookieDocument) .Start("recipient").Elem("uri", "http:///recipient1").End() .End() .Start("subscription") .Elem("channel", "channel:///foo2") .Add(cookie2.AsSetCookieDocument) .Start("recipient").Elem("uri", "http:///recipient2").End() .End(); PubSubSubscriptionSet set1 = new PubSubSubscriptionSet(setDoc1); XDoc setDoc2 = new XDoc("subscription-set") .Elem("uri.owner", "http:///owner1") .Start("subscription") .Elem("channel", "channel:///foo3") .Add(cookie3.AsSetCookieDocument) .Start("recipient").Elem("uri", "http:///recipient3").End() .End() .Start("subscription") .Elem("channel", "channel:///foo4") .Add(cookie4.AsSetCookieDocument) .Start("recipient").Elem("uri", "http:///recipient4").End() .End(); PubSubSubscriptionSet set2 = new PubSubSubscriptionSet(setDoc2); DreamCookie cookie = DreamCookie.NewSetCookie("foo", "bar", new XUri("http://xyz/abc/")); PubSubSubscriptionSet combinedSet = new PubSubSubscriptionSet( new XUri("http:///combined"), 0, cookie, set1.Subscriptions[0], set1.Subscriptions[1], set2.Subscriptions[0], set2.Subscriptions[1]); Assert.AreEqual(4, combinedSet.Subscriptions.Length); Assert.AreEqual(1, combinedSet.Cookies.Count); Assert.AreEqual(cookie, combinedSet.Cookies[0]); }
public void SubscriptionSet_combined_set_should_not_include_pubsub_channel_subscriptions() { DreamCookie cookie = DreamCookie.NewSetCookie("foo", "bar", new XUri("http://xyz/abc/")); PubSubSubscription sub1 = new PubSubSubscription( new XDoc("subscription") .Elem("channel", "pubsub:///foo1") .Add(cookie.AsSetCookieDocument) .Start("recipient").Elem("uri", "http:///recipient1").End(), null); PubSubSubscriptionSet pubsubset = new PubSubSubscriptionSet(new XUri("http:///owner"), 0, cookie, sub1); Assert.AreEqual(0, pubsubset.Subscriptions.Length); PubSubSubscription sub2 = new PubSubSubscription( new XDoc("subscription") .Elem("channel", "channel:///foo1") .Add(cookie.AsSetCookieDocument) .Start("recipient").Elem("uri", "http:///recipient1").End(), null); pubsubset = new PubSubSubscriptionSet(new XUri("http:///owner"), 0, cookie, sub1, sub2); Assert.AreEqual(1, pubsubset.Subscriptions.Length); Assert.AreEqual(1, pubsubset.Subscriptions[0].Channels.Length); Assert.AreEqual("channel:///foo1", pubsubset.Subscriptions[0].Channels[0].ToString()); }
public void SubscriptionSet_Cookies_collapse_to_unique_set() { DreamCookie cookie1 = DreamCookie.NewSetCookie("foo", "bar", new XUri("http://xyz/abc/"), DateTime.MaxValue); DreamCookie cookie2 = DreamCookie.NewSetCookie("foop", "baz", new XUri("http://xyz/abc/"), DateTime.MaxValue); XDoc setDoc = new XDoc("subscription-set") .Elem("uri.owner", "http:///owner") .Start("subscription") .Elem("channel", "channel:///foo") .Add(cookie1.AsSetCookieDocument) .Start("recipient").Elem("uri", "http:///recipient").End() .End() .Start("subscription") .Elem("channel", "channel:///foo") .Add(cookie2.AsSetCookieDocument) .Start("recipient").Elem("uri", "http:///recipient").End() .End() .Start("subscription") .Elem("channel", "channel:///foo") .Add(cookie1.AsSetCookieDocument) .Start("recipient").Elem("uri", "http:///recipient").End() .End() .Start("subscription") .Elem("channel", "channel:///foo") .Add(cookie2.AsSetCookieDocument) .Start("recipient").Elem("uri", "http:///recipient").End() .End() .Start("subscription") .Elem("channel", "channel:///foo") .Add(cookie1.AsSetCookieDocument) .Start("recipient").Elem("uri", "http:///recipient").End() .End(); PubSubSubscriptionSet set = new PubSubSubscriptionSet(setDoc); Assert.AreEqual(2, set.Cookies.Count); Assert.IsTrue(set.Cookies.Contains(cookie1)); Assert.IsTrue(set.Cookies.Contains(cookie2)); }
public void Delete(PubSubSubscriptionSet set) { lock(_repository) { PersistentPubSubDispatchQueue queue; if(!_repository.TryGetValue(set.Location, out queue)) { return; } _repository.Remove(set.Location); queue.DeleteAndDispose(); var subscriptionDocPath = Path.Combine(_queueRootPath, set.Location + ".xml"); try { File.Delete(subscriptionDocPath); } catch(Exception e) { _log.Warn(string.Format("unable to delete subscription doc at '{0}'", subscriptionDocPath), e); } } }
/// <summary> /// Create a subscription from a subscription document. /// </summary> /// <param name="sub">Subscription document.</param> /// <param name="owner">Owning set.</param> public PubSubSubscription(XDoc sub, PubSubSubscriptionSet owner) { Owner = owner; // sanity check the input XDoc channels = sub["channel"]; if (channels.IsEmpty) { throw new ArgumentException("<subscription> must have at least one <channel>"); } XDoc filter = sub["filter"]; if (filter.ListLength > 1) { throw new ArgumentException("<subscription> must have zero or one <filter>"); } XDoc proxy = sub["uri.proxy"]; if (proxy.ListLength > 1) { throw new ArgumentException("<subscription> must have zero or one <uri.proxy>"); } XDoc recipients = sub["recipient"]; if (recipients.IsEmpty) { throw new ArgumentException("<subscription> must have at least one valid <recipient>"); } if (recipients.ListLength > 1 && proxy.ListLength == 0) { throw new ArgumentException("<subscription> must include <uri.proxy> if there is more than one <recipient>"); } // create our internal representation try { Id = sub["@id"].Contents; if (string.IsNullOrEmpty(Id)) { Id = Guid.NewGuid().ToString(); } XDoc cookie = sub["set-cookie"]; if (!cookie.IsEmpty) { Cookie = DreamCookie.ParseSetCookie(cookie); } List <XUri> channelList = new List <XUri>(); foreach (XDoc c in channels) { channelList.Add(c.AsUri); } Channels = channelList.ToArray(); List <XUri> resourceList = new List <XUri>(); foreach (XDoc r in sub["uri.resource"]) { resourceList.Add(r.AsUri); } Resources = resourceList.ToArray(); if (proxy.IsEmpty) { Destination = new DispatcherRecipient(recipients).Uri; } else { Destination = proxy.AsUri; _isProxy = true; } List <DispatcherRecipient> recipientList = new List <DispatcherRecipient>(); foreach (XDoc recipient in recipients) { recipientList.Add(new DispatcherRecipient(recipient)); } Recipients = recipientList.ToArray(); } catch (Exception e) { throw new ArgumentException("Unable to parse subscription: " + e.Message, e); } }