Share config between two grails apps that share a common plugin - grails

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.

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.

Dependency injection of cxf-bean / client in a grails service doesn't work - maybe because it's configured in my plugins config.groovy?

I'm working on a payment plugin for the grails framework. I'm using a payment provider which gives me a SOAP API (WSDL) and I need a cxf-client to communicate with the webservice.
I installed https://github.com/ctoestreich/cxf-client (cxf-client plugin) in my grails plugin project (2.2) and want to use the cxf-client I added to my config.groovy in a grails service.
In the Service Class I just added
RecurringPortType recurringPaymentClient
I don't start the plugin project directly, instead i included it in my mainproject where I use some methods of the plugins service (also autowired into my mainproject).
After using the autowired plugin service (which works) I get a nullpointer exception using a method which uses the autowired cxf-client in the plugins service class. The cxf-client bean reuccringPaymentClient is null.
But why? Do I have to include the cxf-client configuration also into my mainprojects config.groovy? Or is there a solution my mainproject can merge or also use the config.groovy of my new plugin? At this time the cxf-configuration is placed in the plugins config.groovy - Maybe that's the problem?
Using
RecurringPortType recurringPaymentClient = ApplicationHolder.application.mainContext.getBean("recurringPaymentClient")
as described in cxf-client documentation didn't help.
The Config.groovy file in a plugin only applies when the plugin is run as a standalone application itself. It is not read when the plugin is used in another application. A trick I've seen some plugins use (and which I have stolen for one of my own plugins) is to manipulate the configuration in the plugin descriptor's doWithSpring, which is usually early enough to have the required effect. In your case you'd have to make your plugin loadBefore the cxf-client plugin to ensure that your doWithSpring (creating the config) happens before that of cxf-client (which is where the settings will be used).
class MyCleverGrailsPlugin {
def version = "0.1-SNAPSHOT"
def loadBefore = ['cxfClient']
def doWithSpring = {
application.config.cxf.client.recurringPaymentClient.clientInterface = com.example.MyClientPortType
// etc. etc.
}
}
Or you can use ConfigSlurper by hand
def doWithSpring = {
ConfigObject pluginConf = new ConfigSlurper(Environment.current.name).parse(com.example.MyPluginDefaultConfig)
application.config.cxf = pluginConf.cxf.merge(application.config.cxf)
}
This loads a Config.groovy-style script from the plugin's src/groovy/com/example/MyPluginDefaultConfig.groovy, and merges the cxf section of that config into the cxf section of the main application's configuration, with settings in the app overriding the defaults supplied by the plugin.

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.

deleting plugins views (gsp) when building the war

We are using various plugins in our grails application (like logging, spring security core, ui, acl and many others). Now these plugins come with default gsps (in the views folder of each plugin).
I want to build a WAR without including the views of any plugin. So when the war is built right now it creates the plugins folder which contains views folder which come by default with the plugin, these views are introducing a lot of vulnerabilities and so I want to exclude the plugins views.
I am trying this right now in BuildConfig.groovy like below:
grails.project.dependency.resolution = {
grails.war.resources = { stagingDir ->
println "Customized delete started..."
delete{fileset dir: "${stagingDir}/WEB-INF/plugins/logging-0.1/grails-app/views/"}
delete{fileset dir: "${stagingDir}/WEB-INF/plugins/spring-security-ui-0.1.2/grails-app/views/"}
}
}
But the problem is the code tries to delete the views when they are not yet created by the war building process. Hence I get a file not found error for those plugins views.
Where should I write the code to delete the plugins views so that they are already created and available to delete when building the WAR, or how do I not include the plugins views in the WAR?
Thanks in advance..
Priyank
I answered this question on the Grails mailing list.
http://grails.1312388.n4.nabble.com/deleting-plugins-views-gsp-when-building-the-war-td4560517.html
(The answer hasn't yet shown up in nabble)
You can remove/add files from/to a war file in the eventCreateWarStart
event specified in scripts/_Events.groovy file.
This might work:
filename: scripts/_Events.groovy
eventCreateWarStart = { warName, stagingDir ->
Ant.delete(dir: "${stagingDir}/WEB-INF/plugins/logging-0.1/grails-app/views")
Ant.delete(dir: "${stagingDir}/WEB-INF/classes", includes:"gsp_logging*.*")
Ant.delete(dir: "${stagingDir}/WEB-INF/plugins/spring-security-ui-0.1.2/grails-app/views")
Ant.delete(dir: "${stagingDir}/WEB-INF/classes", includes:"gsp_springSecurityUi*.*")
}
I'm not sure if you could also remove plugin Controller classes without problems. We've used Filter classes to "disable" controllers provided by plugins.
As a side-note you can disable "development-only" plugins in the production environment by using the yet undocumented "grails.plugins.excludes" feature:
Example:
in Config.groovy:
import grails.util.Environment
if(Environment.current == Environment.PRODUCTION) {
grails.plugin.excludes = ['somePluginName']
}

Converting Java -> Grails ... How do I load these properties?

I'm converting a Java web app to Grails (1.2.1). In my Java app, I have a singleton that loads properties from a ".properties" file. I've seen I can put that loading into the "Config.groovy" conf file. If my properties are loaded in Config.groovy, how do I load them in my Java file? Here is how I'm doing it when the Config was loaded in java ...
Long interval = ConfigSingleton.getInstance().getGlobalCacheRefreshInterval();
Thanks, - Dave
Adapted from the Grails User Guide:
You can add your own configuration in grails-app/conf/Config.groovy, for example:
globalCacheRefreshInterval = 120
Then later in your application you can access these settings in one of two ways. The most common is via the GrailsApplication object, which is available as a variable in controllers and tag libraries:
Long interval = grailsApplication.config.globalCacheRefreshInterval
The other way involves getting a reference to the ConfigurationHolder class that holds a reference to the configuration object:
def config = org.codehaus.groovy.grails.commons.ConfigurationHolder.config
Long interval = config.globalCacheRefreshInterval
If you want to acess this configuration from a Java class, you can use:
import org.codehaus.groovy.grails.commons.ConfigurationHolder;
...
Map config = ConfigurationHolder.getFlatConfig();
Long interval = (Long) config.get("globalCacheRefreshInterval");
Attention for the correct type in your Config.groovy. In the case above, your configuration property must be defined as a Long:
globalCacheRefreshInterval = 120L
You should also checkout the ConfigSlurper class (this is what grails uses to load its config file).
Dave if what you need is to load the properties file as it is without having to move them to the Config.groovy manually, you can the following inside the Config.groovy file:
grails.config.locations = [
"file:" + "/pathWhereFileLives/fileName.properties"
]
This will load all the properties you have in the file in the Grails configuration class. Something to be aware of is that if you have a property in the Config.groovy and the properties file with the same name, the one from the properties file will override the value of the one from Config.groovy
You can find more information about the Grails external configurations here.

Resources