Retrieving Config Values in Grails 3

Retrieving Config Values in Grails 3

By Jeff Scott Brown, OCI Grails Practice Lead & Principal Software Engineer

OCTOBER 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 the Grails framework 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 article details a number of mechanisms that make it easy for Grails developers to 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.

  1. ---
  2. dataSource:
  3. pooled: true
  4. jmxExport: true
  5. driverClassName: org.h2.Driver

That file defines 3 configuration properties.

  1. dataSource.pooled = true
  2. dataSource.jmxExport = true
  3. dataSource.driverClassName = 'org.h2.Driver'

This article does not go into detail about YAML syntax; for more information on YAML syntax, please visit the YAML site. This article focuses 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, there is a bean named grailsApplication, which is an instance of a class that implements the GrailsApplication interface. Retrieving the config object from this bean is one way to gain access to config values.

  1. # grails-app/conf/application.yml
  2. ---
  3. max:
  4. line:
  5. numbers: 42
  1. // grails-app/init/BootStrap.groovy
  2.  
  3. import grails.core.GrailsApplication
  4.  
  5. class BootStrap {
  6.  
  7. // this property will be auto-wired from
  8. // the Spring application context...
  9. GrailsApplication grailsApplication
  10.  
  11. def init = { servletContext ->
  12. // retrieve the max.line.numbers config value
  13. def maxLineNumbers = grailsApplication.config.getProperty('max.line.numbers')
  14.  
  15. // ...
  16.  
  17. }
  18. }

NOTEconfig.getProperty('max.line.numbers') is preferred over something like config.max.line.numbers. The latter may result in unexpected behavior when retrieving values that do not exist and is also less performant.

Type Conversions

There is an overloaded version of the getProperty method that accepts a type. This overloaded method converts the corresponding config value to the specified type.

  1. // retrieve the max.line.numbers config value
  2. // returns null if the config value does not
  3. // exist or if the type conversion fails
  4. Integer maxLineNumbers = config.getProperty('max.line.numbers', Integer)

Default Values

There is another overloaded version of the getProperty method that accepts both a type and a default value.

  1. // retrieve the max.line.numbers config value
  2. // returns 2112 if the config value does not
  3. // exist or if the type conversion fails
  4. Integer maxLineNumbers = config.getProperty('max.line.numbers', Integer, 2112)

Required Properties

For required properties, you may write application code that reacts, though is still appropriate if the property doesn’t exist. You may also use the getRequiredProperty method, which will throw an exception if a requested property does not exist.

  1. // retrieve the max.line.numbers config value
  2. // throws IllegalStateException if the
  3. // config value does not exist
  4. 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.

  1. import grails.config.Config
  2.  
  3. class SomeController {
  4.  
  5. def someAction() {
  6. Config config = grailsApplication.config
  7. // this would happen for every request to this action...
  8. int maxLineNumbers = config.getProperty('max.line.numbers', Integer, 10)
  9.  
  10. // ...
  11. }
  12. }

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 the interface; and the post processor will invoke the setConfiguration method on those beans, passing the config object as an argument.

  1. import grails.config.Config
  2. import grails.core.support.GrailsConfigurationAware
  3.  
  4. class WidgetService implements GrailsConfigurationAware {
  5.  
  6. int area
  7.  
  8. def someServiceMethod() {
  9. // this method may use the area property...
  10. }
  11.  
  12. @Override
  13. void setConfiguration(Config co) {
  14. int width = co.getProperty('widget.width', Integer, 10)
  15. int height = co.getProperty('widget.height', Integer, 10)
  16. area = width * height
  17. }
  18. }

CONFIG INJECTION USING @Value

Another option for injecting config values is to use the @Value annotation.

  1. import org.springframework.beans.factory.annotation.Value
  2.  
  3. class WidgetService {
  4.  
  5. int area
  6.  
  7. @Value('${widget.width}')
  8. int width
  9.  
  10. def someServiceMethod() {
  11. // this method may use the width property...
  12. }
  13. }

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.

  1. import org.springframework.beans.factory.annotation.Value
  2.  
  3. class WidgetService {
  4.  
  5. @Value('${widget.width:50}')
  6. int width
  7.  
  8. def someServiceMethod() {
  9. // this method may use the area property...
  10. }
  11. }

Conclusion

The examples above detail several approaches for retrieving config values at runtime. The Grails framework intentionally provides several options, each with its own strengths and flexibility. Knowing when and where config values need to be accessible helps dictate which approach makes the most sense for a particular use case.



Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.


secret