Reading Grails configs from groovy - grails

I just tried to read a grails config from groovy. The ConfigSlurper is easy to use, but since it executes the config, it needs all dependencies in place. In my case, it complains about a missing log4j class.
Even when I import this class into my ConfigSlurper script, the config itself runs into this problem.
Any Idea how I could make the log4j classes accessible to the config?
Update: now that I have a proper keyboard in front of me, I can elaborate on my problem:
I have grails configurations which configure log4j as described in the docs:
import org.apache.log4j.*
log4j = {
appenders {
appender new RollingFileAppender(
name: "myAppender",
maxFileSize: 1024,
file: "/tmp/logs/myApp.log"
)
}
}
Then I tried to parse them like this:
def file = new File(<config location>)
def config = new ConfigSlurper().parse(file.toURL())
and the Slurper threw an exception...

I've found two solutions:
First, the documents show a way how to configure a rollingFileAppender without instantiating one: http://grails.org/doc/2.3.7/guide/conf.html .
Second, I managed to fix my original problem with a
#Grab('log4j:log4j:')
in front of my script - not a good solution if you are behind a firewall...
As Jeff already stated, it should also be possible to put the right jar file in the classpath, but I have to say that I often struggle with the cp :-( and when I tried to put the log4j.jar in the .groovy folder, I didn't succeed - but I guess this was because of an inproper groovy installation.

Related

How to Modify Grails 5 Configuration by Adding Groovy Files

I have a large number of Grails 2.5 applications that I want to upgrade to Grails 5, but have been unable to get the configuration to work. In particular, I want my plugin to set up the data source and Spring Security configuration as it did in Grails 2.5.
In my Grails 2.5 applications, I was able to add files to the configuration by adding this code to the top of Config.groovy.
if (!grails.config.location || !(grails.config.location instanceof List)) {
grails.config.location = []
}
grails.config.location << ["classpath:jcc-server-config.properties"]
grails.config.location << ["classpath:SecurityConfig.groovy"]
But this doesn't work in Grails 5. I've tried adding an application.groovy file, but everything defined in the application.yml seems to be set in stone. Has anybody found a way to add a Groovy file to the Grails configuration that will override or add to the settings in application.yml? YAML will not do because I have logic embedded in the configuration to make it work correctly in different environments.
Thanks.
Did you remember to include the external-config dependency? i.e.
implementation 'dk.glasius:external-config:3.0.0'
Re' your question on accessing config values this way, there should be no difference, in my apps I get to the config either via grailsApplication.config, or if grailsApplication isn't immediately available(e.g. classes under src), then with Holders, i.e. Holders.grailsApplication.config.

Add logging to an external pluggable script

As described in Can't call one closure from another, I am using a pluggable script from within a Grails app.
Unfortunately, I've found that I can't use log4j from within these scripts. I am forced to use println.
I tried using
import org.apache.commons.logging.LogFactory
def Log log = LogFactory.getLog(getClass())
but I got no output. When I print out the result of the call to getClass(), I get something like
myscript$_run_closure5
So I'm thinking the issue is that there is no configuration in my Grails Config.groovy file for this class.
Is there a way for me to programmatically add these pluggable scripts to the log4j configuration? Keep in mind that I do not know in advance what the names of the scripts are, so this has to happen at runtime.
Consider the following code:
import org.apache.log4j.Logger
// ...
Logger log = Logger.getLogger('MyPlugin')
new File( grailsApplication.config.externalFiles ).eachFile { file ->
Binding binding = new Binding()
binding.variables.log = log
GroovyShell shell = new GroovyShell(binding)
shell.evaluate(file)
strategies.put( binding.variables.key, binding.variables )
}
Explanation:
It is not obligatory to pass class name to getLogger, it can be actually any string. You just need to make sure that this string is matched in log4j.properties of the main program.
You pass once created log to plugin scripts via binding variable "log". Then plugin scripts can access it simply as log.info('test123')
My personal recommendation would be to use logback instead of log4j. Both libraries were developed by the same guy and it is stated that logback supersedes log4j.

Share config between two grails apps that share a common plugin

We will have two apps that both need to use the same services/utilities/code/configuration.
We are using grailsApplication.config.* to configure things like URLs to external services. These are different depending on whether the app is running in dev/test/qa/staging/prod, so we have used the environments section in Config.groovy. We'll need the same URLs/environments configured for both applications.
In order to avoid duplication, we are trying to build a plugin that will hold all the shared stuff. This works for services and such, but Grails plugins do not include Config.groovy, resources.groovy so all the URL configuration and such can't be put in Config.groovy in the plugin.
Is there a nice way to put that configuration in a single place and have it available for both apps?
Perhaps we could put it in some place in the plugin and "import" it into the Config.groovy of both apps?
The grails.config.locations definition for external configuration files can include java.lang.Class objects to load configuration from pre-compiled Groovy scripts, as well as file: or classpath: URLs to parse Groovy or .properties files at runtime. So you should be able to create a configuration file in the plugin under src/groovy
{plugin}/src/groovy/com/example/CommonConfiguration.groovy
package com.example
environments {
production {
...
}
development {
...
}
}
and then in the applications' Config.groovy files include this class in grails.config.locations
grails.config.locations = [com.example.CommonConfiguration]
However this does mean that when the plugin's CommonConfiguration and the host app's Config.groovy both specify a value for the same property, the plugin would win. To redress the balance, you'd need to put a second external in grails.config.locations (which could be another Class or a URL)
grails.config.locations = [com.example.CommonConfiguration,
"file:app-config.groovy"]
and put app configuration in there (as later externals override earlier ones).
Given that you want to embed the configuration within the plugin you will need to make your plugin smart enough to read it's own configuration and merge that into the containing applications config. The following is based on Grails 1.3.7. The configuration holder may have changed since then (2.0 did a lot of house cleaning) but I am sure you can figure that part out. This example assumes that there is a configuration file called grails-app/conf/MyPluginConfig.groovy inside your plugin.
Inside your /MyPlugin.groovy you will add this merge of your configuration in the doWithSpring closure.
def doWithSpring = {
// get the current application configuration
def currentConfig = org.codehaus.groovy.grails.commons.ConfigurationHolder.config
GroovyClassLoader classLoader = new GroovyClassLoader(getClass().classLoader)
// get the plugin configuration
def pluginConfig = new ConfigSlurper(grails.util.GrailsUtil.environment).parse(classLoader.loadClass('MyPluginConfig'))
// merge the configurations
pluginConfig.merge(currentConfig)
// set the application configuration to the merged configuration
org.codehaus.groovy.grails.commons.ConfigurationHolder.config = pluginConfig
}
That's it in a nutshell. Hope this helps.
Also, take note that you can still override the values in your containing application because of the way the merge is done. The application configuration is merged into the plugin configuration. If the containing application defines something it will override the plugins value.

How to use an external service as a source for Grails app configuration?

Section 3.4 of Grails documentation says that Grails app can be configured from an external source:
grails.config.locations = [
"classpath:${appName}-config.properties",
"classpath:${appName}-config.groovy",
"file:${userHome}/.grails/${appName}-config.properties",
"file:${userHome}/.grails/${appName}-config.groovy" ]
Also, it is possible to load config by specifying a class that is a config script:
grails.config.locations = [com.my.app.MyConfig]
My questions are:
Could you please give an example of how MyConfig class implementation could look like? It is not quite clear from the documentation.
If I want to use some external JSON REST service as a source for my config data, how can this be implemented?
Answer for second question: you can do that in BootStrap.groovy init closure, because basically, it allows you to execute any code:
// Inject grails app
def grailsApplication
def init = { servletContext ->
def externalValue = getItUsingRest(someUrl)
grailsApplication.config.my.custom.var = externalValue
}
Depending on version of grails you are using, you might need to use
org.codehaus.groovy.grails.commons.ConfigurationHolde.config
to get to config instead.
Your application configuration can actually be a Groovy script. So any class which looks like your Config.groovy can act as a configuration class. In our projects we tend to keep the configuration outside the grails application so that the application can be configured without recompiling everything.
Maybe this post of mine will give you a hint on how to use and load external configuration files.

Grails Plugin Get Plugin's Root or Installed Directory

I'm writing a plugin and I'm trying to write a file inside of the Plugin's root or installed directory (not sure what to refer to this to). I can't seem to figure out how to get a hold of this value. Doing System.properties['base.dir'] will result in the implementing Grails project's root directory. So right now I have two directories:
C:/PluginProject/
C:/GrailsAppThatUsesPlugin/
It's my understanding that when this becomes a distributed plugin a user will similarly have two directories:
GRAILS_HOME/grails-version/projects/projectName/plugins/myPlugin/
C:/GrailsAppThatUsesPlugin/
Inside of my plugin project I need to create a file. It needs to be inside of my plugin because the file I'm writing needs to reference other files that my plugin provides. The few things that I've tried that haven't worked are:
System.properties['base.dir']
new File("")
In a groovy script within a plugin you can simply refer to pluginNamePluginDir but I'm trying to access this from a POGO.
Looking at all of the System.properties none of them have the plugin directory
grailsApplication doesn't seem to contain this type of information either.
Any help is greatly appreciated.
This worked for me (both in-place and regular plug-ins):
import org.codehaus.groovy.grails.plugins.GrailsPluginUtils
GrailsPluginUtils.pluginInfos.find { it.name == pluginName }.pluginDir
You can use this:
import org.codehaus.groovy.grails.plugins.PluginManagerHolder
def pluginManager = PluginManagerHolder.pluginManager
def plugin = pluginManager.getGrailsPlugin(pluginName)
def pluginDir = plugin.descriptor.file.parentFile
The plugin name has to be of the form 'spring-security-core', not 'springSecurityCore'

Resources