Blog

How to Deliver Conditional Messages With Subtopics

by on September 1, 2019

In another post, we described a feature for conditional pub/sub message delivery using SQL selectors. With that (selector) approach, the publisher must attach headers to the message and the subscriber uses an SQL-based condition that references header names and values. In addition to selectors, Backendless supports another type of conditional delivery – subtopics.

Consider the following example:

A message with an announcement from Microsoft is published to the topic stocks.nasdaq.msft. Another message from IBM is published to stocks.nyse.ibm. Suppose a subscriber wants to receive all messages related to stock announcements. In this case, they subscribe to the stocks.* subtopic.

Another subscriber is interested in all Nasdaq announcements. That subscriber subscribes to the stocks.nasdaq.* subtopic. Finally, to receive Microsoft announcements, a subscriber would use the stocks.nasdaq.msft subtopic. As you can see, Backendless uses the federated subtopic structure for message filtering/delivery.

The sample below shows how to use subtopics in the Backendless messaging API:

Asynchronous API sample (Android and Plain Java):

AsyncCallback<MessageStatus> publishCallback = new AsyncCallback<MessageStatus>()
{
    @Override
    public void handleResponse( MessageStatus messageStatus )
    {
        System.out.println( "Message published - " + messageStatus.getMessageId() );
    }
    @Override
    public void handleFault( BackendlessFault backendlessFault )
    {
        System.out.println( "Server reported an error " + backendlessFault.getMessage() );
    }
};
while( true )
{
    // *********************************************************************************************************
    // publish message 1 to subtopic "news.dallas.sports"
    // *********************************************************************************************************
    NewsMessage newsMessage1 = new NewsMessage();
    newsMessage1.teaser = "Dallas Cowboys project tab in Frisco: $168 million and climbing";
    newsMessage1.details = "The price tag for the publicly owned portion of Dallas Cowboys development in Frisco has hit $168 million and continues to grow as the project takes shape";
    newsMessage1.newsURL = "http://friscoblog.dallasnews.com/2015/03/dallas-cowboys-project-tab-in-frisco-168-million-and-climbing.html/";
    PublishOptions publishOptions1 = new PublishOptions();
    publishOptions1.setSubtopic( "news.dallas.sports" );
    Backendless.Messaging.publish( "MyAppChannel", newsMessage1, publishOptions1, publishCallback );
    // *********************************************************************************************************
    // publish message 2 to subtopic "news.us.technology"
    // *********************************************************************************************************
    NewsMessage newsMessage = new NewsMessage();
    newsMessage.teaser = "Magic Leap Demo Flaunts Augmented Reality";
    newsMessage.details = "In the latest \"OMG, I want one!\" news, startup Magic Leap on Thursday teased its advanced augmented reality tech";
    newsMessage.newsURL = "http://www.pcmag.com/article2/0,2817,2478526,00.asp";
    PublishOptions publishOptions2 = new PublishOptions();
    publishOptions2.setSubtopic( "news.us.technology" );
    Backendless.Messaging.publish( "MyAppChannel", newsMessage, publishOptions2, publishCallback );
    Thread.sleep( 500 );
}

Synchronous API sample (Plain Java only):

while( true )
{
    // *********************************************************************************************************
    // publish message 1 to subtopic "news.dallas.sports"
    // *********************************************************************************************************
    NewsMessage newsMessage1 = new NewsMessage();
    newsMessage1.teaser = "Dallas Cowboys project tab in Frisco: $168 million and climbing";
    newsMessage1.details = "The price tag for the publicly owned portion of Dallas Cowboys development in Frisco has hit $168 million and continues to grow as the project takes shape";
    newsMessage1.newsURL = "http://friscoblog.dallasnews.com/2015/03/dallas-cowboys-project-tab-in-frisco-168-million-and-climbing.html/";
    PublishOptions publishOptions1 = new PublishOptions();
    publishOptions1.setSubtopic( "news.dallas.sports" );
    MessageStatus messageStatus = Backendless.Messaging.publish( "MyAppChannel", newsMessage1, publishOptions1 );
    System.out.println( "Message published - " + messageStatus.getMessageId() );
    // *********************************************************************************************************
    // publish message 2 to subtopic "news.us.technology"
    // *********************************************************************************************************
    NewsMessage newsMessage = new NewsMessage();
    newsMessage.teaser = "Magic Leap Demo Flaunts Augmented Reality";
    newsMessage.details = "In the latest \"OMG, I want one!\" news, startup Magic Leap on Thursday teased its advanced augmented reality tech";
    newsMessage.newsURL = "http://www.pcmag.com/article2/0,2817,2478526,00.asp";
    PublishOptions publishOptions2 = new PublishOptions();
    publishOptions2.setSubtopic( "news.us.technology" );
    messageStatus = Backendless.Messaging.publish( "MyAppChannel", newsMessage, publishOptions2 );
    System.out.println( "Message published - " + messageStatus.getMessageId() );
    Thread.sleep( 500 );
}

The code above publishes two messages to the news.dallas.sports and news.us.technology topics. Also, notice the published message is a custom strongly-typed object (as opposed to a primitive data type or a string). The code below subscribes to receive messages published to the news.* subtopic.

When you run that code, it will receive both messages from the publisher. However, if you modify the subscriber to receive messages from the news.us.* subtopic, it will receive only one of them (repeatedly, though, as the messages are published in a loop):

AsyncCallback<List<Message>> subscriptionResponder = new AsyncCallback<List<Message>>()
{
    @Override
    public void handleResponse( List<Message> messages )
    {
        Iterator<Message> messageIterator = messages.iterator();
        while( messageIterator.hasNext() )
        {
            Message message = messageIterator.next();
            NewsMessage newsMessage = (NewsMessage) message.getData();
            System.out.println( String.format( "Received message - %s\n\tHeadline - %s\n\tDetails - %s\n\tURL - %s",
                    message.getMessageId(), newsMessage.teaser, newsMessage.details, newsMessage.newsURL ) );
        }
    }
    @Override
    public void handleFault( BackendlessFault backendlessFault )
    {
        System.out.println( "Server reported an error " + backendlessFault.getMessage() );
    }
};
SubscriptionOptions subscriptionOptions = new SubscriptionOptions();
subscriptionOptions.setSubtopic( "news.*" );
Backendless.Messaging.subscribe( "MyAppChannel", subscriptionResponder, subscriptionOptions );

Enjoy!

Leave a Reply