configure Grails app to use JDBC connection pool - grails

This article suggests Tomcat 7 apps should use a JDBC connection pool instead of a commons-dbcp connection pool. However, the latter is the default for a Grails app, and it's not obvious how to change it.
My guess is that I need to define a Spring bean in resources.groovy that overrides a bean that is normally created by default, but I've no idea what this bean should be named or what properties I need to set.

The easiest thing to do would probably be to use the jdbc-pool plugin. Since the configuration options for this pool are intentionally very similar to Commons DBCP (they're documented here) you can use the plugin to define the jar dependency and manage switching the class for you. The plugin hasn't been updated in a year so it's a little out of date (the plugin uses version 1.0.9.0 but the latest is 1.0.9.3) so you might want to define the plugin dependency excluding the jar, and add one for the newer version. It's in the ebr repo, so you'll need to add that to your BuildConfig.groovy (see the plugin's version for how he did it).
There are configuration notes for the pool here and a series of blog posts by the author here.
If you do want to configure this without using the plugin, add the ebr repo and the jar dependency to BuildConfig.groovy:
repositories {
inherits true
...
ebr()
}
dependencies {
runtime('org.apache.tomcat:com.springsource.org.apache.tomcat.jdbc:1.0.9.3') {
transitive = false
}
}
and create an override for the dataSource bean in resources.groovy:
import org.apache.tomcat.jdbc.pool.DataSource
beans = {
dataSource(DataSource) {
// mandatory
driverClassName = '${dataSource.driverClassName}'
username = '${dataSource.username}'
password = '${dataSource.password}'
url = '${dataSource.url}'
// optional
minEvictableIdleTimeMillis=1800000
timeBetweenEvictionRunsMillis=1800000
numTestsPerEvictionRun=3
testOnBorrow=true
testWhileIdle=true
testOnReturn=true
validationQuery="SELECT 1"
}
}
It's convenient to use single-quoted strings with ${} placeholders to take advantage of Spring's property placeholder functionality and keep things DRY since you've already set the driver and connect info in DataSource.groovy.

In DataSource.groovy I use the following:
environments {
integration {
dataSource {
pooled = false
jndiName = "java:/comp/env/jdbc/myJndiName"
}
}
}
And everything else is defined by Tomcat - this just needs to match it. There is no need to define any dataSource bean in resources.groovy

Related

How to customize an existing Grails plugin functionality, modifying behavior of doWithSpring method

