Retrieving Config Values In Grails® 3
August 31, 2016
Introduction
The Grails® framework leverages the “Convention Over Configuration” design paradigm, which functions to decrease the number of decisions that a developer using the framework is required to make without losing flexibility. This is one of the ways Grails significantly increases developer productivity!
While Grails applications often involve considerably less configuration than similar applications built with other frameworks, some configuration may still be necessary. This post will detail a number of mechanisms that make it easy for Grails developers gain access to those configuration values at runtime.
application.yml
The default configuration file for Grails 3 applications is grails-app/conf/application.yml
. In this file, YAML syntax is supported.
dataSource:
pooled: true
jmxExport: true
driverClassName: org.h2.Driver
That file defines 3 configuration properties.
dataSource.pooled = true
dataSource.jmxExport = true
dataSource.driverClassName = 'org.h2.Driver'
This article will not go into detail about YAML syntax. For that see the main YAML site. Instead, this article will focus on Grails 3 specific features related to accessing those configuration values.
The Config Property in GrailsApplication
The GrailsApplication interface defines the getConfig method which returns a Config object. In the Spring application context is a bean named grailsApplication
which is an instance of a class which implements the GrailsApplication
interface. Retrieving the config object from this bean is one way to gain access to config values.
# grails-app/conf/application.yml
max:
line:
numbers: 42
// grails-app/init/BootStrap.groovy
import grails.core.GrailsApplication
class BootStrap {
// this property will be auto-wired from
// the Spring application context...
GrailsApplication grailsApplication
def init = { servletContext ->
// retrieve the max.line.numbers config value
def maxLineNumbers = grailsApplication.config.getProperty('max.line.numbers')
// ...
}
}
NOTE:
config.getProperty('max.line.numbers')
is preferred over something likeconfig.max.line.numbers
. The latter can result in unexpected behavior when retrieving values that do not exist and also is less performant.
Type Conversions
There is an overloaded version of the getProperty
method that accepts a type. This overloaded method will convert the corresponding config value to the specified type.
// retrieve the max.line.numbers config value
// returns null if the config value does not
// exist or if the type conversion fails
Integer maxLineNumbers = config.getProperty('max.line.numbers', Integer)
Default Values
There is another overloaded version of the getProperty
method which accepts both a type and a default value.
// retrieve the max.line.numbers config value
// returns 2112 if the config value does not
// exist or if the type conversion fails
Integer maxLineNumbers = config.getProperty('max.line.numbers', Integer, 2112)
Required Properties
For required properties you could write application code that reacts however is appropriate if the property doesn't exist. You could also use the getRequiredProperty method which will throw an exception if a requested property does not exist.
// retrieve the max.line.numbers config value
// throws IllegalStateException if the
// config value does not exist
Integer maxLineNumbers = config.getRequiredProperty('max.line.numbers', Integer)
Config Dependency Injection
For config values that are needed during request processing, an application may want to retrieve the value only once instead of retrieving it repeatedly during each request that requires access to the config value. For example, the following is not ideal because the application is paying the performance price of retrieving the config value every time the controller action is invoked.
import grails.config.Config
class SomeController {
def someAction() {
Config config = grailsApplication.config
// this would happen for every request to this action...
int maxLineNumbers = config.getProperty('max.line.numbers', Integer, 10)
// ...
}
}
Config Injection Using GrailsConfigurationAware
An alternative is to retrieve the config value only once and then hold on to it so that it may be used later as many times as necessary. One way to do this is to have the config injected into any bean by implementing the GrailsConfigurationAware interface. There is a bean post processor that will discover all beans that implement at that interface and that post processor will invoke the setConfiguration method on those beans, passing the config object as an argument.
import grails.config.Config
import grails.core.support.GrailsConfigurationAware
class WidgetService implements GrailsConfigurationAware {
int area
def someServiceMethod() {
// this method may use the area property...
}
@Override
void setConfiguration(Config co) {
int width = co.getProperty('widget.width', Integer, 10)
int height = co.getProperty('widget.height', Integer, 10)
area = width * height
}
}
Config Injection Using @Value
Another option for injecting config values is to use the @Value annotation.
import org.springframework.beans.factory.annotation.Value
class WidgetService {
int area
@Value('${widget.width}')
int width
def someServiceMethod() {
// this method may use the width property...
}
}
In the example above, an exception will be thrown if the widget.width
config value does not exist. In order to provide a default value with the @Value
annotation, provide the default value after a :
in the expression supplied to the annotation.
import org.springframework.beans.factory.annotation.Value
class WidgetService {
@Value('${widget.width:50}')
int width
def someServiceMethod() {
// this method may use the area property...
}
}
Conclusion
The examples above detail several approaches for retrieving config values at runtime. Grails intentionally provides several options, each with its own strengths and flexibility. Knowing where and when config values need to be accessible will help dictate which approach makes the most sense for a particular use case.