In my previous post on logging database changes with Logbox I wanted to show how to log database changes with interceptors in a cborm
system. This should be quite simular to the quick
example from the previous post. It just needs an extra step, you have to configure this in Application.cfc:
//configure this mapping here, your ormSetting need it
this.mappings[ "/cborm" ] = COLDBOX_APP_ROOT_PATH & "/modules/cborm";
this.ormSettings = {
// ...... (other orm settings)
// Enable event handling
eventhandling = true,
// Set the event handler to use, which will be inside our application.
eventhandler = "cborm.models.EventHandler"
};
The cborm.models.EventHandler
will act as a bridge between the coldfusion ORM event handling and the interceptor system in coldbox. Orm settings are configured in Application.cfc even before coldbox is loaded. I’ve been using this for many years in older application and it always worked like a charm. For my series of blogposts on Logbox I created up a new coldbox application, configured the ORM settings as I have done for many years, and fired up some recent Lucee version. It failed.
Looking at the error, and the coldbox startup code in Application.cfc. The application was requesting application.cbbootstrap
, which was not there onRequestStart
. This is weird, because this variable is set onApplicationStart
which should fire earlier in the proces. So time for some debugging.
Since I had working code before my first guess was to try older box versions. And yes, the error only showed up in coldbox 6.5 and 6.6. So obviously something had changed. When I disabled the OrmEventHandler again the error was gone. When I created my own OrmEventHandler I just had to implement the CFIDE.ORM.IEventHandler interface, which is not that hard. You just create a component and 10 methods, such as preDelete, postDelete, preUpdate and several more. That’s exactly what’s happening in the cborm.models.EventHandler
but only one difference: if I was using my own eventHandler the code was not failing. So I needed a closer look at the differences between the cborm EventHandler and mine.
The cborm eventhandler is extending coldbox.system.remote.ColdboxProxy
. This is useful since the coldbox proxy is opening up access to the coldbox controller and especially the interceptor service. However, when I removed the reference to the coldbox proxy, the original startup error was gone. So time to dig into the coldbox source code. It appeared the source had changed in 6.5 with this fix which looks quite innocent
if( !isNull( application.cbBootstrap ) ) {
variables.COLDBOX_APP_KEY =
application.cbBootstrap.getCOLDBOX_APP_KEY();
}
This small piece of code is executed in the pseudo constructor of the coldbox proxy, so how can this break our startup ???
For an answer on this, we have to look at how coldbox is bootstrapped and where orm is configured. The most important file here is Application.cfc, where all the coldbox magic starts. The most important event is onApplicationStart
where coldbox is loaded. It does so by creating an application.bootstrap
key and a bit later in the process it creates the controller, by default in application.cbcontroller
. Once coldbox is loaded onRequestStart
and onSessionStart
are processed, basically by just passing on the event to application.cbBootstrap
. So in our error scenario, onRequestStart
is fired BEFORE onApplicationStart
which should be impossible. And this all only happens if we are checking for the nullness of application.bootstrap
in the above code.
The problem is in the registration of the ormSettings. This happens before initializing the application, and the pseudo constructor is trying to access the application scope before the application has started. Lucee and ACF act in different ways here. ACF is just ignoring it, so no errors raised here. Lucee on the other hand tells me application
scope is there but it is firing an onRequestStart, doing this before the application has started. That makes no sense at all and breaks coldbox cborm.
So, where should we fix this? I think it is definitely wrong to fire an onRequestStart
when the application has not started yet. On the other side I could also argue the cborm Eventhandler or the coldbox proxy should not try to access application variables knowing that the application has not started yet. So we have several options:
- lucee should fix an engine bug. I am afraid this will take a while, so no option today.
- the coldbox proxy should not access application scope on initialization, which means the box 6.5 fix should be reverted. I don’t see any reason why we would need a non-default
COLDBOX_APP_KEY
, but probably other people think it is necessary. It just ruins the event handling - We could remove the inheritance from the coldbox proxy in the ORM handler. That means we need an other way to get references to the coldbox controller, interceptors and wirebox in this handler. I created such an object by copying the ormEventHander, removing the inheritance and add a few private functions
private any function getWireBox(){
return getController().getWireBox();
}
private function announceInterception(){
return getController().getInterceptorService().announce( argumentCollection = arguments );
}
private function getController(){
// Setup Default Namespace Key for controller locations
variables.COLDBOX_APP_KEY = "cbController";
if ( !isNull( application.cbBootstrap ) ) {
variables.COLDBOX_APP_KEY = application.cbBootstrap.getCOLDBOX_APP_KEY();
}
return application[ variables.COLDBOX_APP_KEY ];
}
By adding the getController function in the orm handler we can announce interceptions, which is the main bridging function of this event handler. It might needs some extra tests, but if you want you can just replace the default cborm version by this modified version. You don’t have to touch cborm code for that, you can just specify your own event handler in application.cfc.
So, it is up to Ortus if they want to patch cborm, the coldbox proxy or just think Lucee should fix their engine. In the meantime I can just use my own OrmEvent handler version. It was an interesting exercise, and I learned a lot about the inner workings of coldbox trying to solve this problem, but I was quite surprised this issue never came up before: coldbox 6.5 is already present since July 9th 2021. So who is using orm events?
I upgraded from CB 6.1 and met the issue. Wasted good hours before I found your post and THE solution! Thank you for documenting it here.
A bit of a godsend today. There is no way I would have thought of all this. Nice work! Weird that this isn’t fixed yet in the actual packages, right?
Well, there is a coldbox fix now, but it is not in a release yet.
https://ortussolutions.atlassian.net/browse/COLDBOX-1094
I’ve been pushing this a bit, but initially it was not clear if coldbox should be fixed or cborm. I had a workaround for cborm, but a coldbox fix is a lot better. I was surprised this has not been discovered before.The problem was already there for at least half a year. You can imagine how many people use this….