I am new to grails and while working with Spring Security LDAP plugin it was identified that it accepts the ldap server password in plain text only. The task in hand is to pass an encrypted password which is decrypted before it is consumed by the plugin during its initialization phase.
I have already searched for all possible blogs and stackoverflow questions but could not find a way to extend the main plugin class to simply override the doWithSpring() method so that i can simply add the required decryption logic for the Ldap server password. Any help here will be appreciated.
I have already seen and tried jasypt plugin but it also does not work well if the password is stored in some external file and not application yml. So I am looking for a solution to extend the Spring security plugin main class, add the required behavior and register the custom class.
EDIT
Adding the snippet from Grails LDAP Security plugin, which I am trying to override. So If i am successfully able to update the value of securityConfig object before the plugin loads, the purpose is solved.
Some snippet from the plugin:
def conf = SpringSecurityUtils.securityConfig
...
...
contextSource(DefaultSpringSecurityContextSource, conf.ldap.context.server) { // 'ldap://localhost:389'
authenticationSource = ref('ldapAuthenticationSource')
authenticationStrategy = ref('authenticationStrategy')
userDn = conf.ldap.context.managerDn // 'cn=admin,dc=example,dc=com'
**password = conf.ldap.context.managerPassword // 'secret'**
contextFactory = contextFactoryClass
dirObjectFactory = dirObjectFactoryClass
baseEnvironmentProperties = conf.ldap.context.baseEnvironmentProperties // none
cacheEnvironmentProperties = conf.ldap.context.cacheEnvironmentProperties // true
anonymousReadOnly = conf.ldap.context.anonymousReadOnly // false
referral = conf.ldap.context.referral // null
}
ldapAuthenticationSource(SimpleAuthenticationSource) {
principal = conf.ldap.context.managerDn // 'cn=admin,dc=example,dc=com'
**credentials = conf.ldap.context.managerPassword // 'secret'**
}
You don't need to override the doWithSpring() method in the existing plugin. You can provide your own plugin which loads after the one you want to affect and have your doWithSpring() add whatever you want to the context. If you add beans with the same name as the ones added by the other plugin, yours will replace the ones provided by the other plugin as long as you configure your plugin to load after the other one. Similarly, you could do the same think in resources.groovy of the app if you don't want to write a plugin for this.
You have other options too. You could write a bean post processor or bean definition post processor that affects the beans created by the other plugin. Depending on the particulars, that might be a better idea.
EDIT:
After seeing your comment below I created a simple example that shows how you might use a definition post processor. See the project at https://github.com/jeffbrown/postprocessordemo.
The interesting bits:
https://github.com/jeffbrown/postprocessordemo/blob/master/src/main/groovy/demo/SomeBean.groovy
package demo
class SomeBean {
String someValue
}
https://github.com/jeffbrown/postprocessordemo/blob/master/src/main/groovy/demo/SomePostProcessor.groovy
package demo
import org.springframework.beans.BeansException
import org.springframework.beans.MutablePropertyValues
import org.springframework.beans.PropertyValue
import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
class SomePostProcessor implements BeanDefinitionRegistryPostProcessor{
#Override
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinition definition = registry.getBeanDefinition('someBean')
MutablePropertyValues values = definition.getPropertyValues()
PropertyValue value = values.getPropertyValue('someValue')
def originalValue = value.getValue()
// this is where you could do your decrypting...
values.addPropertyValue('someValue', "MODIFIED: ${originalValue}".toString())
}
#Override
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
https://github.com/jeffbrown/postprocessordemo/blob/master/grails-app/conf/spring/resources.groovy
beans = {
someBean(demo.SomeBean) {
someValue = 'Some Value'
}
somePostProcessor demo.SomePostProcessor
}
https://github.com/jeffbrown/postprocessordemo/blob/master/grails-app/init/postprocessordemo/BootStrap.groovy
package postprocessordemo
import demo.SomeBean
class BootStrap {
SomeBean someBean
def init = { servletContext ->
log.info "The Value: ${someBean.someValue}"
}
def destroy = {
}
}
At application startup you will see log output that looks something like this...
2017-10-23 19:04:54.356 INFO --- [ main] postprocessordemo.BootStrap : The Value: MODIFIED: Some Value
The "MODIFIED" there is evidence that the bean definition post processor modified the property value in the bean. In my example I am simply prepending some text to the string. In your implementation you could decrypt a password or do whatever you want to do there.
I hope that helps.
After trying Jasypt plugin and BeanPostProcessor solutions unsuccessfully for my use case, I found below solution to work perfectly.
To describe again the problem statement here,
a) we had to keep the passwords in an encrypted format inside properties files
b) and given we were packaging as a war file so the properties must not be kept inside the war to allow automated deployment scripts update the encrypted passwords depending on the environment
Jasypt plugin was a perfect solution for the use case a), but it was not able to cover the b) scenario
Moreover, the Grails LDAP Security plugin was getting loaded quite early hence Bean Post processors were also not helping out here.
Solution:
Created a new class by implementing the interface SpringApplicationRunListener. Extended its methods and parsed the properties file using YamlPropertySourceLoader
Sample code:
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
PropertySource<?> applicationYamlPropertySource = loader.load(
"application.yml", new ClassPathResource("application.yml"),"default");
return applicationYamlPropertySource;
Once the properties were loaded inside the MapPropertySource object, parsed them for the encrypted values and applied the decryption logic.
This whole implementation was executed before any plugins were initialized during Grails bootup process solving the purpose.
Hope it will help others.

How to use Flyway in a Grails 3 project with Spring Boot's autoconfig

