After lunch today I decided to embark on a little test. Folks know - or should know - that ColdFusion ships with an incredibly powerful Server Monitor. If you haven't yet played with it, I highly encourage taking a look at Charlie Arehart's four part article over on the Adobe Developer connection for a review. One of the features he talks about is the Alerts system. For folks who don't want to spend all day staring at a computer screen (wait, people don't?), then the Alerts feature is a powerful way to have the monitor tell you when something is wrong as opposed to you keeping a constant eye on it. Alerts can do a variety of things, but when the built in functionality doesn't meet your needs, you can also have it run a CFC for you - and that's where this experiment began.
I began by creating a simple data services messaging gateway in my ColdFusion Administrator. This is a pretty deep topic (and some of my readers will share that it's not always as easy as I'm going to make out), but for the most part, I simply created the event gateway and I was done with that part. On the Flex side, it's a simple matter of setting up code to connect to that event gateway. So here for example is a one page Flex Mobile app that let's me send a message to the gateway.
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView" viewActivate="init(event)" xmlns:mx="library://ns.adobe.com/flex/mx"> <fx:Declarations>
<mx:Consumer id="mainConsumer" destination="ColdFusionGateway" message="msgResponder(event)" />
<mx:Producer id="mainProducer" destination="ColdFusionGateway" />
</fx:Declarations> <fx:Script>
<![CDATA[
import mx.messaging.ChannelSet;
import mx.messaging.channels.AMFChannel;
import mx.messaging.events.MessageEvent;
import mx.messaging.messages.AsyncMessage; import spark.events.ViewNavigatorEvent; private var pollChannel:AMFChannel = new AMFChannel("cf-polling-amf", "http://127.0.0.1/flex2gateway/cfamfpolling");
private var amfChannelSet:ChannelSet = new ChannelSet(); protected function init(event:ViewNavigatorEvent):void {
amfChannelSet.addChannel(pollChannel); mainConsumer.channelSet = amfChannelSet;
mainProducer.channelSet = amfChannelSet;
mainConsumer.subscribe();
} protected function msgResponder(event:MessageEvent):void
{
trace("got something");
debug.text += event.message.body.MESSAGE + '\n';
} protected function btnClick(event:MouseEvent):void
{
var msgString:String = inputText.text;
var msg:AsyncMessage = new AsyncMessage();
msg.body.MESSAGE = msgString;
msg.headers.gatewayid="MobileGateway1";
mainProducer.send(msg);
trace("sent "+msgString);
} ]]>
</fx:Script> <s:layout>
<s:VerticalLayout />
</s:layout> <s:TextInput id="inputText" />
<s:Button label="Send" click="btnClick(event)" /> <s:Label id="debug" />
</s:View>
On the ColdFUsion side, my CFC listened for messages and echoed them back:
remote struct function onIncomingMessage(required any event) {
writelog(file="application", text="gateway - #serializejson(event)#");
event.data.body.MESSAGE = "Sent back " & event.data.body.MESSAGE;
return event.data;
} }
component {
Notice I modify the message a bit just so I can see it working right. Here's a screen shot of it working.
To be clear - that's Flex talking to BlazeDS and ColdFusion on the server. If I had more devices, they all would have gotten the message. So - with me so far? Because here is where things get interesting.
As I mentioned before, the Server Monitor allows you to specify alerts, and allows you to specify CFCs to run. The docs, unfortunately, are completely lacking in useful information here. For example, they don't tell you how to specify the CFC. You can only use a file name (no directory!) and it must live in cfusioninstall\runtime\bin. (Thanks to Charlie's article on that.) And while you are told what methods your CFC must have, they don't tell you what the data looks like when your CFC is run. I ended up using writeDump to a directory just to see this! Here is an example:
Ok, so given that I've got some basic info there - I wrote my CFCs then to send messages to my event gateway.
remote function onAlertStart(struct alert) {
writelog(file="application", text="START - #serializejson(alert)#");
writedump(output="c:\raytest#createUUID()#.html", var=alert, format="html", label="START");
var s = {
destination="ColdFusionGateway",
body={
type=alert.alerttype,
message=alert.alertmessage,
start=true
}
}; sendGatewaymessage("MobileGateway1", s);
} remote function onAlertEnd(struct alert) {
writelog(file="application", text="END - #serializejson(alert)#");
writedump(output="c:\raytest#createUUID()#.html", var=alert, format="html", label="END"); var s = {
destination="ColdFusionGateway",
body={
type=alert.alerttype,
message=alert.alertmessage,
start=false
}
}; sendGatewaymessage("MobileGateway1", s); } }
component {
To be clear, the first two lines in both methods was just for testing purposes. You can see though that I take a few values from the alert and send it to the gateway. Now for the front end.
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="Server Monitor" viewActivate="init(event)" xmlns:mx="library://ns.adobe.com/flex/mx"> <fx:Declarations>
<mx:Consumer id="mainConsumer" destination="ColdFusionGateway" message="msgResponder(event)" />
<mx:Producer id="mainProducer" destination="ColdFusionGateway" />
</fx:Declarations> <fx:Style>
@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx"; .inactiveBtn {
backgroundColor:#c0c0c0;
} .activeBtn {
backgroundColor:red;
}
</fx:Style>
<fx:Script>
<![CDATA[
import mx.messaging.ChannelSet;
import mx.messaging.channels.AMFChannel;
import mx.messaging.events.MessageEvent;
import mx.messaging.messages.AsyncMessage; import spark.events.ViewNavigatorEvent; private var pollChannel:AMFChannel = new AMFChannel("cf-polling-amf", "http://192.168.1.108/flex2gateway/cfamfpolling");
private var amfChannelSet:ChannelSet = new ChannelSet(); protected function init(event:ViewNavigatorEvent):void {
amfChannelSet.addChannel(pollChannel); mainConsumer.channelSet = amfChannelSet;
mainProducer.channelSet = amfChannelSet;
mainConsumer.subscribe();
} protected function msgResponder(event:MessageEvent):void
{
trace("got something");
switch(event.message.body.TYPE) { case "Timeouts Alert": {
ssa.styleName = 'inactiveBtn';
usa.styleName = 'inactiveBtn';
jma.styleName = 'inactiveBtn';
if(event.message.body.START == "true") toa.styleName = 'activeBtn';
else toa.styleName = 'inactiveBtn';
break;
}
case "Slow Server Alert": {
if(event.message.body.START == "true") ssa.styleName = 'activeBtn';
else ssa.styleName = 'inactiveBtn';
usa.styleName = 'inactiveBtn';
jma.styleName = 'inactiveBtn';
toa.styleName = 'inactiveBtn';
}
}
//only log if starting
if(event.message.body.START == "true") messageArea.text = event.message.body.MESSAGE + '\n' + messageArea.text;
} ]]>
</fx:Script> <s:layout>
<s:VerticalLayout paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" verticalAlign="middle" horizontalAlign="center" />
</s:layout> <s:Label id="ssa" width="100%" height="60" styleName="inactiveBtn" text="Slow Server Alert"
textAlign="center" verticalAlign="middle"/> <s:Label id="toa" width="100%" height="60" styleName="inactiveBtn" text="Timeout Alert"
textAlign="center" verticalAlign="middle"/> <s:Label id="usa" width="100%" height="60" styleName="inactiveBtn" text="Unresponsive Server"
textAlign="center" verticalAlign="middle"/> <s:Label id="jma" width="100%" height="60" styleName="inactiveBtn" text="JVM Memory"
textAlign="center" verticalAlign="middle"/> <s:Label id="messageArea" width="100%" height="100%" /> </s:View>
Yeah - a bit more code there. It may help if I run it so you can see it first:
Basically - my application has 4 labels. When a message is received (notice this version never actually sends anything - I can get rid of the Producer) - I highlight various fields based on what the alert type was. I only wrote code for two of the types, but you can imagine what support for the other two would do. And what happens? Check out this horrible, shaky, YouTube video. I apologize in advance. Oh, and it's boring too. You will have to wait a bit to see the results. That isn't Flex or BlazeBS being slow, it's me trying to force a slow server alert on my box by reloading a very large Model-Glue site in 5-6 tabs at once.
Archived Comments
BlazeDS? What's that? You should have used ADEP Data Services! :D
I know I know. :) It's not my fault BlazeDS ships with CF. ;)
Ray,
By any chance have you tried to subscribe with a client id(like consumer.subscribe(id)). I am working on a web app and am using BlazeDS for longpolling. Every time I try to subscribe, I get a DuplicateSessionError.
I tracked down the point of error, and found it to be when I subscribe the consumer. Looking deeper, I found that when the flex app subscribes, it sends a clientId of null, which(I think) creates the duplicate session. There is an argument of clientId(defualts to null) in the function subscribe, but I tried entering what I think was the client ID and get a out of channels error.
Here is the general workflow of the application: sign in through RemoteObject, then subscribe the consumer with the appropriate subtopic. In your app the subscribe comes first, which creates the clientId for the rest of your session. There must be a way to subscribe with a specified session.
Let me know if you have any ideas on a way to approach this. Thanks for the help.
No sorry I have not seen this.
So just as an update. I figured it out. When I first set up the connection of Flex and BlazeDS, I used the project setting > Flex Server to set the location of my Server Configuration. This added the flag to the compiler, and I figured it would only use the setting on compile time. But in fact some of it is used at runtime as well.
When I changed the location of the server when I deployed to my production machine, it couldn't find the .xml files. And that caused the channelset to be set as null. It connected at without it being set. Anyways, I added the channel set like the example above and everything seems to be working.
Thanks for the example above. Compared everything and it helped me find my answer. Cheers.
Darn good find there - thanks for posting back.
Hi,
Sorry for asking this here, but is there a place I can look for answers regarding a CF to AIR connection error:
Send Failed
Channel.Connect.Failed error
NetConnection.Call.Failed: HTTP: Status 500
url: '[url]/flex2gateway/'
It only happens on occasion & the info I've found on the web leads to nowhere.
Many thanks for any clue,
Tomas
All I can recommend is checking the logs. 500 implies an error of some sort and something should be logged.
Hey, thanks. That has given me a new path to work in.
You know Raymond if you don't post the xml config files it"s of no use but your own. Seems nice but as you said setting up a messaging channel is no as easy as it sounds however it is not all that complicated neither the logic is to diagram the channels the names get confusing because it is always a combination my,cf,amf and polling spread across the 4 messaging,services,proxy and remotting config files. A few lines of code will get it working. Nice article but without the config files it's kinda useless really unless you want to spend one afternoon on this witch defeats the purpose of putting it out there...Thanks anyway
Sorry to disappoint you, Alexander. This post is almost 2 years old, so I don't exactly remember everything about it, but I *believe* I used the default settings for Blaze inside of CF. In other words, I don't think I edited any XML files. So be my guest to try it w/ the defaults and let us know.
Alexander, it is nice that you tried to comment, but without proper spelling, grammar, and punctuation, it's really kind of useless; because I cannot understand a thing you meant, which defeats the purpose of putting it out there. Thanks anyway.
@Jason Nice pretty funny. XD
Don`t worry not offended I have a sense of humor...
@Raymond Let me ask you, do you do consulting work?
I do a bit of side work, but I've got a full time gig with Adobe. I definitely don't do any Flex work though.