Thu, 16 Apr 2009

Programming Language Interoperability in Windows Azure

Shoutomatic screenshotI love programming languages.  Each of the languages I’ve learned over the years has taught me something about programming.  To play with a few languages and to show off some of the language interoperability in Windows Azure, I recently put together a sample called “Shoutomatic.”  You can play with it at http://shout.cloudapp.net, and you can download the source code here.

Overview

Shoutomatic consists of a web role and a worker role.  The web role accepts user input and places it on a queue.  The worker role polls the queue and sends a message to Twitter with each message that comes in.

Shoutomatic uses PHP, .NET, and Java in the cloud, and I use a Python script locally to set up the storage account.  I should warn you now that actually building and deploying from the source code I’ve shared is tricky… you’ll need to download a number of dependencies.  Let me know if you’re trying this and the instructions aren’t clear.

Let’s take each language one a time to see how the thing works.

PHP

As part of our March CTP, we introduced FastCGI support into Windows Azure.  PHP is the perfect language to take advantage of this, because there’s already great support for using PHP on Windows via IIS7.

I’ve written about PHP in Windows Azure before in the context of Tweval.  There was nothing significantly different in building this sample, though instead of reading entities from Windows Azure tables, this time I’m writing messages to a Windows Azure queue.  Here’s the interesting bit of the source code:


<?php
$account = "GETYOUROWNACCOUNT";
$key = "GETYOUROWNKEY";
function enqueue_tweet($text) {
    $date = gmdate('D, d M Y H:i:s \G\M\T');
    $stringtosign = "POST\n\ntext/xml\n\nx-ms-date:$date\n/$account/tweet/messages";
    $signature = 'SharedKey $account:' . base64_encode(hash_hmac('sha256',
        $stringtosign, base64_decode($key), true));
    $session = curl_init("http://$account.queue.core.windows.net/tweet/messages");
    curl_setopt($session, CURLOPT_HTTPHEADER, array("x-ms-date:$date",
        "Authorization:$signature", "content-type:text/xml"));
    curl_setopt($session, CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($session, CURLOPT_POSTFIELDS, ''.base64_encode($text).'');
    return curl_exec($session);
}
enqueue_tweet($_POST['shouttext']);
header("Location:/?submitted=true");
?>

This handles a form post, puts the message on the tweet queue, and redirects back to the main page.

For a detailed explanation of how to get started with PHP on Windows Azure, I recommend reading Wade Wegner’s excellent post “Running a PHP Application on Windows Azure.”

.NET

Windows Azure worker roles today need to be written in .NET.  That’s the interface that our system understands to talk to your code.  However, as I described in my last post, there’s no reason the rest of your code needs to be written in .NET.  For Shoutomatic, I wrote a simple worker role that just launches a Java program and waits for it to exit.  As a bonus, the code logs everything the Java code outputs (stdout goes to the “Verbose” log, and stderr goes to the “Error” log).  All the rest of the worker is in Java code.  Here’s the complete .NET code for the worker.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using System.Text;
using Microsoft.ServiceHosting.ServiceRuntime;
using System.Diagnostics;
using System.IO;

namespace Shoutomatic_WorkerRole
{
    public class WorkerRole : RoleEntryPoint
    {
        public override void Start()
        {
            Process p = new Process()
            {
                StartInfo = new ProcessStartInfo(Path.Combine(
                    Environment.GetEnvironmentVariable("RoleRoot"),
                    @"jre\bin\java.exe"), @"-cp lib;lib\*;. worker")
                {
                    RedirectStandardInput = true,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    UseShellExecute = false,
                    WindowStyle = ProcessWindowStyle.Hidden,
                    WorkingDirectory = Environment.GetEnvironmentVariable("RoleRoot")
                }
            };

            p.ErrorDataReceived += (sender, e) => { RoleManager.WriteToLog("Error", e.Data); };

            p.OutputDataReceived += (sender, e) => { RoleManager.WriteToLog("Verbose", e.Data); };

            p.Start();
            p.BeginOutputReadLine();
            p.BeginErrorReadLine();
            p.WaitForExit();
            throw new Exception("Java process died!");
        }

        public override RoleStatus GetHealthStatus()
        {
            return RoleStatus.Healthy;
        }
    }
}

Java

Similarly to how the PHP runtime needs to package with your application to run in Windows Azure, to run Java code, you’ll need to package the Java Runtime Environment (JRE) with your application.  I did this by installing the JRE, copying the directory, and then uninstalling again.  (I couldn’t find a version of the JRE that didn’t require installation.)

The only interesting part of the Java code is the bit that talks to Windows Azure storage.  I wrote a reusable method called Sign, which signs HTTP requests with the appropriate storage authorization (but note the comments in the code; it’s not fully general).  I use that method twice, once when reading from the queue and once when deleting the queue message.

Here’s the entirety of the Sign method:

public static void Sign(HttpRequestBase request, String account, String key) throws Exception
{
    SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
    fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
    request.setHeader("x-ms-date", fmt.format(Calendar.getInstance().getTime()) + " GMT");

    StringBuilder sb = new StringBuilder();
    sb.append(request.getMethod().toUpperCase() + '\n'); // method
    sb.append('\n'); // md5 (optional)
    if (request.getFirstHeader("content-type") != null)
    {
        sb.append(request.getFirstHeader("content-type"));
    }
    sb.append('\n');
    sb.append('\n'); // TODO: date for table requests
    sb.append("x-ms-date:" + request.getFirstHeader("x-ms-date").getValue() + '\n'); // headers
    // TODO: the rest of the headers
    sb.append('/' + account + request.getURI().getPath()); // resource TODO: "?comp=..." if present

    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(new SecretKeySpec(Base64Coder.decode(key), "HmacSHA256"));
    request.setHeader("Authorization", "SharedKey " + account + ":" +
        new String(Base64Coder.encode(mac.doFinal(sb.toString().getBytes("UTF-8")))));
}

Here’s the code to fetch a message from the queue:

HttpClient httpclient = new DefaultHttpClient();
HttpGet get = new HttpGet("http://" + account + ".queue.core.windows.net/tweet/messages");
Sign(get, account, key);

and here’s the code to delete that message after processing.

HttpDelete delete = new HttpDelete("http://" + account + ".queue.core.windows.net/tweet/messages/"
    + id + "?popreceipt=" + popreceipt);
Sign(delete, account, key);
httpclient.execute(delete);

The rest of the code is just XML processing.  If you want to read it all, download the full source code.

Python

Lastly, I included in the source code a small Python script I wrote that uses winazurestorage.py, a library my coworker Sriram Krishnan originally created and I’ve recently been hacking on.  This script simply creates the tweet queue, which is required in the rest of the code.  I didn’t do this just to show off that there’s a Python library to talk to Windows Azure storage… this is actually the method I use to create tables and queues in the cloud when getting ready to deploy something.  I do it in a separate script to avoid doing this type of creation more than once.

Here’s the Python code:

# get winazurestorage.py from http://github.com/sriramk/winazurestorage
from winazurestorage import *

queue_server = 'queue.core.windows.net'
account = 'GETYOUROWNACCOUNT'
key = 'GETYOUROWNKEY'

queues = QueueStorage(queue_server, account, key)
print queues.create_queue('tweet') # should print 201

Summary

Shoutomatic was a really fun sample to write.  It took me about a day and a half (most of that time rediscovering Java) to code, and though the resulting app is fairly unimpressive, I think the flexibility of the platform really shows when you peak under the hood.

Those links again: