Retrieving Config Values In Grails 3

31 Aug 2016

Tags: Grails, Config

Introduction

Grails 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 like config.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 implementat 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 their 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.

Published on 31 Aug 2016