Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at the New York ColdFusion User Group (Jun. 2008) with:

PubNub.cfc - A ColdFusion Wrapper For The PubNub Realtime Messaging Platform

By Ben Nadel on
Tags: ColdFusion

Last week, I started playing around with PubNub - a cloud-based, realtime messaging platform. In my first post, I explored the ability for client applications to send messages to each other (using JavaScript) without the aid of any server-side proxy. This bi-directional communication approach required the PubNub "publish key" to be displayed in public. In a more secure application, you'd want to keep your publish key on the server and have the clients publish through a secure, server-side endpoint. Of course, before I could explore that approach, I needed to create a ColdFusion component wrapper for the PubNub RESTful API.

Over the weekend, I wrote PubNub.cfc. Since the PubNub RESTful API is quite small in scope, the PubNub.cfc wrapper only consists of a few public methods:

  • init( ... ). The init() method has an optional "secret key" argument. This is used to sign the resource used in the Publish requests. This is only used in enterprise accounts and its use or misuse will make absolutely no difference in non-enterprise accounts (ie. it does nothing, it hurts nothing - it won't fail).
  • publish( channel, message ). This method posts a message to the given channel. The message is implicitly serialized as JSON before it is posted to the channel.
  • subscribe( channel [, timeToken] ). This method gets all the messages posted to the given channel since the given time token. If no time token is supplied, it seems that an empty array is always returned. However, in that response is a new time token that can be used on subsequent subscribe() requests.
  • subscribeAsync( channel, callback [, timeout] ). This method works like the subscribe() method; however, rather than returning the result, the subscribe() method is invoked repeatedly within an asynchronous CFThread body. All retrieved messages are passed off to the given callback argument. Only one channel can be subscribed at any one time. Subsequent calls to this method will automatically unsubscribe the previous channel.
  • unsubscribe(). This method simply kills the asynchronously subscribed CFThread.
  • history( channel, limit ). This gets the most recently published messages (as defined by the limit).
  • time( [returnAsBigInt] ). This returns a normalized time from the PubNub server (in milliseconds). The PubNub.cfc returns this value as a string due to the limitations of integer size in ColdFusion. It can optionally be returned as a Java BigInteger instance.

Ok, now that you see the API, let's take a look at the PubNub.cfc ColdFusion component wrapper in action. For this simple demo, I'm not going to show the asynchronous subscribe - perhaps another time (its a bit more involved and harder to demonstrate).

  • <!---
  • Create an instance of our PubNub component with DEMO credentials.
  • This is an "account" that all people can use (but there is no
  • privacy on it since everyone knows the keys).
  • --->
  • <cfset pubnub = createObject( "component", "com.PubNub" ).init(
  • publishKey = "demo",
  • subscribeKey = "demo"
  • ) />
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!---
  • Get the current time token from PubNub. It uses a normalized,
  • centralized timeline and can give you a time token in the number
  • of milliseconds since the "zero" date. This will be used to for
  • the subscribe method call farther down.
  • --->
  • <cfset timeToken = pubnub.time() />
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!--- Create a message to publish. --->
  • <cfset message = {} />
  • <cfset message[ "uuid" ] = createUUID() />
  • <cfset message[ "message" ] = "This is a test message from ColdFusion (#timeFormat( now(), 'hh:mm:ss TT' )#)." />
  •  
  • <!--- Publish the message. --->
  • <cfset response = pubnub.publish(
  • channel = "coldfusion:hello_world",
  • message = message
  • ) />
  •  
  • <h2>
  • Publish Response
  • </h2>
  •  
  • <cfdump
  • var="#response#"
  • label="Publish Response"
  • />
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!---
  • Get the history for our channel. This will return the most
  • recent [limit] items in time-ascending order. That is, the
  • oldest of the group is first, the newest of the group is last
  • in the array.
  • --->
  • <cfset response = pubnub.history(
  • channel = "coldfusion:hello_world",
  • limit = 5
  • ) />
  •  
  • <h2>
  • History Response
  • </h2>
  •  
  • <cfdump
  • var="#response#"
  • label="History Response"
  • />
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!---
  • Subscribe the given channel using the retreived time token
  • from above. This will return all the messages posted to the
  • channel since the given time. This should include the broadcast
  • message from above.
  • --->
  • <cfset response = pubnub.subscribe(
  • channel = "coldfusion:hello_world",
  • timeToken = timeToken
  • ) />
  •  
  • <h2>
  • Subscribe Response
  • </h2>
  •  
  • <cfdump
  • var="#response#"
  • label="Subscribe Response"
  • />

Note that we are using the demo/demo credentials. This is the PubNub demo account which anyone can jump in and start using. This creates an extremely low barrier of entry to the API (not to mention cost-free); but, of course, it creates no privacy as everyone can access the channels on the demo account.

When we run the above code, we get the following page output:


 
 
 

 
 PubNub.cfc ColdFusion component wrapper for the PubNub realtime messaging platform. 
 
 
 

As you can see, the message was published to the channel and then retrieved both as part of a history request and a subscribe request. Notice that the subscribe request also returns a timeToken. This is so that subsequent requests to the subscribe() API can get the latest messages.

PubNub.cfc ColdFusion Component on GitHub

The code for the PubNub.cfc ColdFusion component is currently on my GitHub account. I'm also working on some various demos, including one for asynchronous subscribe. The PubNub team has given me the go-ahead to push this code to the core PubNub repository; so, whenever I figure out how the heck to do that, I will.




Reader Comments

@Tim,

Once you have a CFC wrapper for the API, I think the differences are going to be minimal if you are going from the server to the client. From what I remember, the digital signing of the Pusher request was more complicated (using SHA-256 hashing).

However, Pusher allows you to create "Apps" in your dashboard with individual API keys; PubNub appears to only have one set of keys (using channels as the primary differentiators).

Also, PubNub allows client-to-client (with a public key), which I don't believe I've seen in any of the other Realtime options.

But really, my understanding of the Realtime SaaS platforms is more experimental than anything else. I've not done anything with any production level integration yet.

Reply to this Comment

I am aware of ColdFusion's Event Gateways: They handle incoming realtime messages with a dedicated 'onIncomingMessage' event method.
The PubNub.cfc deals with incoming realtime messages like so:
- Method 'subscribeAsync': In an asynchronous CFThread body, all retrieved messages are passed off to the given callback function argument.

Questions:
- Is PubNub.cfc's 'subscribeAsync' approach comparable to a dedicated 'onIncomingMessage' event method ?
- To deal with several PubNub channels on the server-side is no esoteric use-case ! In order to tackle that use case with the PubNub.cfc: Is it appropriate to have one dedicated PubNub.cfc instance per channel ?

Reply to this Comment

I am now trying to integrate the PubNub.cfc into the ColdBox Framework; the main problem I encounter is:
- An asynchronous subscription seems to last only for a few seconds; this is not what one calls sustainable ...

I have settings like so:
- Doing the subscription: pubnub.subscribeAsync(timeout = 6000000)
- ColdBox App settings:
<cfset this.sessionManagement = true>
<cfset this.sessionTimeout = createTimeSpan(0,0,30,0)>
<cfset this.applicationTimeout = createTimeSpan(0,0,30,0)>

I have no idea why the CFThread of the PubNub subscribeAsync method only serves me for a few seconds; since I am very much after a sustainable/usable PubNub.cfc, I am grateful for hints how to make it so !

Reply to this Comment

I just wanted to add my newest insight: In order to control the (request) timeout of the endless cfloop used in 'subscribeAsync', I have to do the following:
- Add a cfsetting directive to the .cfm page invoking 'subscribeAsync' like so:
- <cfsetting requesttimeout="240">
...
... subscribeAsync(...) ...

This seems to work for me; in this particular case, the subscribeAsync thread would listen for 4 minutes.

Another story is: How to sustainably kill subscribeAsync threads ...

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.