Data Push with CFTHREAD and LiveCycle DS
There’s an old demonstration we used to show for Flex Data Services, now LiveCycle DS, that pushed data from the server to the client to be charted. More modern incarnations of this can still be found as samples included with LiveCycle DS and BlazeDS (Trader Desktop). The original incarnation was great fun to show, but I never really liked the workflow of getting the data feed started and stopped. Old habits die hard, and I used that dislike as an excuse to explore the new CFTHREAD feature of ColdFusion 8 in conjunction with the included LiveCycle DS instance for data push.
The original workflow required that you click on a link to start and stop the feed, before and after your demonstration respectively. This meant linking to pages that simply said “Feed Started”, and then going back to get the link to the actual application. You’d then link into the application and see the data being pushed from the server. Then when you were done, you’d go back once more, click the link to stop the feed, and then be presented with “Feed Stopped”. You were then free to go about your other demonstrations.
To be fair, there is something to be said for the modularity of the separate steps. If you’re trying to make a point that the data isn’t just being generated randomly on the client, it helps your viewers to take the distinctly separate steps.
From a usability perspective however, it seemed to cry out for an in-application “Start/Stop” button. Clicking on that button could call essentially the same server resource to manage the data feed, but without the page refresh. To delineate as to whether you wanted the feed started or stopped, you’d send along a different query string value. Then of course you’d want a variable to track the state of the feed such that you could toggle the query string value being sent to the server. If it all sounds wonderfully complex, it’s actually just a few lines of code.
public static const START_LABEL:String = "Start";
public static const START_RESPONSE:String = "Started";
public static const STOP_LABEL:String = "Stop";
public static const STOP_RESPONSE:String = "Stopped";
// Called by user interface gesture to start or stop service
// Makes HTTP call to manage thread
public function doStart( event:Event ):void
{
var params:Object = new Object();
// Create the query string values the endpoint expects
if( isRunning )
{
params.action = "stop";
} else {
params.action = "start";
}
// Send endpoint request with argument to manage thread
svcFeed.send( params );
}
// Called to handle thread management result
public function doResult( event:ResultEvent ):void
{
var response:String = event.result.@status;
if( response == START_RESPONSE )
{
btnStart.label = STOP_LABEL;
isRunning = true;
} else {
btnStart.label = START_LABEL;
isRunning = false;
}
}
...
<mx:HTTPService
id="svcFeed"
url="/messaging/feed.cfm"
resultFormat="e4x"
result="doResult( event )" />
<mx:Boolean id="isRunning">false</mx:Boolean>
<mx:Button id="btnStart" label="{START_LABEL}" click="doStart( event )" />
Now that the request is at the server, comes the requirement to start a thread that kicks data out to LiveCycle DS for delivery. This is actually two steps. The first involves handling the client request and managing the thread. Luckily ColdFusion 8 makes threading a pretty straightforward task. The CFTHREAD tag enclosed the functionality that you want to run in the separate thread. In this case a loop that generates a random number, sends that value to LiveCycle DS, and then waits some amount of time.
<cfapplication name="feed" />
<cfset Application.isRunning = TRUE />
<cfset result = "Started" />
<cfif action EQ "start">
<cfthread action="run" name="messages" priority="normal">
<cfset path = "/Users/khoyt/Desktop/thread.txt" />
<cfset thread.count = 0 />
<cfloop condition="Application.isRunning EQ TRUE">
<cfset cfevent = StructNew() />
<cfset success = StructInsert( cfevent, "body", RandRange( 0, 100 ) ) />
<cfset response = SendGatewayMessage( "Price Feed", cfevent ) />
<cfset thread.count = thread.count + 1 />
<cfset output = "Test: " & thread.count & " " & response />
<cffile action="write" addnewline="yes" file="#path#" output="#output#" />
<cfthread action="sleep" duration="500" />
</cfloop>
</cfthread>
</cfif>
<cfif action EQ "stop">
<cfset Application.isRunning = FALSE />
<cfset result = "Stopped" />
</cfif>
<feed status="<cfoutput>#result#</cfoutput>" />
The second step is actually the message gateway itself. We can send a ColdFusion gateway message using the SendGatewayMessage() function. It takes the name of the gateway to which we’re sending the message, and the value we’re sending as a structure. To create a gateway using the ColdFusion Adminstrator, we also need a ColdFusion Component (CFC) to handle the requests. There’s actually very little code in this CFC - just enough to extract the piece of data you want, and then returning that value in a form that will be recognized by the receiving clients.
<cfcomponent displayname="Price Feed" hint="Enables multiple clients to follow price feed data.">
<cffunction name="onIncomingMessage" output="no" access="public">
<cfargument name="CFEvent" type="struct" required="yes" />
<cfset mess = StructNew() />
<cfset success = StructInsert( mess, "body", CFEvent.body ) />
<cfreturn mess />
</cffunction>
</cfcomponent>
With the CFC in place, you can then setup the feed in the ColdFusion Administrator. The ID for the gateway can be whatever you’d like. The gateway type is a “DataServicesMessaging” gateway. The CFC path needs to point to the CFC we just created. A configuration file is optional, but I found it useful for better control of the gateway, so I adapted one from the included samples, mostly to map to a destination name, which I called “feed”. Since this is something I show regularly, I also choose an automatic startup mode.

