Example #1
0
        /// <summary>
        ///     Creates a new subscription for the specified topic.
        /// </summary>
        /// <typeparam name="T">The type of data the subscription is expected to return.</typeparam>
        /// <typeparam name="TPayloadConverter">The type of the converter that can convert from bytes to the type T.</typeparam>
        /// <param name="topic">The topic to subscribe to.</param>
        /// <param name="qos">The QOS level to subscribe at.</param>
        /// <returns>An observable that yields messages when they arrive.</returns>
        /// <exception cref="InvalidTopicException">If a topic that does not meet the MQTT topic spec rules is provided.</exception>
        private IObservable <MqttReceivedMessage <T> > CreateNewSubscription <T, TPayloadConverter>(string topic, MqttQos qos)
            where TPayloadConverter : IPayloadConverter <T>, new()
        {
            Log.Info(m => m("Creating subscription for topoc {0} @ QOS {1}.", topic, qos));

            try {
                var subscriptionTopic = new SubscriptionTopic(topic);

                // Get an ID that represents the subscription. We will use this same ID for unsubscribe as well.
                var msgId = messageIdentifierDispenser.GetNextMessageIdentifier("subscriptions");

                // create a new observable that is used to yield messages
                // that arrive for the topoc.
                var observable = CreateObservableForSubscription(subscriptionTopic, msgId);

                var sub = new Subscription {
                    Topic             = subscriptionTopic,
                    Qos               = qos,
                    MessageIdentifier = msgId,
                    CreatedTime       = DateTime.Now,
                    Observable        = observable,
                };

                pendingSubscriptions.Add(sub.MessageIdentifier, sub);

                // build a subscribe message for the caller and send it off to the broker.
                var msg = new MqttSubscribeMessage().WithMessageIdentifier(sub.MessageIdentifier)
                          .ToTopic(sub.Topic.ToString())
                          .AtQos(sub.Qos);
                connectionHandler.SendMessage(msg);

                return(WrapSubscriptionObservable <T, TPayloadConverter>(sub.Observable));
            } catch (ArgumentException ex) {
                Log.Warn(m => m("Error while processing topoc {0}. topoc structure not valid.", topic), ex);
                throw new InvalidTopicException(ex.Message, topic, ex);
            }
        }
Example #2
0
 public void MultiWildcardOnlyTopicMatchesAnyRandomTopic() {
     var topic = new SubscriptionTopic("#");
     Assert.True(topic.Matches(new PublicationTopic("finance/ibm/closingprice")));
 }
Example #3
0
 public void SingleLevelEqualTopicsMatch() {
     var topic = new SubscriptionTopic("finance");
     Assert.True(topic.Matches(new PublicationTopic("finance")));
 }
Example #4
0
 public void ToStringReturnsSameTopicAsInput() {
     const string expectedTopicString = "finance/ibm";
     var topic = new SubscriptionTopic(expectedTopicString);
     Assert.Equal(expectedTopicString, topic.ToString());
 }
Example #5
0
 public void TopicsDifferingOnlyByCaseDoNotMatch() {
     var topic = new SubscriptionTopic("finance");
     Assert.False(topic.Matches(new PublicationTopic("Finance")));
 }       
Example #6
0
 public void TopicWithMultiWildcardDoesNotMatchTopicWithDifferenceBeforeWildcardLevel() {
     var topic = new SubscriptionTopic("finance/#");
     Assert.False(topic.Matches(new PublicationTopic("money/ibm")));
 }  
Example #7
0
 public void TopicWithSingleWildcardAtEndMatchesAnythingInSameLevel() {
     var topic = new SubscriptionTopic("finance/+/closingprice");
     Assert.True(topic.Matches(new PublicationTopic("finance/ibm/closingprice")));
 }
Example #8
0
 public void TopicWithSingleWildcardAtEndDoesNotMatchTopicThatGoesDeeper() {
     var topic = new SubscriptionTopic("finance/+");
     Assert.False(topic.Matches(new PublicationTopic("finance/ibm/closingprice")));
 }
Example #9
0
 public void TopicWithSingleWildcardAtEndMatchesTopicWithEmptyLastFragmentAtThatSpot() {
     var topic = new SubscriptionTopic("finance/ibm/+");
     Assert.True(topic.Matches(new PublicationTopic("finance/ibm/")));
 }
Example #10
0
 public void SingleLevelNonEqualTopicsDoNotMatch() {
     var topic = new SubscriptionTopic("finance");
     Assert.False(topic.Matches(new PublicationTopic("money")));
 }
Example #11
0
 public void TopicWithSingleWildcardMatchesTopicEmptyFragmentAtThatPoint() {
     var topic = new SubscriptionTopic("finance/+/closingprice");
     Assert.True(topic.Matches(new PublicationTopic("finance//closingprice")));
 }
