Representation of a message type that clients can be subscribed to.
        string RemoveFromLookup(Subscriber subscriber, MessageType typeName)
        {
            try
            {
                // note: ReaderWriterLockSlim has a thread affinity and cannot be used with await!
                rwLock.EnterWriteLock();

                Dictionary <MessageType, string> subscriptions;
                if (lookup.TryGetValue(subscriber, out subscriptions))
                {
                    string messageId;
                    if (subscriptions.TryGetValue(typeName, out messageId))
                    {
                        subscriptions.Remove(typeName);
                        if (subscriptions.Count == 0)
                        {
                            lookup.Remove(subscriber);
                        }

                        return(messageId);
                    }
                }
            }
            finally
            {
                rwLock.ExitWriteLock();
            }

            return(null);
        }
        void AddToLookup(Subscriber subscriber, MessageType typeName, string messageId)
        {
            try
            {
                // note: ReaderWriterLockSlim has a thread affinity and cannot be used with await!
                rwLock.EnterWriteLock();

                Dictionary <MessageType, string> dictionary;
                if (!lookup.TryGetValue(subscriber, out dictionary))
                {
                    dictionary = new Dictionary <MessageType, string>();
                }
                else
                {
                    // replace existing subscriber
                    lookup.Remove(subscriber);
                }

                dictionary[typeName] = messageId;
                lookup[subscriber]   = dictionary;
            }
            finally
            {
                rwLock.ExitWriteLock();
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="Subscription"/> class.
        /// </summary>
        /// <param name="messageType">
        /// The message type.
        /// </param>
        /// <param name="clients">
        /// The clients.
        /// </param>
        public Subscription(MessageType messageType, IEnumerable<Address> clients)
        {
            Contract.Requires<ArgumentNullException>(messageType != null, "messageType != null");
            Contract.Requires<ArgumentNullException>(clients != null, "clients != null");

            this.Id = FormatId(messageType);
            this.MessageType = messageType;
            this.Clients = clients.ToList();
        }
        public async Task Subscribe(Subscriber subscriber, MessageType messageType, ContextBag context)
        {
            //When the subscriber is running V6 and UseLegacyMessageDrivenSubscriptionMode is enabled at the subscriber the 'subcriber.Endpoint' value is null
            var endpoint = subscriber.Endpoint ?? subscriber.TransportAddress.Split('@').First();
            var subscriptionClient = new SubscriptionClient { TransportAddress = subscriber.TransportAddress, Endpoint = endpoint };

            var attempts = 0;

            //note: since we have a design that can run into concurrency exceptions we perform a few retries
            // we should redesign this in the future to use a separate doc per subscriber and message type
            do
            {
                try
                {
                    using (var session = OpenAsyncSession())
                    {
                        var subscriptionDocId = Subscription.FormatId(messageType);

                        var subscription = await session.LoadAsync<Subscription>(subscriptionDocId).ConfigureAwait(false);

                        if (subscription == null)
                        {
                            subscription = new Subscription
                            {
                                Id = subscriptionDocId,
                                MessageType = messageType,
                                Subscribers = new List<SubscriptionClient>()
                            };

                            await session.StoreAsync(subscription).ConfigureAwait(false);
                        }

                        if (!subscription.Subscribers.Contains(subscriptionClient))
                        {
                            subscription.Subscribers.Add(subscriptionClient);
                        }
                        else
                        {
                            var savedSubscription = subscription.Subscribers.Single(s => s.Equals(subscriptionClient));
                            if (savedSubscription.Endpoint != subscriber.Endpoint)
                            {
                                savedSubscription.Endpoint = subscriber.Endpoint;
                            }
                        }

                        await session.SaveChangesAsync().ConfigureAwait(false);
                    }

                    return;
                }
                catch (ConcurrencyException)
                {
                    attempts++;
                }
            } while (attempts < 5);
        }
        public static IEnumerable<string> GetPipelineInfoFor(this AuditMessageReceived envelope,MessageType messageType)
        {
            var key = "NServiceBus.PipelineInfo." + messageType.TypeName;
            var result = new List<string>();

            if(!envelope.HasHeader(key))
                return result;

            return envelope.Headers[key].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
        }
        public Task Subscribe(Subscriber subscriber, MessageType messageType, ContextBag context)
        {
            var body      = $"{messageType.TypeName}, Version={messageType.Version}";
            var label     = Serialize(subscriber);
            var messageId = storageQueue.Send(body, label);

            AddToLookup(subscriber, messageType, messageId);

            log.DebugFormat($"Subscriber {subscriber.TransportAddress} added for message {messageType}.");

            return(TaskEx.CompletedTask);
        }
        public static string FormatId(MessageType messageType)
        {
            // use MD5 hash to get a 16-byte hash of the string
            using (var provider = new MD5CryptoServiceProvider())
            {
                var inputBytes = Encoding.Default.GetBytes(messageType.TypeName + "/" + messageType.Version.Major);
                var hashBytes = provider.ComputeHash(inputBytes);
                // generate a guid from the hash:
                var id = new Guid(hashBytes);

                return $"Subscriptions/{id}";
            }
        }
        public Task Unsubscribe(Subscriber subscriber, MessageType messageType, ContextBag context)
        {
            var messageId = RemoveFromLookup(subscriber, messageType);

            if (messageId != null)
            {
                storageQueue.TryReceiveById(messageId);
            }

            log.Debug($"Subscriber {subscriber.TransportAddress} removed for message {messageType}.");

            return(TaskEx.CompletedTask);
        }
        public void Init()
        {
            var messages = storageQueue.GetAllMessages()
                           .OrderByDescending(m => m.ArrivedTime)
                           .ThenBy(x => x.Id) // ensure same order of messages with same timestamp accross all endpoints
                           .ToArray();

            try
            {
                rwLock.EnterWriteLock();

                foreach (var m in messages)
                {
                    var messageTypeString = m.Body as string;
                    var messageType       = new MessageType(messageTypeString); //this will parse both 2.6 and 3.0 type strings
                    var subscriber        = Deserialize(m.Label);

                    Dictionary <MessageType, string> endpointSubscriptions;
                    if (!lookup.TryGetValue(subscriber, out endpointSubscriptions))
                    {
                        lookup[subscriber] = endpointSubscriptions = new Dictionary <MessageType, string>();
                    }

                    if (endpointSubscriptions.ContainsKey(messageType))
                    {
                        // this message is stale and can be removed
                        storageQueue.TryReceiveById(m.Id);
                    }
                    else
                    {
                        endpointSubscriptions[messageType] = m.Id;
                    }
                }
            }
            finally
            {
                rwLock.ExitWriteLock();
            }
        }
        public async Task Unsubscribe(Subscriber subscriber, MessageType messageType, ContextBag context)
        {
            var subscriptionClient = new SubscriptionClient { TransportAddress = subscriber.TransportAddress, Endpoint = subscriber.Endpoint };

            using (var session = OpenAsyncSession())
            {
                var subscriptionDocId = Subscription.FormatId(messageType);

                var subscription = await session.LoadAsync<Subscription>(subscriptionDocId).ConfigureAwait(false);

                if (subscription == null)
                {
                    return;
                }

                if (subscription.Subscribers.Contains(subscriptionClient))
                {
                    subscription.Subscribers.Remove(subscriptionClient);
                }

                await session.SaveChangesAsync().ConfigureAwait(false);
            }
        }
Example #11
0
 /// <summary>
 /// Equality, only major version is used
 /// </summary>
 /// <param name="other"></param>
 /// <returns></returns>
 public bool Equals(MessageType other)
 {
     if (ReferenceEquals(null, other)) return false;
     if (ReferenceEquals(this, other)) return true;
     return Equals(other.TypeName, TypeName) && other.Version.Major == Version.Major;
 }
        /// <summary>
        /// The format id.
        /// </summary>
        /// <param name="messageType">
        /// The message type.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string FormatId(MessageType messageType)
        {
            Contract.Requires<ArgumentNullException>(messageType != null, "messageType != null");
            Contract.Ensures(Contract.Result<string>() != null);

            var id = DeterministicGuid.Create(messageType.TypeName, "/", messageType.Version.Major);
            return string.Format("Subscriptions/{0}", id);
        }
Example #13
0
 /// <summary>
 /// Equality, only major version is used.
 /// </summary>
 public bool Equals(MessageType other)
 {
     if (ReferenceEquals(null, other))
     {
         return false;
     }
     if (ReferenceEquals(this, other))
     {
         return true;
     }
     return Equals(other.TypeName, TypeName);
 }