Ratpacked: Using Groovy Configuration Scripts As Configuration Source
Ratpack has a lot of options to add configuration data to our application.
We can use for example YAML and JSON files, properties, environment variables and Java system properties.
Groovy has the ConfigSlurper
class to parse Groovy script with configuration data.
It even supports an environments
block to set configuration value for a specific environment.
If we want to support Groovy scripts as configuration definition we write a class that implements the ratpack.config.ConfigSource
interface.
We create a new class ConfigSlurperConfigSource
and implement the ConfigSource
interface.
We must implement the loadConfigData
method in which we read the Groovy configuration and transform it to a ObjectNode
so Ratpack can use it:
// File: src/main/groovy/mrhaki/ratpack/config/ConfigSlurperConfigSource.groovy
package mrhaki.ratpack.config
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.ArrayNode
import com.fasterxml.jackson.databind.node.ObjectNode
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import ratpack.config.ConfigSource
import ratpack.file.FileSystemBinding
import java.nio.file.Path
@CompileStatic
class ConfigSlurperConfigSource implements ConfigSource {
private final String configScript
private final URL scriptUrl
private final String environment
ConfigSlurperConfigSource(final String configScript) {
this(configScript, '')
}
ConfigSlurperConfigSource(final String configScript, final String environment) {
this.configScript = configScript
this.environment = environment
}
ConfigSlurperConfigSource(final Path configPath) {
this(configPath, '')
}
ConfigSlurperConfigSource(final Path configPath, final String environment) {
this(configPath.toUri(), environment)
}
ConfigSlurperConfigSource(final URI configUri) {
this(configUri, '')
}
ConfigSlurperConfigSource(final URI configUri, final String environment) {
this(configUri.toURL(), environment)
}
ConfigSlurperConfigSource(final URL configUrl) {
this(configUrl, '')
}
ConfigSlurperConfigSource(final URL configUrl, final String environment) {
this.scriptUrl = configUrl
this.environment = environment
}
@Override
ObjectNode loadConfigData(
final ObjectMapper objectMapper,
final FileSystemBinding fileSystemBinding) throws Exception {
// Create ConfigSlurper for given environment.
final ConfigSlurper configSlurper = new ConfigSlurper(environment)
// Read configuration.
final ConfigObject configObject =
configScript ?
configSlurper.parse(configScript) :
configSlurper.parse(scriptUrl)
// Transform configuration to node tree
final ObjectNode rootNode = objectMapper.createObjectNode()
populate(rootNode, configObject)
return rootNode
}
@CompileDynamic
private populate(final ObjectNode node, final ConfigObject config) {
// Loop through configuration.
// ConfigObject also implements Map interface,
// so we can loop through key/value pairs.
config.each { key, value ->
// Value is another configuration,
// this means the nested configuration
// block.
if (value instanceof Map) {
populate(node.putObject(key), value)
} else {
// If value is a List we convert it to
// an array node.
if (value instanceof List) {
final ArrayNode listNode = node.putArray(key)
value.each { listValue ->
listNode.add(listValue)
}
} else {
// Put key/value pair in node.
node.put(key, value)
}
}
}
}
}
We have several options to pass the Groovy configuration to the ConfigSlurperConfigSource
class.
We can use a String
, URI
, URL
or Path
reference. Let's create a file with some configuration data.
// File: src/ratpack/application.groovy
app {
serverPort = 9000
}
environments {
development {
app {
serverName = 'local'
}
}
production {
app {
serverName = 'cloud'
serverPort = 80
}
}
}
Next we create a Ratpack application using the Groovy DSL. In the serverConfig
section we use our new ConfigSlurperConfigSource
:
// File: src/ratpack/ratpack.groovy
import com.google.common.io.Resources
import com.mrhaki.config.ConfigSlurperConfigSource
import static groovy.json.JsonOutput.prettyPrint
import static groovy.json.JsonOutput.toJson
import static ratpack.groovy.Groovy.ratpack
//final Logger log = LoggerFactory.getLogger('ratpack')
ratpack {
serverConfig {
// Use Groovy configuration.
add new ConfigSlurperConfigSource('''\\
app {
message = 'Ratpack swings!'
}''')
// Use external Groovy configuration script file.
add new ConfigSlurperConfigSource(
Resources.getResource('application.groovy'), 'development')
require '/app', SimpleConfig
}
handlers {
get('configprops') { SimpleConfig config ->
render(prettyPrint(toJson(config)))
}
}
}
// Simple configuration.
class SimpleConfig {
String message
String serverName
Integer serverPort
}
Let's check the output of the configprops
endpoint:
$ http -b localhost:5050/configprops
{
"message": "Ratpack swings!",
"serverName": "local",
"serverPort": 9000
}
Now we set the environment to production
in our Ratpack application:
// File: src/ratpack/ratpack.groovy
...
ratpack {
serverConfig {
...
// Use external Groovy configuration script file.
add new ConfigSlurperConfigSource(
Resources.getResource('application.groovy'), 'production')
...
}
...
}
If we check configprops
again we see different configuration values:
$ http -b localhost:5050/configprops
{
"message": "Ratpack swings!",
"serverName": "cloud",
"serverPort": 80
}
Written with Ratpack 1.3.3.