Example #12
0
 public void TopicWithSingleAndMultiWildcardMatchesTopicWithAnyValueAtThoseLevelsAndDeeper() {
     var topic = new SubscriptionTopic("finance/+/closingprice/month/#");
     Assert.True(topic.Matches(new PublicationTopic("finance/ibm/closingprice/month/october/2014")));
 }
Example #13
0
 public void TopicWithMoreThanOneSingleWildcardAtDifferentLevelsMatchesTopicWithAnyValueAtThoseLevels() {
     var topic = new SubscriptionTopic("finance/+/closingprice/month/+");
     Assert.True(topic.Matches(new PublicationTopic("finance/ibm/closingprice/month/october")));
 }
Example #14
0
 public void MultiWildcardOnlyTopicMatchesTopicStartingWithSeparator() {
     var topic = new SubscriptionTopic("#");
     Assert.True(topic.Matches(new PublicationTopic("/finance/ibm/closingprice")));
 }
Example #15
0
 public void TopicWithSingleWildcardAtEndDoesNotMatchTopicThatDoesNotContainAnythingAtSameLevel() {
     var topic = new SubscriptionTopic("finance/+");
     Assert.False(topic.Matches(new PublicationTopic("finance")));
 }        
Example #16
0
 public void TopicWithMultiWildcardAtEndMatchesTopicThatDoesNotMatchSameDepth() {
     var topic = new SubscriptionTopic("finance/#");
     Assert.True(topic.Matches(new PublicationTopic("finance")));
 }
Example #17
0
 public void MultiLevelNonEqualTopicsDoNotMatch() {
     var topic = new SubscriptionTopic("finance/ibm/closingprice");
     Assert.False(topic.Matches(new PublicationTopic("some/random/topic")));
 }
Example #18
0
        /// <summary>
        ///     Creates an observable for a subscription.
        /// </summary>
        /// <param name="subscriptionTopic">The topic to the obserbable should read messages on.</param>
        /// <param name="msgId">The messgeid assigned to the subscription.</param>
        /// <returns>An observable that yields a byte array for each message that arrives on a topoc.</returns>
        private IObservable <MqttReceivedMessage <byte[]> > CreateObservableForSubscription(SubscriptionTopic subscriptionTopic, short msgId)
        {
            var observable = Observable.Create <MqttReceivedMessage <byte[]> >(observer => {
                Log.Info(m => m("Creating underlying core observable for topoc {0}.", subscriptionTopic));

                // Listen for payload messages and when they arrive for our topoc
                // publish them onto the observable.
                var msgPubObservable
                    = Observable.FromEventPattern <PublishEventArgs>(h => publishingManager.MessageReceived += h,
                                                                     h => publishingManager.MessageReceived -= h);
                var msgPubSub = msgPubObservable
                                .Where(ep => subscriptionTopic.Matches(ep.EventArgs.Topic))
                                .Select(ep => ep.EventArgs)
                                .Subscribe(eventArgs => {
                    try {
                        // we use the messages topic name here so that if the
                        // matched subscription was a wildcard, we give the
                        // consumer the actual topic the message was published
                        // for, not the original wildcard subscription topic.
                        observer.OnNext(new MqttReceivedMessage <byte[]>(eventArgs.Topic.ToString(),
                                                                         eventArgs.PublishMessage.Payload.Message.ToArray()));
                    } catch (Exception ex) {
                        Log.Error(m => m("Error while publishing message to observer for topic {0}.", eventArgs.Topic.ToString()), ex);
                    }
                });

                // Unsubscribe from the topoc on the server,
                return(Disposable.Create(() => {
                    Log.Info(m => m("Last subscriber gone for topic '{0}', unsubscribing on broker.", subscriptionTopic));

                    // stop processing publish messages for this topoc received by thethe publishing manager.
                    msgPubSub.Dispose();

                    // build a unsubscribe message for the caller and send it off to the broker.
                    var unsubscribeMsg = new MqttUnsubscribeMessage()
                                         .WithMessageIdentifier(messageIdentifierDispenser.GetNextMessageIdentifier("unsubscriptions"))
                                         .WithMessageIdentifier(msgId)
                                         .FromTopic(subscriptionTopic.ToString());
                    connectionHandler.SendMessage(unsubscribeMsg);
                }));
            });

            // Publish and refcount so we can share the single subscription amongst all
            // subscribers and dispose automatically when everyone has disposed their
            // subscriptions.
            return(observable.Publish()
                   .RefCount());
        }
Example #19
0
 public void TopicWithMultiWildcardAtEndMatchesTopicWithAnythingAtWildcardLevel() {
     var topic = new SubscriptionTopic("finance/#");
     Assert.True(topic.Matches(new PublicationTopic("finance/ibm")));
 }