Since I wrapped GameOne, and begun work on GameTwo, I've been thinking a lot about messaging about AIR/ColdFusion applications. I had an idea today for a simple application. Imagine your web site sells widgets. Every time you sell a widget, your boss wants an alert. You could easily use email, but emails tend to get ignored. How about writing a quick AIR application instead?

I began on the server side. I didn't want to build a real ecommerce site for this demo. Instead I decided to simply simulate one person spending X amount of money and a certain time.

Data Services Messaging is fairly simple to setup. I went to my ColdFusion Administrator, Event Gateways, Gateway Instances, and created a new instance. I named it SimpleTest, used the DataServicesMessaging type, and pointed to a CFC in my code folder. This CFC is used when Flex sends data to the server. However, in my application, the client will never send data. A blank file then is fine for the CFC. However, if you try to skip making the file the administrator will get pissy with you.

Important: Note that you have to start the gateway instance. Even with the startup set to auto, which I would assume would mean 'start now too please', don't forget to hit that start icon after you create it.

At this point I can write a quick CFM that will simulate a web site sale.

<cffunction name="firstName" output="false"> <cfset var list = "Bob,Ray,Jacob,Noah,Lynn,Jeanne,Stacy,Mel,Darth,Luke,Anakin,Padme,Kirk,Frank,James,Hal,Ben,Lori,Kerry,Gorf"> <cfset var name = listGetAt(list, randRange(1, listLen(list)))> <cfreturn name> </cffunction> <cffunction name="lastName" output="false"> <cfset var list = "Camden,Smith,Nadel,Stroz,Pinkston,Sharp,Jonas,Vader,Palpatine,Break,Sneider"> <cfset var name = listGetAt(list, randRange(1, listLen(list)))> <cfreturn name> </cffunction>

<cftry> <cfset msg = StructNew()> <cfset msg.body = {}> <cfset msg.body["firstName"] = firstName()> <cfset msg.body["lastName"] = lastName()> <cfset msg.body["amount"] = "#randRange(1,100)#.#randRange(0,9)##randRange(0,9)#"> <cfset msg.body["timestamp"] = now()> <cfset msg.destination = "ColdFusionGateway">

<cfdump var="#msg#"> <cfset ret = SendGatewayMessage("simpletest", msg)>

<cfdump var="#ret#">

<cfcatch> <cfdump var="#cfcatch#"> </cfcatch> </cftry>

Those first two UDFs were simply me playing around. They return a random first and last name. The important part is the msg structure. I set the name, a random amount, and set a timestamp. (The docs say I don't have to, but frankly, the timeformat of the embedded timestamp wasn't something I knew how to parse.) Why? Those keys are entirely application dependent. If I were building a simple chat application, I may have just had a username and text property. For GameOne, one of my broadcasts includes stock data.

The destination, ColdFusionGateway, is actually specified in the XML files that ship with ColdFusion. I'll be honest and say I only kinda half-grok these files. I had to peek around in there when I played with BlazeDS locally, but all of the code here today runs on a 'stock' ColdFusion8. It has the ColdFusionGateway specified in the Flex XML files so for now, just go with it.

Once the data is set, I pass it to sendGatewayMessage. The first argument is the name of the event gateway I just created. The second is the structure defined in the file.

And that's it. Obviously this would be in a CFC method, you could imagine it being called after the order process is done.

The Flex/AIR side is even simpler. I used all of 2 files. (Ignoring the XML file AIR uses to create the build.) My main file contains one tag - a list. This list uses another file to handle rendering sales updates from the server. Here is the complete application file:

<?xml version="1.0" encoding="utf-8"?> <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">

<mx:Consumer id="consumer" message="handleMessage(event)" channelSet="{myChannelSet}" destination="ColdFusionGateway" />

<mx:Script> <![CDATA[ import mx.messaging.channels.AMFChannel; import mx.messaging.Channel; import mx.messaging.ChannelSet; import mx.controls.Alert; import mx.collections.ArrayCollection;
import mx.messaging.events.MessageEvent;

[Bindable] public var myAC:ArrayCollection = new ArrayCollection();

[Bindable] public var myChannelSet:ChannelSet

private function handleMessage(e:MessageEvent):void { var body:Object = e.message.body var newMsg:Object = new Object() newMsg.firstName = body.firstName newMsg.lastName = body.lastName newMsg.amount = new Number(body.amount) newMsg.timestamp = body.timestamp myAC.addItemAt(newMsg,0) }

private function init():void { myChannelSet = new ChannelSet() // var customChannel:Channel = new AMFChannel("my-cfamf","http://localhost/flex2gateway/cfamfpolling") var customChannel:Channel = new AMFChannel("my-cfamf","http://www.coldfusionjedi.com/flex2gateway/cfamfpolling") myChannelSet.addChannel(customChannel)

consumer.subscribe(); }

]]> </mx:Script>

<mx:List dataProvider="{myAC}" itemRenderer="ItemRenderer" height="100%" width="100%"/>

</mx:WindowedApplication>

I won't cover every line, but let's talk a bit about the important bits - specifically the consumer. Since this application doesn't send messages, I don't need a producer, only a consumer. The consumer uses a channel set, myChannelSet, that I define in my init function. You can see where I commented out my local address and replaced it with the 'production' value. (And yes, this can be done better via Ant.) My renderer isn't that complex, and could be sexier, but here ya go:

<?xml version="1.0" encoding="utf-8"?> <mx:Box xmlns:mx="http://www.adobe.com/2006/mxml" backgroundColor="#e0e0e0" cornerRadius="4" borderStyle="solid" borderThickness="0" paddingBottom="5" paddingTop="5" paddingLeft="5" paddingRight="5" height="90" dropShadowColor="#000000" dropShadowEnabled="true">

<mx:CurrencyFormatter id="fmtCurrency" precision="2"/> <mx:DateFormatter id="fmtDate" formatString="H:NN:SS" />

<mx:Text text="Purchaser: {data.firstName} {data.lastName}" /> <mx:Text text="Purchased: {fmtCurrency.format(data.amount)}" /> <mx:Text text="Time: {fmtDate.format(data.timestamp)} " /> </mx:Box>

You can download the AIR application below. You can force a fake sale by simply visiting: http://www.coldfusionjedi.com/demos/messagetest/test.cfm. What's cool is - if someone else does it, your AIR application will see it as well.

So this is probably a bit of a trivial example, but shoot, look how simple it is. I'm really excited about LCDS/BlazeDS!