Publishing Real-time Updates from Apex with Pusher

The UK Department of Trade and Investment regularly runs trade missions to the Bay Area; yesterday the UK TI ‘Cloud Mission’, a group of British entrepreneurs working with cloud technology, visited us here at salesforce.com, and I had the pleasure of presenting our Social Enterprise Platform (database.com, Force.com and Heroku) to them, and chatting with some of them about their companies and technology.

Among the delegates was Max Williams, CEO of Pusher, a startup providing real-time updates (in the sense of WebSockets) as-a-service, and Max encouraged me to try Pusher out. How could I resist? Since there’s already a Heroku Pusher add-on, I decided to try pushing events from Apex code running on Force.com.

Looking at the Pusher docs, it’s all very straightforward – you drop some JavaScript into a web page that allows you to subscribe for updates on a channel. There’s a REST API for pushing events to the channel, and it took all of half an hour to get it working from Apex. It’s very similar to our Streaming API, except that an app explicitly publishes updates to a channel, rather than the platform generating events as data is modified.

Since the Apex implementation comprises all of 51 lines of code, rather than spinning up a GitHub project, I decided to just post it here, since it’s a nice little example of calling a REST API from Apex.

So, here’s the browser code, in a Visualforce Page:

<apex:page title="Pusher Demo">
  <script src="http://js.pusherapp.com/1.9/pusher.min.js" type="text/javascript">
  </script>
  <script type="text/javascript">
    var pusher = new Pusher('YOUR_PUSHER_AUTH_KEY');
    var channel = pusher.subscribe('test_channel');
    channel.bind('my_event', function(data) {
      alert(data);
    });
  </script>
</apex:page>

A real app would do something more interesting with the data than just pop up an alert, but you get the idea, I’m sure 🙂

The Apex Pusher class:

public class Pusher {
    public class PusherException extends Exception {
    }

    String authKey;
    String authSecret;
    String appid;

    public Pusher(String authKey, String authSecret, String appid) {
        this.authKey = authKey;
        this.authSecret = authSecret;
        this.appid = appid;
    }

    public void push(String channel, String name, String message) {
        String authTimestamp = String.valueOf(Datetime.now().getTime() / 1000);
        String authVersion = '1.0';
        String bodyMd5 = EncodingUtil.convertToHex(Crypto.generateDigest('MD5', 
          Blob.valueOf(message)));
        String path = '/apps/'+appid+'/channels/'+channel+'/events';

        String stringToSign = 'POST\n'+path+'\nauth_key='+authKey+
            '&auth_timestamp='+authTimestamp+'&auth_version='+authVersion+
            '&body_md5='+bodyMd5+'&name='+name;

        String authSignature =
            EncodingUtil.convertToHex(Crypto.generateMac('hmacSHA256',
              Blob.valueOf(stringToSign), Blob.valueOf(authSecret)));

        String endpoint = 'https://api.pusherapp.com'+path+
            '?auth_key='+authKey+
            '&auth_timestamp='+authTimestamp+
            '&auth_version='+authVersion+
            '&body_md5='+bodyMd5+
            '&name='+name+
            '&auth_signature='+authSignature;

        HttpRequest req = new HttpRequest(); 

        req.setMethod('POST');
        req.setEndpoint(endpoint);
        req.setBody(message);    

        Http http = new Http();

        HTTPResponse res = http.send(req);

        if (res.getStatusCode() != 202) {
            throw new PusherException(res.getStatusCode() + ' ' + res.getStatus());
        }
    }
}

Apex code to invoke the Pusher class:

Pusher myPusher = new Pusher('YOUR_PUSHER_AUTH_KEY', 'YOUR_PUSHER_AUTH_SECRET',
  'YOUR_PUSHER_APP_ID');

myPusher.push('test_channel', 'my_event', 'Hello from Apex');

And proof that it works 🙂

Although I just pushed a string here, you could just as easily push JSON data:

Pusher myPusher = new Pusher('YOUR_PUSHER_AUTH_KEY', 'YOUR_PUSHER_AUTH_SECRET',
 'YOUR_PUSHER_APP_ID');

Map<String,String> myMap = new Map<String,String>{
  'key1' => 'value1',
  'key2' => 'value2'
};

myPusher.push('test_channel', 'my_event', JSON.serialize(myMap));

So, go give Pusher a spin, and let us know in the comments if you’ve implemented an interesting API from Apex recently.

Published
October 28, 2011
Topics:

Leave your comments...

Publishing Real-time Updates from Apex with Pusher