Wednesday, October 15, 2008

Generic ColdSpring Gateway Service Code

I've been digging into ColdFusion's Gateway's of late and created a simple generic Gateway that works great with coldspring (GatewayService.cfc). The beauty is the simplicity. With this process, you can send any 'message' to the gateway and have the application run it asynchronously. The message you send will be any unit of work in your coldspring model. For example if you have an XmlManager object that has a method of 'generateRSSFeeds', you can pass it a Structure of data as an argumentcollection, as well as the object name and method name and have coldspring execute that for you w/in your Gateway.

<cffunction name="onIncomingMessage" output="false" access="public" returntype="void" hint="Generic Method that will execute any method on the Service Factory - Struct: beanname (coldspring Bean to call), methodname (method to call on cs bean), argumentcollection (data passed as argument collection to the method)">
<cfargument name="CFEvent" type="struct" required="yes">

<cfset var data =>
<cfset var errorMessage = ''/>

<cfif not structkeyexists(data,"args")>
<cfset data.args = StructNew()>

args: argumentcollection

<cfinvoke component="#application.serviceFactory.getBean(data.beanname)#"
<cfcatch type="any">
<cfset errorMessage = cfcatch.Message/>
<cfif structkeyexists(data.args,"debug")>
<cflog application="true" file="cfmlgateway" text="CF Gateway Called: bean: #data.beanname# method: #data.methodname# data: #structKeylist(data.args)# #errorMessage#">

<cfreturn />

There is also a 'server.init.cfm' feature that sets up this gateway through the ColdFusion Admin API and makes sure that it exists in the server that's running your application - and any changes to the gateway it will determine and recreate it when needed. The gateway is a CFML gateway and is given a name that uniquely identifies it (just like a DSN). Note: You must call the login method of the cfide.adminapi.administrator before you are allowed access to do this.

<cfset Local.gatewaypath = expandpath('.') & '\model\gateway\GatewayService.cfc'/>
<cfset Local.eventGateway = createObject( "component", "cfide.adminapi.eventgateway")/>
<cfset Local.gatewayFound = false/>

<!--- query the gateway Instances to find a match and if not create it --->
<cfset Local.tmpArray = Local.eventGateway.getGatewayInstances()/>

<cfloop from="1" to="#arraylen(Local.tmpArray)#" index="Local.i">
<cfif Local.tmpArray[Local.i].gatewayid eq 'mygatewayname'>
<cfset Local.gatewayFound = true/>

<!--- check and see if the cfcpath is the same- if not delete it and recreate it --->
<cfif Local.tmpArray[Local.i].cfcpaths[1] neq Local.gatewaypath>
<cfset Local.eventGateway.deleteGatewayInstance(Local.tmpArray[Local.i].gatewayid)>
<cfset Local.gatewayFound = false/>

<cfif not Local.gatewayFound>

<!--- Create gateway instance --->
<cfset Local.tmpArray = ArrayNew(1) />
<cfset Local.tmpArray[1] = Local.gatewaypath/>

<cfset Local.eventGateway.setGatewayInstance('mygatewayname','CFML',Local.tmpArray,'','auto') />
<cfset Local.eventGateway.startGatewayService() />

<cfset Local.eventGateway.startGatewayInstance('mygatewayname') />
<cflog log="Application" text="Gateway mygatewayname Created" type="information"/>

There is also a 'gatewaymanager.cfc' that simply is the API to all your gateway service calls, in which you can add on other things like debugging and error handling etc if needed.

<cffunction name="sendMessage" output="false" access="public" returntype="boolean" hint="Returns true of false if the message was successfully sent to the gateway">
<cfargument name="beanname" type="string" required="true"/>
<cfargument name="methodname" type="string" required="true"/>
<cfargument name="args" type="struct" required="false" default="#StructNew()#"/>

<cfset var boolReturn = SendGatewayMessage('mygatewayname',arguments)>

<cfreturn boolReturn />

With great power come great responsibility - please use wisely!