I got an email a while back from a user who was having issues with ColdFusion's Asynchronous Gateway code. It had been a while since I used it so I thought it might be nice to refresh my own memory, and provide a simple guide for users on how to work with it. I ran into a few issues that I bet have tricked up my own readers. (Hopefully we will see Damon Cooper's cfthread get rolled into the core product to make this a lot easier.) This guide will not cover everything you can do with the Asynch gateway, but it should be enough to get you going.
So let's talk. When using the Asynch Gateway you are coordinating and working with two different files. You have one file that makes the requests. You can think of this as the customer asking for orders. The other file is a CFC and receives the requests via the Asynch Gateway. You can think of this as the wageslave behind the counter making your Happy Meal. To think of it another way - you have one file that fire offs the events and another file handling the (potentially) slower request in an asynchronous matter. All that means is that ColdFusion doesn't have to wait around for the slow process to end.
The very first thing you need to do is create the CFC. You do not need to write any code in this CFC, but you must at least have the file. My file is located under my web root at testingzone/test.cfc. Next, you need to go into your ColdFusion Administrator. Under Event Gateways click Gateway Instances. Click Add Gateway Instance. A form will pop up. You are concerned about the Gateway ID and the CFC Path. The Gateway ID is simply a label, nothing more. So for example, if your site is CIASecretPrisons.com, you may want to give your gateway a name related to the site: CIASecretPrison_AsynchGateway. That's a bit long but you get the idea.
The CFC path is simply the path to the CFC you are using. You can actually change that at run time, but I'll leave that to the next blog entry. Leave the configuration file field alone and keep Startup Mode to automatic. This just ensures your gateway will run when ColdFusion restarts. Make your gateway and then start it by clicking the green start icon. Click the screen shot below for a larger version of the settings that I used.
Now open up your CFC in your favorite text editor. In order to correctly listen to the Asynch Gateway, your CFC needs to have a method named onIncomingMessage. This method is sent one argument, a struct, that contains the a set of data. What data? It depends. Let's say you were using the gateway to send mail. Well then your data may contain an email address and a message. I'm using a modified version of the example from the docs which was a basic logger:
<cfcomponent output="false">
<cffunction name="onIncomingMessage" output="false" returnType="void">
<cfargument name="cfEvent" type="struct" required="yes">
<cfscript>
if(not structKeyExists(arguments.cfEvent.data, "file")) arguments.cfEvent.Data.file="defaultEventLog";
if(not structKeyExists(arguments.cfEvent.data, "type")) arguments.cfEvent.Data.type="info";
</cfscript>
<cfif structKeyExists(arguments.cfevent.data, "message")>
<cflog text="#arguments.cfEvent.data.message#" file="#arguments.cfEvent.data.file#"
type="#arguments.cfEvent.data.type#" thread="yes" date="yes" time="yes" application="yes">
</cfif>
</cffunction>
</cfcomponent>
The code should be pretty trivial. The data passed to the gateway exists in a key named "data" under the cfEvent argument passed in. You see some basic checking for a file and type attribute, but outside of that the code simply logs a value named "message".
Let's now switch to the CFM file that will call this CFC. Let me show the code first and then I'll explain it.
<cfscript>
status = false;
props = structNew();
props.message = "Replace me with a variable with data to log";
status = SendGatewayMessage("Asynch CF", props);
if (status IS true) WriteOutput('Event Message "#props.Message#" has been sent.');
</cfscript>
The code begins by initializing a status variable. This is used as a default flag for what our gateway returns. I then create my data. Again - what you use here will depend on what you are doing with your code. Our logger can take a few things like file and type, but it really just needs a message. Next the SendGatewayMessage function is used. This lets ColdFusion communicate with the Event Gateway system. I know - it sounds complicated. Just think of it as a message. In this case, I used the gateway ID I had created "Asynch CF". The second argument is the structure of data. Just so you know - there are some special keys you can use within that structure to change how things work behind the scenes. I'll discuss that in the next entry.
Guess what - we're done. If you run this in your browser you will see the message logged. By itself this isn't too sexy. But consider modifying your onIncomingMessage like so:
<cfset thread = CreateObject("java", "java.lang.Thread")>
<cfset thread.sleep(5000)>
This will cause your CFC to pause for five seconds. Rerun your CFM and you will notice no delay. The only delay you have is in actual logging of the message.
In my next blog entry I'll show a more real world example based on the code I wrote as a proof of concept for cfthread. I'll also discuss those "other" keys I alluded to earlier.
Something very important to remember: It is possible that your code may contain an error (shocking, I know). What happens when your CFC screws up? You don't get an error on your CFM page. In fact, the status message will still be true. If your CFC doesn't seem to be working correctly, check the eventgateway.log. I added a simple syntax error to my CFC and it was nicely logged:
"Error", "Thread-21", "09/07/06", "08:33:05", ,"Error invoking CFC for gateway Asynch CF: Variable structKeyExistsTheSmithsRule is undefined. "
Archived Comments
Gee, it would be nice if there was a permanent guide to Async Gateways somewhere......
I will take my entries and make a PDF for them and link to it (when all done).
I was just teasing, I knew you would. This is very well written and easy to understand (as is all your stuff). Thank you, I'm looking forward to the next entry!
This comment has nothing to do with this current post whatsoever.
But just wanted to say thanks Ray, for fixing your blog layout to work with lower resolution monitors.
This was the first day that i didn't have to scroll all the way to the bottom to read the first article of the day.
There are many other great CF blogs out there that have that same problem.
Do you mind sharing, what it was you did to fix the CSS/layout to make it work on the lower-res?
Thanks,
Ali
That wasn't me, but Scott Stroz. I will ping him to see if he can share his fix as I don't remember it. He is on vacation this week, so you may need to remind me next week.
Very cool feature. I have implemented similiar items with AJAX and remote cfcs, but still good to know other capabilities of CF that I have not explored yet.
I did a 2 page step by step install of an async gateway in the first issue of the Fusion Authority Quarterly Update. I'll make sure it gets to the website within the next week.
I guess I better hurry up then and finish my next part. ;)
Seriously - the more examples the better, wouldn't you agree? Please post back here with the URL.
Agree? Of course I agree. I've been asking Adobe to make Async gateways a part of every version of CF (even if limited in pro). They are fantastic and makes someone really think about how code works and how it can be 'cut up'.
I have 2 async gateways running on HoF. The first is a logging gateway and let me tell you it is super fast and cool. If I had to do these logging operations inside a user session it would bog down the user. Sending a small packet of info to a async gateway to be precessed and logged is a speed savings that can be measured in an order of magnitude.
The second is a digest sender for my lists (which I just fixed - hidden, non-vared variable in a cached environment is deadly). When you have to process 50+ lists with about 10 digests options per list on an hourly basis, an async gateway is perfect.
Let me get something up on Blog of Fusion about the digest gateway now and then get the FAQU article up asap.
(as a side note, I'm revising my old list of CF functions into some better docs. I'll send you that as soon as I'm done as well.)
Ray, a couple of typo's here that may confuse others...
Initially you said the Gateway ID was to be called "CIASecretPrison_AsynchGateway" but your screen shot and SendGatewayMessage() code sample shows "Asynch CF".
:-)
Well, I had said you should use an appropriate name, meaning, just like you would for a DSN.
Yes, I appreciate that but as I said it may confuse people new to the topic if you're not consistent in your doc's.
Fair point. My followup just uses one name throughout.
Ray, did you banish me from your blog? I _swear_ that I had subscribed to this post, but I didn't get any of the emails.
Not at all. Don't forget that EVERYTIME you comment you MUST cick Subscribe, which is a pain, but I wanted to err on the side of not sending email.
When I was playing with these I found using the jabberbot cfc that Ben Forta talks about here: http://www.forta.com/blog/i... was a fun way of debugging the async calls.
I just used jabarbot to chat to my google chat client with debug messages. Quite surreal especially when it takes a while for the call to complete and you server starts chatting to you seemingly randomly.
Tell me more about this thread sleep. Is that all that has to be done to implement it? Is there anything that needs to be borne in mind. Are there any drawbacks of this implementation?
I don't quite get your question. I used thread.sleep to simply "fake" a slow process.
I'm relatively new with CF, and am trying to implement an asynchronous gateway for the first time. I copied your CFC (using the name LargeApplicationPDFGenerator - the name of the CFC I'll eventually implement), added the gateway instance, then invoked it. I am consistently getting the following exception.
Error [Thread-11] - Error invoking CFC for gateway LargeApplicationPDFGenerator: Variable form is undefined.
I have not defined or used a variable named "form". Is this error referring to the document? Any help you provide would be greatly appreciated.
What CFC Jerry? That doesn't look like mine.
Never mind. The CFC worked when I moved it to the gateway subdirectory of the ColdFusion server installation (with the provided gateway examples). I was unaware there are restrictions on the CFC location.
Ray, I have found your article quite helpful. Thanks for your quick response!
Hello,
It seems that I am being 'tricked' as you say.
I'm getting my feet wet using Async Gateways with CF8, and I have created a loop which calls the gateway X amount of times.
The gateway request then logs the data, however, it is logging X requests, but only the actual data from the last request in the loop. Thus, it's populating the table with duplicate records of the last request X amount of time from the loop.
I may be mistaken, but it seems as though it's sharing the same thread as it is being overwritten with the latest request, rather than creating individual threads for each request. If so, do I need to add something like a timeout function or use cfthread?
Any feedback is welcomed!
Thanks,
Ryan
I'd like to revisit Jerry Simpson's comment on 9/17/07 1:20 PM. I have also confirmed this problem for seperate drives even on the installation server. Why is the gateway limiting us to storing our CFCs in the gateway folder for the CF Server installation drive? For various reasons we do not store project files on it. Can this be edited? Your help is appreciated. Thanks.
Does CF have permissions to read where you wanted to store the file? When I say "CF", I mean the user CF runs as. If on Windows, you have to run the service as a user, not as system.
Ray,
Yes. It is the location for all my project CFCs and they work fine. Also my barebones sample gateway CFC does not throw the error if located on the installation server install drive. My target directory for directorywatcher also has permissions even though it is on another server (used a UNC). I got errors related to it until I gave the CF user full permissions to that server.
Brian,
I found out that I could keep the gateway code in the gateway folder, but reference components that are available via a mapping.
Jerry
It may be then just be a bug with the code used for the gateway. I'd file a bug report (www.adobe.com/go/wish).
I was wondering if you had a means of dealing with the request scope within the actually call itself. I have a method that I want the Asychronous Gateway to call, which leverages the request scope in several places. Thoughts?
To be honest, I haven't used this gateway since CF added cfthread.
This unrelated, and its a question.
Can one use javascript to invoke a coldfusion cfc and if so will this "$.get('myEntityWS.cfc?method=updateDescription&value=someValue');" do?
Arthur, this comment isn't really on topic for this blog. I did respond to your email on it though.