Event Gateway issues

I am having a little difficulty understanding event gateways in Lucee. I believe this is because the documentation might possibly be out of date. For example in the documentation it says you should be able to create event gateways inside of the server configuration but I do not see an option for that, it seems to only exist inside of the web configuration. Is that supposed to be the case or do I have something configured incorrectly?

Another thing I noticed is that in the documentation it states that the CFCs are stored inside “{Lucee-web}/lucee/gateway/lucee/extension/gateway/” but what I found is that they are actually inside of “{Lucee-web}/lucee/components/lucee/extension/gateway/” I am assuming this was changed at some point and is probably just needs to be corrected but was hoping someone can confirm.

Something else I noticed which might be a bug is that if I make changes to my custom listener I have to delete the listener and re-create it before the changes are active. In the documentation it says to stop the gateway and then restart it but in my testing this does nothing, it is possible that maybe I have missed something.

Are you referring to this documentation via @gert

https://lucee.daemonite.io/t/lucee-event-gateways/2066

If the documentation needs updating it would be great to get those corrections in place.

This is actually the documentation I was using http://docs.lucee.org/guides/Various/event-gateway.html

I plan to contribute to the documentation soon because there isn’t very much information on how to implement a Java based event gateway which is what I have been trying to tackle. I finally was able to piece something together so I will write up my steps so it may help others.

2 Likes

I’m actually trying to make heads and tails of this functionality (GitHub - jbvanzuylen/cf-jms-gateway: JMS Gateways for ColdFusion) at the moment, because we have an Apache MQ eventgateway in ACF working that needs to be replicated in Lucee. Would be good to know what yu discover and I’ll share if I get the aforementioned working (and hopefully working as the ACF eventgateway does, so we do not need to change any code).

If you are looking for a Java implementation this is what I have created for a skeleton file:

import java.util.Map;

import lucee.runtime.gateway.Gateway;
import lucee.runtime.gateway.GatewayEngine;

public class LuceeSkeleton implements Gateway {
	
	private String id;
	private int state=Gateway.STOPPED;
	private String cfcPath;
	private GatewayEngine engine;

	@Override
	public void init(GatewayEngine engine, String id, String cfcPath, Map config) {
		this.engine=engine;
		this.cfcPath=cfcPath;
		this.id=id;
		engine.log(this,GatewayEngine.LOGLEVEL_INFO,"initializing");
	}
	
	@Override
	public void doRestart() {
		doStop();
		doStart();
		
	}

	@Override
	public void doStart() {
		state = STARTING;
		engine.log(this,GatewayEngine.LOGLEVEL_INFO,"starting");
		
		state = RUNNING;
		engine.log(this,GatewayEngine.LOGLEVEL_INFO,"running");
	}
	
	@Override
	public void doStop() {
		state = STOPPING;
		engine.log(this,GatewayEngine.LOGLEVEL_INFO,"stopping");
		
		state = STOPPED;
		
	}

	@Override
	public Object getHelper() {
		return null;
	}

	@Override
	public String getId() {
		return id;
	}
	
	 @Override
	public int getState() {
		return state;
	 }

	@Override
	public String sendMessage(Map _data) {
		String status="OK";

		return status;
	}


}

What I did is export the above code as a JAR and place it inside of the {lucee-root}/lucee-server/context/lib/ and restart the server. You should see the JAR under the section Info → Bundle (jar) in the Lucee web/server admin. You then need to create a Gateway driver file and place it inside of the directory {lucee-root}/lucee-server/context/context/admin/gdriver/

To get started with the Gateway driver I used the skeleton file from this repo

This should be enough to get you started I think. Let me know if you have any other questions and I can try to help. I am just learning Java so I am always open to suggestions for code improvement.

1 Like

I have gotten the above code to display two new types of event gateways in the Web admin > Services > Event gateway, but when they are added the mysteriously disappear. There is no way to find them in the Admin interface as far as I can see to change settings or stop the gateways.

Anyone an idea where this gem is hidden in Lucee? Or is it just an error?

In my experience it is an error with something in the JAR file. Check the logs and see if there is anything listed in them.

