Windows Azure, Microsoft .NET Services – Working With Queues (I)

[This post is based on the Windows Azure Tools for Microsoft Visual Studio July 2009 CTP]

In my previous post on Acquiring an Authentication Token I demonstrated how you can use a Http Get operation to acquire an Authentication Token.  This token can be used to create, modify, delete and send messages to both queues and routers as part of the Windows Azure, Microsoft .NET Services. Again, whilst there are .NET libraries for working with both queues and routers in this post we’ll still to the basic Http messages so that you can see what’s going on under the hood.

The first place to start is to create a queue. To do this you send a Http Post to the url that you want the queue to reside at, supplying a QueuePolicy entry.  The url has to belong to your solution url, so for example https://blogsample.servicebus.windows.net/myqueue. There are a number of attributes you can set in the QueuePolicy you supply but as a minimum you need to set the ExpirationInstant, which as you can imagine is the instant that the queue will expire and self-destruct (unless renewed). An example QueuePolicy would look like the following:

<entry >http://www.w3.org/2005/Atom">
  <QueuePolicy >http://schemas.microsoft.com/netservices/2009/05/servicebus/connect">
    <ExpirationInstant>2009-08-30T23:56:25.2951184Z</ExpirationInstant>
  </QueuePolicy>
</entry>

Now to send this:

public static string HttpCreateQueue(string token, string queue)
{
    string queueUri = "
https://blogsample.servicebus.windows.net/" + queue;

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(queueUri);
   request.Headers.Add("X-MS-Identity-Token", token);
    request.Method = "POST";
    request.ContentType = "application/atom+xml;type=entry;charset=utf-8";

    XElement content = new XElement(XName.Get("entry", "http://www.w3.org/2005/Atom"),
                            ServicesElement("QueuePolicy",
                                ServicesElement("ExpirationInstant", DateTime.UtcNow.AddMinutes(30))));

    using (var requestStream = request.GetRequestStream())
    using (var writer = new System.IO.StreamWriter(requestStream))
    {
            writer.Write(content);
            writer.Flush();
    }

    using (var response = request.GetResponse())
    using (var responseStream = response.GetResponseStream())
    using (var reader = new StreamReader(responseStream))
    {
        return reader.ReadToEnd();
    }
}

private static XElement ServicesElement(string element, object content)
{
    return new XElement(XName.Get(element, "
http://schemas.microsoft.com/netservices/2009/05/servicebus/connect"), content);
}

Note that you need to supply the authentication token using the X-MS-Identity-Token header.  Here we have built up the QueuePolicy as an XElement, you can of course use the .NET library to generate this for you. When you run this code the response (assuming it is successful) should be a 201, Created. Subsequent Posts to this url will result in a 202, Accepted – note however this does not update or alter the queue. In addition to the 201 status, the Post also returns information about the created queue:

<entry >http://www.w3.org/2005/Atom">
   <id>uuid:11cdc611-62fb-49d1-8dee-e47bf9cc93d8;id=1407</id>
   <title type="text">myqueue</title>
   <updated>2009-08-30T23:44:13Z</updated>
   <link rel="alternate" href="
https://blogsample.servicebus.windows.net/myqueue/"/>
   <link rel="self" href="
https://blogsample.servicebus.windows.net/myqueue/!(queue)"/>
   <link rel="queuehead" href="
https://blogsample.servicebus.windows.net/myqueue/!(queue/head)"/>
   <link rel="queuecontrol" href="
https://blogsample.servicebus.windows.net/myqueue/!(queue/control)"/>
   <QueuePolicy >http://schemas.microsoft.com/netservices/2009/05/servicebus/connect" >;>
      <ExpirationInstant>2009-08-31T00:14:13.2742033Z</ExpirationInstant>
      <MaxMessageSize>61440</MaxMessageSize>
      <EnqueueTimeout>PT10S</EnqueueTimeout>
      <MaxConcurrentReaders>10</MaxConcurrentReaders>
      <MaxDequeueRetries>3</MaxDequeueRetries>
      <MaxMessageAge>PT10M</MaxMessageAge>
      <MaxQueueLength>2147483647</MaxQueueLength>
      <MaxQueueCapacity>10485760</MaxQueueCapacity>
      </QueuePolicy>
</entry>

The important elements to note from this are the links.  These correspond to the urls where you will perform actions on the queue.

alternate – This is the url you will use when placing new messages on the queue.  You can think of this as the tail of the queue.

self – This is the url you will now use to update, renew and delete the queue.

queuehead – This is the url where you will pop messages off the queue.  As the name implies, this is the head of the queue

queuecontrol – Currently unused, it is anticipated that this will be used for queue message control.

We’ll come back to these in a minute but let’s first see what this would look like in javascript (again remember this will only work in the context of Gadgets and Widgets where the cross site scripting rules are relaxed):

function httpCreateQueue(token, queue) {
    var policy = "<entry >http://www.w3.org/2005/Atom">" +
  "<QueuePolicy >http://schemas.microsoft.com/netservices/2009/05/servicebus/connect">" +
    "<ExpirationInstant>2009-08-31T03:56:25.2951184Z</ExpirationInstant>" +
  "</QueuePolicy>" +
"</entry>";

    http = new XMLHttpRequest();
    http.open("POST", "
https://blogsample.servicebus.windows.net/" + queue, false);
    http.setRequestHeader("X-MS-Identity-Token", token);
    http.setRequestHeader("Content-type", "application/atom+xml;type=entry;charset=utf-8");
    http.onreadystatechange = function() {
        if (http.readyState == 4 && http.status == 201) {
            var response = http.responseText;
            // Do stuff with the response message
        }
    }
    http.send(policy);
}

One observation is that the service bus is quite sensitive when it comes to the formatting and contents of the request, and like all secure services if you get something wrong it will give you a 500 Internal Server error with no additional information to diagnose what’s going wrong.  In my case the number one thing that was biting me was the ExpirationInstant.  This needs to be UTC time and in the future 🙂

Leave a comment