Story of loading Grails Config keys from Database :-)

We are familiar with externalising the configuration keys through flat files which could be introduced through Config.groovy. Primarily, this helps majorly in the production environment to customise the configuration keys without touching the WAR.

Now, what if I keep my configuration keys externalised through Database? How to load these into the Configuration Context before the Grails Runtime start making use of those?

Remember.. many keys are even used for the construction of some of the spring bean objects that belong to the plugins within my project. So, loading from BootStrap.groovy won’t be an option which happens a little late in the life cycle for me. So, we somehow need to get hold of the configuration context right after Grails initialised it.

How do we know when Grails has done with its configuration context loading? Notifying the needy of something happened do indicate of the observer-observable pattern. And in Grails, as we know, the way to listen to Grails Life Cycle notifications is through the eventListeners placed in _Events.groovy.

Now that could be interpreted rightly that if there is an event that Grails fires to indicate the completion of configuration context loading, then we can comfortably place the respective event handler in _Events.groovy. This event handler then will help us to merge those configuration keys read from the Database with the ones in the Grails configuration context that are accessible through grails.util.Holders.config API. Now we need to find out if there is such an event exist?

Grails portal documentation at http://grails.github.io/grails-doc/2.3.x/guide/commandLine.html#events did not leave any clue about it. Some serious head-down debugging through the Grails source code, using Intellij IDEA breakpoints, helped me to zero-in on GrailsProjectPackager.createConfig method. This does the parsing of Config.groovy including its links to the externalised config flat files. It eventually notifies the BuildEventListener about the completion of configuration loading. I was happy to see that I get notified about this through the eventAppCfgEnd event handler placed within the _Events.groovy. As it is not documented in the Grails document, I am not sure if it is not intended for development purposes.

But twist happened next when my team mate informed me that the event handler he placed in his _Events.groovy do not getting notified of it. Further debugging revealed that mine was test-app execution environment, while his was run-app. The set of events to be triggered are chosen by the execution environment and that varies between test-app and run-app. I couldn’t check it for WAR yet, but I expect the same as of run-app.

As a matter of fact, there was an open Grails 2.3.7 issue that complains of parsing the configuration files multiple times during the run-app (https://github.com/grails/grails-core/issues/4925). One of the comments under this issue had explained that the configuration files would be parsed two times in a non-forked mode – first time to load the log4j configuration, and the latter during web-app context initialization. However, my personal experience was that it parses 4 times with 2.3.7 instead of just 2 – anyway that is a different topic altogether. Doesn’t matter 2 or 4, my point here was that if that could be the reason why run-app was decided not to throw “eventCfgEnd”. And if then, there could be some other event to indicate the log4j configuration loading and web-app configuration loading?

Further debugging showed up that the first config loading happens right after the “eventCompileEnd” but before “eventConfigureServerContextPathEnd”. While the second happens right after “eventPackageAppEnd”; but before “eventStatusUpdate (“Running Grails Application”)”. However, I noticed that there is an if-check within the createConfig method not to proceed if there is context alredy available due to which the second one do not really enter inside. So, I decided to depend upon “eventConfigureServerContextPathEnd”. However, like the eventCfgEnd, this too was not documented under Grails portal.

Anyway I along with my team determined to proceed with this approach. Idea was to load Database keys into Holders.config through the event handler I placed in _Events.groovy for “eventConfigureServerContextPathEnd”. Then we have injected this key into a Spring Bean property through resources.groovy which would complete loading-accessing user story. But here we stumbled upon another hurdle.

The grails.util.Holders API uses a class Holder.groovy to manage the storage of configuration context keys. This ensures that the accessors and the loaders of the keys should be loaded by the same classloader instance. In our case, _Events.groovy was loaded during the build cycle by a class loader which seemed to be different from the one used for the remaining. So, a configuration key set dynamically through Holders.config.someKey = “someValue” from _Events.groovy file won’t be available to use within the classes, loaded after the build-cycle – say resources.groovy.

So, I have to go through a little dirty way to create a customHolder class that maintains a private static ConfigObject within it to store the dynamically populated config keys from _Events.groovy – like CustomHolder.config.someKey = “someValue”. Also added a getConfig API for this class to return a ConfigObject.merge of this private ConfigObject with Holder.config that ensures the super set of keys. The calls in resources.groovy then can use CustomHolder.config.someKey.

I know that this contradicts the design concept around Holder.groovy to access-restrict around the class-loader. But I do not see that it will affect my user stories anyway. More than that, I was surprised to see such a restriction imposed upon the classes belong to the same Grails application. In my opinion, for a rapid web application development framework like Grails, a Grails developer should never had to run against class-loading related issues to work within the same JVM. But that does not seem to be the case here.

Leave a comment