What does your Driver file look like? You need to change it to point at the class name of the JAR file. Look here http://docs.lucee.org/guides/Various/event-gateway/create-event-gateway.html#gateway-driver-functions the relevant parts are the first two functions. getClass() and getCFCPath()

Using the library of cf-jms-gateway there is no need to point to any JAR-file is there? It should all be in the settings in the Lucee Web admin. In there I point to a basic class for Apache MQ and the path to the CFC where all the magic happens in ACF. But there is no description in the library of Jean-Bernard that describes what should be put where etc. So for now I am at a loss and wondering if this is an error in Lucee regarding the whole event gateway implementation, or lack thereof. I would really love to see something along the lines of the ACF implementation and do not get why that part has been left out. It is a rather large discrepancy in regards to ACF compatibility :open_mouth:

When I startup Lucee now I get this error:

2018-01-09 09:37:27.443
lucee.commons.lang.ClassException: cannot load class through its string name, because no definition for the class with the specified name [org.primeoservices.cfgateway.jms.lucee.LuceeJMSSenderGateway] could be found caused by (java.lang.ClassNotFoundException:org.primeoservices.cfgateway.jms.lucee.LuceeJMSSenderGateway;java.lang.ClassNotFoundException:org.primeoservices.cfgateway.jms.lucee.LuceeJMSSenderGateway not found by lucee.core [64];)
	at lucee.commons.lang.ClassUtil.loadClass(ClassUtil.java:217)
	at lucee.transformer.library.ClassDefinitionImpl.getClazz(ClassDefinitionImpl.java:81)
	at lucee.runtime.gateway.GatewayEntryImpl.createGateway(GatewayEntryImpl.java:86)
	at lucee.runtime.gateway.GatewayEngineImpl.load(GatewayEngineImpl.java:105)
	at lucee.runtime.gateway.GatewayEngineImpl.addEntry(GatewayEngineImpl.java:92)
	at lucee.runtime.gateway.GatewayEngineImpl.addEntries(GatewayEngineImpl.java:81)
	at lucee.runtime.config.ConfigWebImpl.setGatewayEntries(ConfigWebImpl.java:358)
	at lucee.runtime.config.XMLConfigWebFactory.loadGateway(XMLConfigWebFactory.java:2409)
	at lucee.runtime.config.XMLConfigWebFactory.loadGatewayEL(XMLConfigWebFactory.java:2353)
	at lucee.runtime.config.XMLConfigWebFactory.load(XMLConfigWebFactory.java:516)
	at lucee.runtime.config.XMLConfigWebFactory.reloadInstance(XMLConfigWebFactory.java:331)
	at lucee.runtime.config.XMLConfigAdmin._reload(XMLConfigAdmin.java:330)
	at lucee.runtime.config.XMLConfigAdmin.storeAndReload(XMLConfigAdmin.java:307)
	at lucee.runtime.tag.Admin.store(Admin.java:5573)
	at lucee.runtime.tag.Admin.doUpdateGatewayEntry(Admin.java:2965)
	at lucee.runtime.tag.Admin._doStartTag(Admin.java:720)
	at lucee.runtime.tag.Admin.doStartTag(Admin.java:340)
	at CFM_file_cfm$cf.call(/website/CFM_file_cfm:38)
	at lucee.runtime.PageContextImpl._doInclude(PageContextImpl.java:939)
	at lucee.runtime.PageContextImpl._doInclude(PageContextImpl.java:833)
	at lucee.runtime.PageContextImpl.doInclude(PageContextImpl.java:817)
	at CFM_file_cfm$cf.call(/website/CFM-FILE.cfm:7)
	at lucee.runtime.PageContextImpl._doInclude(PageContextImpl.java:939)
	at lucee.runtime.PageContextImpl._doInclude(PageContextImpl.java:833)
	at lucee.runtime.PageContextImpl.doInclude(PageContextImpl.java:817)
	at vokk_webwinkel.fbx_fusebox_cfm$cf.call(/vokk_webwinkel/fbx_fusebox.cfm:251)
	at lucee.runtime.PageContextImpl._doInclude(PageContextImpl.java:939)
	at lucee.runtime.PageContextImpl._doInclude(PageContextImpl.java:833)
	at lucee.runtime.PageContextImpl.doInclude(PageContextImpl.java:817)
	at vokk_webwinkel.index_cfm$cf.call(/vokk_webwinkel/index.cfm:9)
	at lucee.runtime.PageContextImpl._doInclude(PageContextImpl.java:939)
	at lucee.runtime.PageContextImpl._doInclude(PageContextImpl.java:833)
	at lucee.runtime.listener.ModernAppListener._onRequest(ModernAppListener.java:223)
	at lucee.runtime.listener.MixedAppListener.onRequest(MixedAppListener.java:43)
	at lucee.runtime.PageContextImpl.execute(PageContextImpl.java:2405)
	at lucee.runtime.PageContextImpl._execute(PageContextImpl.java:2395)
	at lucee.runtime.PageContextImpl.executeCFML(PageContextImpl.java:2363)
	at lucee.runtime.engine.Request.exe(Request.java:44)
	at lucee.runtime.engine.CFMLEngineImpl._service(CFMLEngineImpl.java:1091)
	at lucee.runtime.engine.CFMLEngineImpl.serviceCFML(CFMLEngineImpl.java:1039)
	at lucee.loader.engine.CFMLEngineWrapper.serviceCFML(CFMLEngineWrapper.java:102)
	at lucee.loader.servlet.CFMLServlet.service(CFMLServlet.java:62)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
	at org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:476)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:802)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1410)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:745)

