Custom Event Gateways
Custom Event Gateways
Here you will find a short introduction into writing your own Event Gateway type.
Since you can write these in pure CFML (and Java when you want it), it is really simple to do.
There are 2 to 3 files you need to create:
- the Gateway CFC
- the Gateway Driver CFC
- A listener CFC
The Gateway CFC
This is the file which contains the action you want your gateway to do.
Also, it is the file which is instantiated by Lucee when the gateway starts.
You can take the following files as an example:
- {Lucee-install}/lib/lucee-server/context/gateway/lucee/extension/gateway/DirectoryWatcher.cfc
- {Lucee-install}/lib/lucee-server/context/gateway/lucee/extension/gateway/MailWatcher.cfc
The example code shown underneath is a modified version of the DirectoryWatcher.cfc, which, at time of writing, is in line for reviewing at the Lucee team.
By default, you need to have the following functions:
- An init function, which receives the necessary config data.
- A start function, which continues to run while variables.state="running".
- A stop and restart function.
- A getState function, which returns the current state of the gateway instance (running, stopping, stopped).
- A sendMessage function, which will be called when the CFML sendGatewayMessage function is used.
The following is all the code you need:
<cfcomponent output="no">
<cfset variables.logFileName="DirectoryWatcher" />
<cfset variables.state="stopped" />
<span class="nb"><cffunction</span> <span class="nv">name</span><span class="o">=</span><span class="s2">"init"</span> <span class="nv">access</span><span class="o">=</span><span class="s2">"public"</span> <span class="nv">output</span><span class="o">=</span><span class="s2">"no"</span> <span class="nv">returntype</span><span class="o">=</span><span class="s2">"void"</span><span class="nb">></span>
<span class="nb"><cfargument</span> <span class="nv">name</span><span class="o">=</span><span class="s2">"id"</span> <span class="nv">required</span><span class="o">=</span><span class="s2">"true"</span> <span class="nv">type</span><span class="o">=</span><span class="s2">"string"</span> <span class="o">/</span><span class="nb">></span>
<span class="nb"><cfargument</span> <span class="nv">name</span><span class="o">=</span><span class="s2">"config"</span> <span class="nv">required</span><span class="o">=</span><span class="s2">"true"</span> <span class="nv">type</span><span class="o">=</span><span class="s2">"struct"</span> <span class="k">default</span><span class="o">=</span><span class="s2">"</span><span class="s-Interp">#structNew()#</span><span class="s2">"</span> <span class="o">/</span><span class="nb">></span>
<span class="nb"><cfargument</span> <span class="nv">name</span><span class="o">=</span><span class="s2">"listener"</span> <span class="nv">required</span><span class="o">=</span><span class="s2">"true"</span> <span class="nv">type</span><span class="o">=</span><span class="s2">"component"</span> <span class="o">/</span><span class="nb">></span>
<span class="nb"><cfset</span> <span class="k">var</span> <span class="nv">cfcatch</span><span class="o">=</span><span class="s2">""</span> <span class="o">/</span><span class="nb">></span>
<span class="nb"></cffunction></span>
<span class="nb"><cffunction</span> <span class="nv">name</span><span class="o">=</span><span class="s2">"start"</span> <span class="nv">access</span><span class="o">=</span><span class="s2">"public"</span> <span class="nv">output</span><span class="o">=</span><span class="s2">"no"</span> <span class="nv">returntype</span><span class="o">=</span><span class="s2">"void"</span><span class="nb">></span>
<span class="nb"><cfset</span> <span class="nv">variables.state</span> <span class="o">=</span> <span class="s2">"running"</span> <span class="o">/</span><span class="nb">></span>
<span class="nb"><cfloop</span> <span class="nv">condition</span><span class="o">=</span><span class="s2">"variables.state EQ 'running'"</span><span class="nb">></span>
<span class="nb"><cftry></span>
<span class="nb"><cfset</span> <span class="nf">checkFileSize</span><span class="p">()</span> <span class="o">/</span><span class="nb">></span>
<span class="nb"><cfcatch</span> <span class="nv">type</span><span class="o">=</span><span class="s2">"any"</span><span class="nb">></span>
<span class="nb"><cfset</span> <span class="nf">variables.listener.onError</span><span class="p">(</span><span class="nv">cfcatch</span><span class="p">)</span> <span class="o">/</span><span class="nb">></span>
<span class="nb"></cfcatch></span>
<span class="nb"></cftry></span>
<span class="nb"></cfloop></span>
<span class="nb"></cffunction></span>
<span class="nb"><cffunction</span> <span class="nv">name</span><span class="o">=</span><span class="s2">"stop"</span> <span class="nv">access</span><span class="o">=</span><span class="s2">"public"</span> <span class="nv">output</span><span class="o">=</span><span class="s2">"no"</span> <span class="nv">returntype</span><span class="o">=</span><span class="s2">"void"</span><span class="nb">></span>
<span class="nb"><cfset</span> <span class="nv">variables.state</span> <span class="o">=</span> <span class="s2">"stopped"</span> <span class="o">/</span><span class="nb">></span>
<span class="nb"></cffunction></span>
<span class="nb"><cffunction</span> <span class="nv">name</span><span class="o">=</span><span class="s2">"restart"</span> <span class="nv">access</span><span class="o">=</span><span class="s2">"public"</span> <span class="nv">output</span><span class="o">=</span><span class="s2">"no"</span> <span class="nv">returntype</span><span class="o">=</span><span class="s2">"void"</span><span class="nb">></span>
<span class="nb"><cfset</span> <span class="nf">stop</span><span class="p">()</span> <span class="o">/</span><span class="nb">></span>
<span class="nb"><cfset</span> <span class="nf">start</span><span class="p">()</span> <span class="o">/</span><span class="nb">></span>
<span class="nb"></cffunction></span>
<span class="nb"><cffunction</span> <span class="nv">name</span><span class="o">=</span><span class="s2">"getState"</span> <span class="nv">access</span><span class="o">=</span><span class="s2">"public"</span> <span class="nv">output</span><span class="o">=</span><span class="s2">"no"</span> <span class="nv">returntype</span><span class="o">=</span><span class="s2">"string"</span><span class="nb">></span>
<span class="nb"><cfreturn</span> <span class="nv">variables.state</span> <span class="o">/</span><span class="nb">></span>
<span class="nb"></cffunction></span>
<span class="nb"><cffunction</span> <span class="nv">name</span><span class="o">=</span><span class="s2">"sendMessage"</span> <span class="nv">access</span><span class="o">=</span><span class="s2">"public"</span> <span class="nv">output</span><span class="o">=</span><span class="s2">"no"</span> <span class="nv">returntype</span><span class="o">=</span><span class="s2">"void"</span><span class="nb">></span>
<span class="nb"><cfargument</span> <span class="nv">name</span><span class="o">=</span><span class="s2">"data"</span> <span class="nv">required</span><span class="o">=</span><span class="s2">"true"</span> <span class="nv">type</span><span class="o">=</span><span class="s2">"any"</span> <span class="o">/</span><span class="nb">></span>
<span class="cm"><!--- handle incoming messages here ---></span>
<span class="nb"></cffunction></span>
<span class="nb"><cffunction</span> <span class="nv">name</span><span class="o">=</span><span class="s2">"checkFileSize"</span> <span class="nv">access</span><span class="o">=</span><span class="s2">"private"</span> <span class="nv">output</span><span class="o">=</span><span class="s2">"no"</span> <span class="nv">returntype</span><span class="o">=</span><span class="s2">"void"</span><span class="nb">></span>
<span class="nt"><cfquery</span> <span class="na">name=</span><span class="s">"qFile"</span> <span class="na">datasource=</span><span class="s">"#variables.config.datasource#"</span><span class="nt">></span>
SELECT *
FROM FILE
WHERE directory = "#getDirectoryFromPath(variables.config.filepath)#"
AND name = "#getFileFromPath(variables.config.filepath)#"
<span class="nt"></cfquery></span>
<span class="cm"><!--- call the listener CFC if the file size meets the minimum requirement ---></span>
<span class="nb"><cfif</span> <span class="nv">qFile.recordcount</span> <span class="o">and</span> <span class="nv">qFile.size</span> <span class="o">gte</span> <span class="nv">variables.config.minimalsize</span><span class="nb">></span>
<span class="nb"><cfset</span> <span class="nv">variables.listener</span><span class="p">[</span><span class="nv">variables.config.listenerFunction</span><span class="p">](</span><span class="nv">qFile.directory</span> <span class="o">&</span> <span class="nv">server.separator.file</span> <span class="o">&</span> <span class="nv">qFile.name</span><span class="p">,</span> <span class="nv">qFile.size</span><span class="p">)</span> <span class="o">/</span><span class="nb">></span>
<span class="nb"></cfif></span>
<span class="nb"></cffunction></span>
</cfcomponent>
We will save the file as {Lucee-install}/lib/lucee-server/context/gateway/filesizechecker/FileSizeWatcher.cfc.
The variables.listener and variables.config variables did not just come falling from the sky; instead, it was saved to the variables scope in the init() function.
Lastly, we need to create the Gateway driver. We can use the Gateway driver code shown before, and then save it as {Lucee-install}/lib/lucee-server/context/admin/gdriver/FileSizeWatcher.cfc.
Now we are almost good to go! We do need to restart Lucee to have it pick up the new Gateway driver. So just go to the server admin, click on the menu-item "Restart", and then hit the "Restart Lucee" button.
We can add an instance of our new Gateway type now! You can do it by using cfadmin like this:
<cfadmin action="updateGatewayEntry" type="server" password="server-admin-password"
startupMode="automatic"
id="zipLargeLogFiles"
class=""
cfcpath="filesizechecker.FileSizeWatcher"
listenerCfcPath="filesizechecker.FileBackuper"
custom="# {
filepath = "C:/mysite/logs/failedlogins.log",
listenerFunction = "onBigFilesize",
minimalsize = 100000,
interval = 60000
} #"
readOnly="false"
/>
Interval: time in milliseconds to wait between each check. Minimalsize: the minimum filesize in bytes.
After executing the cfadmin code, or going through the admin screens, you should now have an instance of your own Event Gateway type running!
When creating a Socket gateway or an Instant messaging gateway, you will need to do a bit more coding, but hopefully this instruction helped you out!