Now that our data feed is turned on and is actively sending data to the client, the last step is simply to subscribe to the destination and chart the values. I recently turned Ryan Stewart onto use of the LiveCycle DS messaging features. My favorite quote from him after seeing this in action was that he “thought I had mad skills until he realized it was just two tags.” And as it just so happens, in our case, since we won’t be sending any data, all we have is one tag to subscribe to the data feed.
The Consumer class is the heart of listening for messages. It latches onto a server destination, in this case the “feed” we specified in our gateway configuration file, and listens for messages. When a message arrives, the Consumer instance raises an event that includes the message contents. From there you can take out whatever it is that you need, and do what you please. In our case, this means charting the data.
// Called when the application has finished rendering
public function doComplete( event:Event ):void
{
for( var d:Number = 0; d < 50; d++ )
{
prices.addItem( {value: 0} );
}
consumer.subscribe()
}
// Handle an incoming message
public function doMessage( event:MessageEvent ):void
{
var value:Number = new Number( event.message.body );
// Pop one value off to add another
// Presents the appearance of a scrolling chart
prices.removeItemAt( 0 );
prices.addItem( {value: value} );
// Show the numeric representation
txtPrice.text = value.toString();
}
...
<mx:Consumer
id="consumer"
destination="feed"
message="doMessage( event )" />
<mx:ArrayCollection id="prices" />
<mx:LineChart
id="crtPricing"
bottom="8"
left="8"
right="8"
top="8"
dataProvider="{prices}"
showDataTips="{!isRunning}"
dataTipFunction="pricingTip">
<mx:verticalAxis>
<mx:LinearAxis minimum="0" maximum="100" />
</mx:verticalAxis>
<mx:series>
<mx:Array>
<mx:LineSeries yField="value" />
</mx:Array>
</mx:series>
</mx:LineChart>
The caveat on the chart here is that if we just continually added data into an array, rather than present a captivating effect of sliding across the screen, the chart itself would just get smaller and smaller. To counter this I’ve gone the extra distance to first pre-populate the array with fifty (50) zero values. Then when new data comes in, the value is pushed onto the end of the array, and the first element is removed. In this fashion there’s only ever fifty elements being rendered by the chart. The result is visually impressive set of sliding data being delivered via push.

As usual the source code for this example is available for download. There’s a lot of moving pieces, but once you’ve got them all in place, it’s easy to expand on the system for your own requirements. I set a default wait time between messages to 500 milliseconds. For instant gratification, run this once out of the box, and then stop the thread. Modify the “feed.cfm” to ten (10) milliseconds and click the “Start/Stop” button again. Watch the data fly across the screen!
May 22nd, 2008 at 11:25 am
WE need to vote again
https://bugs.adobe.com/jira/browse/FP-247
I realy hope we can a USB communication in AIR and Flash Player
I can imagine many different type of applications which can communicate with different devices connected over USB, atleast my phone (over bluetooth or cable)…
It would be great if AIR and Flash player can have USB API for those things, so that we (developers) are not forced to write socket-servers and take care of installation etc..
May 24th, 2008 at 2:47 am
Having USB communication in AIR and Flash Player would be awesome !!
May 29th, 2008 at 1:05 am
thanks for the download code….usb communication would do a loads of good
May 29th, 2008 at 5:26 am
I invite Polish users on forum about Adobe Flex:
http://www.flexforum.pl
November 16th, 2008 at 7:33 am
What you think about new google style?
I like it
March 19th, 2009 at 3:59 am
Will the cfm used above the duplicate thread ? say when the cfm?action=start was executed before. Another client call the cfm?action=start again, will it create two thread in the system ? Any way to preserve or re-use only one single thread for querying the data ? I changed the above cfm to query database and sometime it reports “Run Client Storage Purge” on system log, more discussion can be found at http://www.webresourcesdepot.com/internet-usage-statistics-from-statowl/
May 5th, 2009 at 12:48 pm
Awesome post, thanks a lot for the info - I don’t usually like to add comments in these things but enjoyed the info. Keep up the good work, I bookmarked you!