Anyone an idea what this means? I have two web-contexts:

* C:\inetpub\wwwroot\ (localhost - I assume...)
* C:\lucee\tomcat\webapps\ROOT\ (localhost:8888 - I think...)

In which web-context does Lucee install / create a gateway via the ADMIN-tag?

This is my call in the above website (somewhere in the CFM-page) running under http://localhost/website/:

<cfscript>
/* create gateway */
	admin action="updateGatewayEntry"
			type="web"
			password="supersecret"
			id="MyTaskGateway"
			cfcpath="lucee.extension.gateway.TaskGateway"
			listenerCfcPath="TaskGatewayListener"
			custom="#{
				script='https://websitename.domain/dosomething.cfm'
				,sleep='1000'
			}#"
			class=""
			startupMode="automatic"
			readOnly=false;
/* get gateways */
	admin action="getGatewayEntries"
			type="web"
			password="supersecret"
			returnVariable="gateways";
	WriteDump(gateways);
</cfscript>

The first admin-action fires with no issues, the second returns an empty queryobject. What am I doing wrong? I think I am close, I can smell the gateway, and still it eludes me.

OK, I am a step closer. I deleted the second web-context (:8888 - internal webserver?) and was left with only the first one. Then I logged into the web-admin and added a new Gateway instance of type Task Gateway, and LO AND BEHOLD, it materialized! I now have a running Task Gateway performing a CFM-script every day.

But, Lucee still does not add a Task gateway via the CFADMIN-tag of type web if I run this in the code (see example above). So I’m still searching for a way to add a CFML-gateway to listen to a CFC so I can make use of SendGatewayMessage.

Hi Sebastiaan,

It very well might be unrelated, but I have run into trouble with adding custom CFC’s to Lucee’s web config 2 years ago. It turned out Lucee doesn’t know where to find my CFC’s when it is still initializing, and I therefor needed to create an evil hack for it. See the comments at

Basically, when the web context starts, I manually load the cfc, then reload the request, after which Lucee can find the cfc.

Maybe, just maybe, that same workaround would make your cfadmin statements work as well…

By the way, I ran into some of the same issues as you have with event gateways several years ago. A) changes in the event gateway cfc are never picked up, and B) the event gateway instance was not showing up / manageable in the admin. And for me, that was back in the Railo days. I abandoned event gateways all together because of these issues.

Kind regards, Paul Klinkenberg

Hi @frinky and others. so I got the CFML gateways to work, after many hours of stress and painstaking testing. The setup in the end seems very simple, but getting there was quite hard. Please find below a working setup for a CFML eventgateway, enabling the use of the internal CFML function SendGatewayMessage.

Application.cfc