According to the Flyway documentation it should be possible to use Flyway in a Grails 3 project out-of-the-box:
Grails 3.x is based on Spring Boot comes with out-of-the-box integration for Flyway.
All you need to do is add flyway-core to your build.gradle:
compile "org.flywaydb:flyway-core:4.1.1"
Spring Boot will then automatically autowire Flyway with its DataSource and invoke it on startup.
This doesn't work for me. Flyway does not kick in at application startup. In the logs I see some suspicious lines:
FlywayAutoConfiguration did not match
- #ConditionalOnClass found required class 'org.flywaydb.core.Flyway' (OnClassCondition)
- #ConditionalOnProperty (flyway.enabled) matched (OnPropertyCondition)
- #ConditionalOnBean (types: javax.sql.DataSource; SearchStrategy: all) did not find any beans (OnBeanCondition)
...
Exclusions:
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
And this is how Spring's FlywayAutoConfiguration class looks like:
#Configuration
#ConditionalOnClass(Flyway.class)
#ConditionalOnBean(DataSource.class)
#ConditionalOnProperty(prefix = "flyway", name = "enabled", matchIfMissing = true)
#AutoConfigureAfter({ DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class })
public class FlywayAutoConfiguration {
So it looks to me that it is not working because DataSourceAutoConfiguration is excluded from auto-config.
Is this analysis correct?
Why and where is DataSourceAutoConfiguration excluded? Supposedly somewhere deep inside of Grails, because I am not aware of any place in my code that could cause this.
How can I make the flyway integration into Grails work as promised by the Flyway docs? I know I can do it manually via resources.groovy (working code from my project, heavily inspired by Grails Flyway plugin code):
if (application.config.flyway.enabled != false) {
flyway(Flyway) { bean ->
bean.initMethod = 'migrate'
dataSource = ref('dataSource')
baselineOnMigrate = application.config.flyway.baselineOnMigrate
}
BeanDefinition sessionFactoryBeanDef = getBeanDefinition('sessionFactory')
if (sessionFactoryBeanDef) {
def dependsOnList = ['flyway'] as Set
if (sessionFactoryBeanDef.dependsOn?.length > 0) {
dependsOnList.addAll(sessionFactoryBeanDef.dependsOn)
}
sessionFactoryBeanDef.dependsOn = dependsOnList as String[]
}
}
but if possible I'd prefer the auto-config approach because it supports many flyway properties out-of-the-box and I can keep my resources.groovy tidy.

Is there a way to inject a Grails datasource into logback.groovy for use with DBAppender?

So, Grails already sets up datasources backed by connection pools. Is there a way to leverage those for use with DBAppender in Logback so that I don't have to create a separate parallel datasource/connection pool?
logback.groovy is somewhat external to Grails, so it doesn't accept Spring autowiring, and other tricks like grails.util.Holders.findApplication() don't appear to work.
Woof, this was a chore. Frankly, I'm a bit disillusioned with Logback. Logback creates it's own Spring ApplicationContext. So we have two separate contexts. Ugh. Also, it certainly doesn't help that the DSL that Logback uses for configuring Spring in Groovy is different than Grails'.
Since Logback gets fired up before Grails is completely started, we need to tell Logback to create some dummy appenders that will just store log messages until we fire up appenders from within Grails. We use the logback extension for Spring to do this.
build.gradle:
compile 'org.logback-extensions:logback-ext-spring:0.1.4'
logback.groovy:
import ch.qos.logback.ext.spring.DelegatingLogbackAppender
appender('DB', DelegatingLogbackAppender)
appender('STDOUT', DelegatingLogbackAppender)
resources.groovy:
import ch.qos.logback.ext.spring.ApplicationContextHolder
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.classic.db.DBAppender
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.db.DataSourceConnectionSource
import org.slf4j.LoggerFactory
beans = {
applicationContextHolder(ApplicationContextHolder)
loggerContext(LoggerFactory) { bean ->
bean.factoryMethod = "getILoggerFactory"
}
patternLayoutEncoder(PatternLayoutEncoder) { bean ->
bean.initMethod = 'start'
bean.destroyMethod = 'stop'
context = ref(loggerContext)
pattern = "%level %logger - %msg%n"
}
STDOUT(ConsoleAppender) { bean ->
bean.initMethod = 'start'
bean.destroyMethod = 'stop'
context = ref(loggerContext)
encoder = ref(patternLayoutEncoder)
}
connectionSource(DataSourceConnectionSource) { bean ->
bean.initMethod = 'start'
bean.destroyMethod = 'stop'
context = ref(loggerContext)
dataSource = ref(dataSource)
}
DB(DBAppender) { bean ->
bean.initMethod = 'start'
bean.destroyMethod = 'stop'
context = ref(loggerContext)
connectionSource = ref(connectionSource)
}
}
The ref(dataSource) in the DataSourceConnectionSource references the dataSource you have configured in application.yml or application.groovy.
Say you have multiple dataSources (or even one configured just for logback called dataSources.logging. In that case the bean reference would be dataSource_logging. The default dataSource in that case (called dataSources.dataSource bean reference is just dataSource. Took me a while to figure that one out.
All in all, i miss the days of configuring Log4j from within the Grails config file using the Grails DSL. I get that separating logging from Grails means one less thing for Graeme and the Grails team to deal with, but this was a major PITA for something that I thought would be common. ¯\_(ツ)_/¯

Grails' Config.groovy in plugins and dependent apps

I am making both a Grails plugin and several Grails apps that will use the plugin.
I want to define a few properties and give them defaults in the plugin, but allow apps to override their values (optional). This question surrounds the mechanical details of how to wire both plugin and child app alike in this manner.
Say I want my plugin (grails-myplugin) to define the following properties in its Config.groovy:
myplugin {
fizz {
whistles = true // Default for all child apps using this plugin
buzz = 3 // Default for all child apps using this plugin
}
}
grails.plugins.anotherPlugin.widget = 'auto'
grails.plugins.anotherPlugin.foo = '${myplugin.fizz.buzz}-40' // Hence, by default, is '3-40'
Now, I would like a child Grails app, say, myapp.war (which uses grails-myplugin) to override the 2 fizz properties:
// myapp's Config.groovy:
myplugin {
fizz {
// whistles property not defined here so this app uses the
// plugin's default of "true"
// Overrides the plugin's value of 3; this imples
// grails.plugins.somePlugin.foo is '12-40'
buzz = 12
}
}
A few issues here:
Have I placed everything correctly for the desired functionality?
With the above configuration, in grails-myplugin's Config.groovy, I have an error message:
Multiple markers at this line: - The type groovy.lang.MetaClass cannot be resolved. It is indirectly referenced from required .class files. - The type groovy.lang.GroovyObject cannot be resolved. It is indirectly referenced from required .class files.
The fact that I'm getting this error tells me that I'm ether trying to do something that is impossible in Grails, or that I'm just doing it wrong. Ideas?
Here you have how the quartz plugin resolved it:
https://github.com/grails-plugins/grails-quartz/blob/master/QuartzGrailsPlugin.groovy
Take a look at loadQuartzConfig method.
Also be aware that grails plugin exclude some parts of it to avoid problems when installing. It's configured on the plugin file like this:
def pluginExcludes = [
'grails-app/jobs/**',
'src/docs/**',
'web-app/**'
]

Change ActiveMQConnectionFactory brokerURL at runtime in grails

I've got a bean defined in resource.groovy like
beans = {
jmsConnectionFactory(org.apache.activemq.pool.PooledConnectionFactory) {bean ->
connectionFactory = {org.apache.activemq.ActiveMQConnectionFactory cf ->
brokerURL = brokerDestination
}
}
}
Now, is there a way to dynamically change the brokerURL at runtime and therefore also restart the activemq connection?
An easier solution will be to define multiple connection factories for each of your brokers and corresponding bean destinations. You can then swap out the destinations conditionally in your code based on your requirement - there would not be a easy way to go back and modify the bean definitions once the beans have been initialized.

Resources