In the past, I’ve written and talked about integrating the Windows Azure Service Bus with non-Microsoft platforms like Salesforce.com. I enjoy showing how easy it is to use the Service Bus Relay to connect on-premises services with Salesforce.com. On multiple occasions, I’ve been asked how to do this with Service Bus brokered messaging options (i.e. Topics and Queues) as well. It can be a little tricky as it requires the use of the Windows Azure REST API and there aren’t a ton of public examples of how to do it! So in this blog post, I’ll show you how to send a message to a Service Bus Topic from Salesforce.com. Note that this sequence resembles how you’d do this on ANY platform that can’t use a Windows Azure SDK.
Creating the Topic and Subscription
First, I needed a Topic and Subscription to work with. Recall that Topics differ from Queues in that a Topic can have multiple subscribers. Each subscription (which may filter on message properties) has its own listener and gets their own copy of the message. In this fictitious scenario, I wanted users to submit IT support tickets from a page within the Salesforce.com site.
I could create a Topic in a few ways. First, there’s the Windows Azure portal. Below you can see that I have a Topic called “TicketTopic” and a Subscription called “AllTickets”.
If you’re a Visual Studio developer, you can also use the handy Windows Azure extensions to the Server Explorer window. Notice below that this tool ALSO shows me the filtering rules attached to each Subscription.
With a Topic and Subscription set up, I was ready to create a custom VisualForce page to publish to it.
Code to Get an ACS Token
Before I could send a message to a Topic, I needed to get an authentication token from the Windows Azure Access Control Service (ACS). This token goes into the request header and lets Windows Azure determine if I’m allowed to publish to a particular Topic.
In Salesforce.com, I built a custom VisualForce page with the markup necessary to submit a support ticket. The final page looks like this:
I also created a custom Controller that extended the native Accounts Controller and added an operation to respond to the “Submit Ticket” button event. The first bit of code is responsible for calling ACS and getting back a token that can be included in the subsequent request. Salesforce.com extensions are written in a language called Apex, but it should look familiar to any C# or Java developer.
Http h= new Http(); HttpRequest acReq = new HttpRequest(); HttpRequest sbReq = new HttpRequest(); // define endpoint and encode password String acUrl = 'https://seroter-sb.accesscontrol.windows.net/WRAPV0.9/'; String encodedPW = EncodingUtil.urlEncode(sbUPassword, 'UTF-8'); acReq.setEndpoint(acUrl); acReq.setMethod('POST'); // choose the right credentials and scope acReq.setBody('wrap_name=demouser&wrap_password=' + encodedPW + '&wrap_scope=http://seroter.servicebus.windows.net/'); acReq.setHeader('Content-Type','application/x-www-form-urlencoded'); HttpResponse acRes = h.send(acReq); String acResult = acRes.getBody(); // clean up result to get usable token String suffixRemoved = acResult.split('&')[0]; String prefixRemoved = suffixRemoved.split('=')[1]; String decodedToken = EncodingUtil.urlDecode(prefixRemoved, 'UTF-8'); String finalToken = 'WRAP access_token=\"' + decodedToken + '\"';
This code block makes an HTTP request to the ACS endpoint and manipulates the response into the token format I needed.
Code to Send the Message to a Topic
Now comes the fun stuff. Here’s how you actually send a valid message to a Topic through the REST API. Below is the complete code snippet, and I’ll explain it further in a moment.
//set endpoint using this scheme: https://<namespace>.servicebus.windows.net/<topic name>/messages String sbUrl = 'https://seroter.servicebus.windows.net/demotopic/messages'; sbReq.setEndpoint(sbUrl); sbReq.setMethod('POST'); // sending a string, and content type doesn't seem to matter here sbReq.setHeader('Content-Type', 'text/plain'); // add the token to the header sbReq.setHeader('Authorization', finalToken); // set the Brokered Message properties sbReq.setHeader('BrokerProperties', '{ \"MessageId\": \"{'+ guid +'}\", \"Label\":\"supportticket\"}'); // add a custom property that can be used for routing sbReq.setHeader('Account', myAcct.Name); // add the body; here doing it as a JSON payload sbReq.setBody('{ \"Account\": \"'+ myAcct.Name +'\", \"TicketType\": \"'+ TicketType +'\", \"TicketDate\": \"'+ SubmitDate +'\", \"Description\": \"'+ TicketText +'\" }'); HttpResponse sbResult = h.send(sbReq);
So what’s happening here? First, I set the endpoint URL. In this case, I had to follow a particular structure that includes “/messages” at the end. Next, I added the ACS token to the HTTP Authorization header.
After that, I set the brokered messaging header. This fills up a JSON-formatted BrokerProperties structure that includes any values you needed by the message consumer. Notice here that I included a GUID for the message ID and provided a “label” value that I could access later. Next, I defined a custom header called “Account”. These custom headers get added to the Brokered Message’s “Properties” collection and are used in Subscription filters. In this case, a subscriber could choose to only receive Topic messages related to a particular account.
Finally, I set the body of the message. I could send any string value here, so I chose a lightweight JSON format that would be easy to convert to a typed object on the receiving end.
With all that, I was ready to go.
Receiving From Topic
To get a message into the Topic, I submitted a support ticket from the VisualForce page.
I immediately switched to the Windows Azure portal to see that a message was now queued up for the Subscription.
How can I retrieve this message? I could use the REST API again, but let’s show how we can mix and match techniques. In this case, I used the Windows Azure SDK for .NET to retrieve and delete a message from the Topic. I also referenced the excellent JSON.NET library to deserialize the JSON object to a .NET object. The tricky part was figuring out the right way to access the message body of the Brokered Message. I wasn’t able to simply pull it out a String value, so I went with a Stream instead. Here’s the complete code block:
//pull Service Bus connection string from the config file string connectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"]; //create a subscriptionclient for interacting with Topic SubscriptionClient client = SubscriptionClient.CreateFromConnectionString(connectionString, "tickettopic", "alltickets"); //try and retrieve a message from the Subscription BrokeredMessage m = client.Receive(); //if null, don't do anything interesting if (null == m) { Console.WriteLine("empty"); } else { //retrieve and show the Label value of the BrokeredMessage string label = m.Label; Console.WriteLine("Label - " + label); //retrieve and show the custom property of the BrokeredMessage string acct = m.Properties["Account"].ToString(); Console.WriteLine("Account - " + acct); Ticket t; //yank the BrokeredMessage body as a Stream using (Stream c = m.GetBody<Stream>()) { using (StreamReader sr = new StreamReader(c)) { //get a string representation of the stream content string s = sr.ReadToEnd(); //convert JSON to a typed object (Ticket) t = JsonConvert.DeserializeObject<Ticket>(s); m.Complete(); } } //show the ticket description Console.WriteLine("Ticket - " + t.Description); }
Pretty simple. Receive the message, extract interesting values (like the “Label” and custom properties), and convert the BrokeredMessage body to a typed object that I could work with. When I ran this bit of code, I saw the values we set in Salesforce.com.
Summary
The Windows Azure Service Bus brokered messaging services provide a great way to connect distributed systems. The store-and-forward capabilities are key when linking systems that span clouds or link the cloud to an on-premises system. While Microsoft provides a whole host of platform-specific SDKs for interacting with the Service Bus, there are platforms that have to use the REST API instead. Hopefully this post gave you some insight into how to use this API to successfully publish to Service Bus Topics from virtually ANY software platform.
Good stuff, thanks. But I keep getting a 401 – Invalid authorization token signature – when I send a message. The SWT token looks fine as far as I can tell, so I think it’s a problem with the way I’ve set up ACS. Any tips on that?
Hey Mike. Are you using the password or the key to authenticate? I sometimes get a messed up ACS account where I’m passing in the wrong credentials! Look at the ACS portal and try both values.
Is there also a way of doing this the other way round? So my Azure app adds a message to a Topic and a third-party customer who subscribes to that Topic using an HTTP endpoint (using a non-.NET system) receives this message?
Definitely. You can use the REST API (http://msdn.microsoft.com/en-us/library/windowsazure/hh780770.aspx) to also pull from a Topic subscription. You could do this synchronously on demand, or have a background job that does this occasionally.
Hi Richard
Thanks for your article. It was really helpful. I’m using the REST API to receive a message according to the above document in MSDN. However I’m getting “The specified HTTP verb (POST) is not valid”, HTTP 400 error when try to retrieve the message. Any idea why? I’m using SAS for authentication. The send message works fine.
Cheers
Manoj
Did you ever figure this out getting “The specified HTTP verb (POST) is not valid.” I’m getting the same error trying to call /$deadletterqueue
You are using Azure service bus in the cloud. If I were to use on premise Service Bus for windows server and I have the port 9355 open and ssl enabled, how can I send a message to the queue from salesforce and also pick up from the queue? Do I use Shared Access Signature Authentication? Is that similar to adding ACS token to the HTTP Authorization header?
Wow, not sure about using on-prem Service Bus with this. So you have a public IP and those ports open?
That’s the plan. We are planning to test it out. I was wondering if you had any suggestions.
Nothing comes to mind. If the ports are open, I don’t see any obvious reason it wouldn’t work. That said, you’ll want to be careful about security and opening up a service like that to public internet traffic.
@Chandra – Did you have any luck getting this to work?
No, We didn’t even try. We had to drop this plan.
Do you know what should I configure security related in salesforce in addition to workflow rule and outbound message to send the xml message to an azure topic or queue?
You definitely have to make sure to have the endpoint added to the remote site settings http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_callouts_remote_site_settings.htm