<cffunction name="onApplicationStart">
	<cfscript>
		/* create CFML eventgateway */
		var password = "";
		var gatewayId = "";
		var gatewayMapping = "";
		var listenerMapping = "";
		var currentGateways = "";
		var queryService = "";
		var queryResult = "";
		/* settings come from coldspring via injected properties */
		password = getProperty("gatewayPassword");
		gatewayId = getProperty("gatewayName");
		gatewayMapping = getProperty("gatewayMapping");
		listenerMapping = getProperty("gatewayListenerMapping");
		/* get available gateways from the WEB context */
		admin action="getgatewayentries" type="web" password="#password#" returnvariable="gateways";
		currentGateways = ValueList(gateways.id);
		/* if gateway hasn't yet been created, do that now */
		if(! ListFind(currentGateways,"#gatewayId#")) {
			admin action="updategatewayentry"
					type="web"
					password="#password#"
					id="#gatewayId#"
					cfcpath="#gatewayMapping#"
					listenerCfcPath="#listenerMapping#"
					custom="#{}#"
					class=""
					startupMode="manual";
			// start the new gateway
			admin action="gateway" type="web" password="#password#" id="#gatewayId#" gatewayaction="start";
		/* gateway exists - check if it has been started */
		} else if(ListFind(currentGateways,"#gatewayId#")) {
			queryService = new Query();
			/* the below statements can all be chained, much like in jQuery, but keep them separate here for the ease of use and overview */
			queryService.setName("checkGatewayStatus");
			queryService.setDBType("query");
			queryService.setAttributes(sourceQuery=gateways);
			queryService.addParam(name="gatewayId",value="#gatewayId#",cfsqltype="CF_SQL_VARCHAR");
			queryResult = queryService.execute(sql="SELECT state FROM sourceQuery WHERE id = :gatewayId").getResult();
			/* if the gateway isn't already running or is starting up */
			if(! ListFind("starting,running",queryResult.state)) {
				// start the gateway
				admin action="gateway" type="web" password="#password#" id="#gatewayId#" gatewayaction="start";
			}
		}
		// for use in the SendGatewayMessage(application.applicationNameEventGatewayName,data)
		application.applicationNameEventGatewayName = gatewayId;
	</cfscript>
</cffunction>

The gateway function and the gateway listener in our webapp is placed in the model of the MVC structure in the same directory.

Gateway.cfc

<cfscript>
	/* A gateway that listens for messages and passes them on to the listener.

	Settings for config:
		- interval: the interval to wait between polls (ms, default 1000)
	
	The config is passed on to the listener with every call, so additional config parameters can be used. */
	component {

		public void function init(required string id, required struct config, required component listener) {
			variables.state = "stopped";
			variables.id = arguments.id;
			variables.config = arguments.config;
			variables.listener = arguments.listener;
			variables.interval = variables.config.interval ?: 1000;
		}

		public void function start() {
			while (variables.state == "stopping") {
				Sleep(10);
			}
			if (variables.state != "running" && variables.state != "starting") {
				variables.state = "starting";
				variables.queue = CreateObject("java","java.util.concurrent.ConcurrentLinkedQueue").init();
				variables.state = "running";
				WriteLog(file="messagegateway",type="information",text="gateway #variables.id# running");
				while (variables.state == "running") {
					var time = GetTickCount();
					var data = variables.queue.poll();
					while (!IsNull(data)) {
						try {
							variables.listener.YOURLISTENERFUNCTIONHERE(data);
						} catch (any e) {
							WriteLog(file="messagegateway",type="error",text="gateway #variables.id# error: [#e.type#] #e.message# #e.detail#");
							WriteLog(file="failedmessages",type="warning",text="gateway #variables.id#: #Serialize(data)#");
						}
						data = variables.queue.poll();
					}
					var tickCount = GetTickCount() - time;
					if (tickCount < variables.interval) {
						Sleep(variables.interval - tickCount);
					}
				}
				variables.state = "stopped";
				WriteLog(file="messagegateway",type="information",text="gateway #variables.id# stopped");
			}
		}

		public void function stop() {
			if (variables.state == "running") {
				variables.state = "stopping";
				WriteLog(file="messagegateway",type="information",text="gateway #variables.id# stopping");
			}
		}

		public void function restart() {
			stop();
			start();
		}

		public string function getState() {
			return variables.state;
		}

		public void function sendMessage(required Struct data) {
			variables.queue.add(arguments.data);
		}

	}
</cfscript>
2 Likes