public SubscriptionInfo Unsubscribe(Topic topic, Subscriber subscriber)
        {
            topic.Requires("topic").IsNotNull();
            subscriber.Requires("subscriber").IsNotNull();

            if (isDisposed)
            {
                throw new ObjectDisposedException(MethodBase.GetCurrentMethod()?.DeclaringType?.Name,
                    "instance has been disposed");
            }

            var key = GetKey(topic, subscriber);
            SubscriptionInfo subscriptionInfo;
            if (subscriptions.TryRemove(key, out subscriptionInfo))
            {
                subscriptionInfo.CancelToken.Cancel();
                if (!subscriber.LongLived || resourceCounter.Decrement(subscriptionInfo.Subscriber.Name) == 0)
                {
                    amazonSnsFacade.UnsubscribeQueueFromTopic(subscriptionInfo.SubscriptionArn);
                }
                subscriptionInfo.CancelToken = null;
                subscriptionInfo.SubscriptionArn = null;
            }
            return subscriptionInfo;
        }
        public bool IsSubscribed(Topic topic, Subscriber subscriber)
        {
            topic.Requires("topic").IsNotNull();
            subscriber.Requires("subscriber").IsNotNull();

            if (isDisposed)
            {
                throw new ObjectDisposedException(MethodBase.GetCurrentMethod()?.DeclaringType?.Name,
                    "instance has been disposed");
            }

            var key = GetKey(topic, subscriber);
            return subscriptions.ContainsKey(key);
        }
        public void ExpireSubscriber(Subscriber subscriber)
        {
            subscriber.Requires("subscriber").IsNotNull();

            if (!subscribers.ContainsKey(subscriber.Name))
            {
                return;
            }

            subscribers.Remove(subscriber.Name);
            if (!subscriber.LongLived)
            {
                amazonSqsFacade.DeleteQueue(subscriber.QueueUrl);
            }
        }
        public SubscriptionInfo Subscribe(Topic topic, Subscriber subscriber, Action<Message> messageHandler)
        {
            topic.Requires("topic").IsNotNull();
            subscriber.Requires("subscriber").IsNotNull();
            messageHandler.Requires("messageHandler").IsNotNull();

            if (isDisposed)
            {
                throw new ObjectDisposedException(MethodBase.GetCurrentMethod()?.DeclaringType?.Name,
                    "instance has been disposed");
            }

            var key = GetKey(topic, subscriber);
            return subscriptions.AddOrUpdate(key, k => AddSubscription(topic, subscriber, messageHandler),
                (x, s) => UpdateSubscription(s, messageHandler));
        }
        public Subscriber GetSubscriber(string channelName, string subscriberId, bool longLived)
        {
            channelName.Requires().IsNotNullOrWhiteSpace();
            subscriberId.Requires().IsNotNullOrWhiteSpace();

            var uniqueQueue = GetQueueName(channelName, subscriberId);
            if (subscribers.ContainsKey(uniqueQueue))
            {
                return subscribers[uniqueQueue];
            }

            var queueUrl = amazonSqsFacade.CreateOrRetrieveQueue(uniqueQueue);
            var queueArn = amazonSqsFacade.GetQueueArn(queueUrl);

            var subscriber = new Subscriber(uniqueQueue, queueUrl, queueArn, longLived);
            subscribers[uniqueQueue] = subscriber;
            return subscriber;
        }
        private SubscriptionInfo AddSubscription(Topic topic, Subscriber subscriber, Action<Message> messageHandler)
        {
            var subscriptionInfo = new SubscriptionInfo
            {
                Topic = topic,
                Subscriber = subscriber
            };

            amazonSqsFacade.SetSqsPolicyForSnsPublish(subscriber.QueueUrl, subscriber.QueueArn, topic.TopicArn);
            subscriptionInfo.SubscriptionArn =
                amazonSnsFacade.SubscribeQueueToTopic(subscriptionInfo.Subscriber.QueueArn,
                    subscriptionInfo.Topic.TopicArn);
            if (subscriptionInfo.Subscriber.LongLived)
            {
                resourceCounter.Increment(subscriptionInfo.Subscriber.Name);
            }
            subscriptionInfo.CancelToken = queuePoller.Start(subscriptionInfo, messageHandler);

            return subscriptionInfo;
        }
 private static string GetKey(Topic topic, Subscriber subscriber)
 {
     return string.Concat(topic.Name, "-", subscriber.Name);
 }