Groovy - Dynamic properties in Config Slurper
In this post I will provide quick tip on how to use dynamic properties in Groovy Config Slurper.
Groovy provides a nice utility ConfigSlurper for reading configuration files
where settings can be overridden to different values for different environments.
Let me illustrate it with a quick example, Given this EnvironmentConfigSimple.groovy
:
//Default values
availableDiskSpaceForData = 1024
numberOfTopics = 4
numberOfPartitions = 4
environments {
dev {
//will use default value
}
test {
availableDiskSpaceForData = 10240
}
prod {
availableDiskSpaceForData = 102400
}
}
We can read value for availableDiskSpaceForData depending on environment using ConfigSlurper :
println new ConfigSlurper("dev").parse(EnvironmentConfigSimple.class).availableDiskSpaceForData; //Outputs: 1024
println new ConfigSlurper("test").parse(EnvironmentConfigSimple.class).availableDiskSpaceForData; //Outputs: 10240
println new ConfigSlurper("prod").parse(EnvironmentConfigSimple.class).availableDiskSpaceForData; //Outputs: 102400
Let’s say we want to add a property logRetentionBytes : maximum size of data that a topic/partition can hold allotted from availableDiskSpaceForData. It can be computed dynamically by dividing availableDiskSpaceForData by total number of topic/partitions. We can do something like this
numberOfTopics = 4
numberOfPartitions = 4
availableDiskSpaceForData = 1024
environments {
dev {
availableDiskSpaceForData = 1024
logRetentionBytes = availableDiskSpaceForData / (numberOfTopics * numberOfPartitions)
}
test {
availableDiskSpaceForData = 10240
logRetentionBytes = availableDiskSpaceForData / (numberOfTopics * numberOfPartitions)
}
prod {
availableDiskSpaceForData = 102400
logRetentionBytes = availableDiskSpaceForData / (numberOfTopics * numberOfPartitions)
}
}
Here is output value of logRetentionBytes reading using ConfigSlurper
println new ConfigSlurper("dev").parse(EnvironmentConfigSimple.class).logRetentionBytes; //Outputs: 64
println new ConfigSlurper("test").parse(EnvironmentConfigSimple.class).logRetentionBytes; //Outputs: 640
println new ConfigSlurper("prod").parse(EnvironmentConfigSimple.class).logRetentionBytes; //Outputs: 6400
This works. But the code is duplicated and it will be hard to maintain. If we have more dynamic properties, it will make it even worse. It will be neat if computation logic is written once and dynamically evaluated. Groovy’s Eval utility can do the trick.
We can refactor above environment config to add calculationParams
property to define properties that will be used as input params and calculatedProperties
property to define dynamically computed properties.
Here is the revised EnvironmentConfigDynamic.groovy
calculationParams {
numberOfTopics = 4
numberOfPartitions = 4
availableDiskSpaceForData = 1024
}
calculatedProperties {
logRetentionBytes = 'calculationParams.availableDiskSpaceForData / (calculationParams.numberOfTopics * calculationParams.numberOfPartitions)'
}
environments {
dev {
calculationParams.availableDiskSpaceForData = 1024
}
test {
calculationParams.availableDiskSpaceForData = 10240
}
prod {
calculationParams.availableDiskSpaceForData = 102400
}
}
Now, we can write evaluateCalculatedProperties
method that reads calculatedProperties properties from groovy ConfigObject
and evaluates them using calculationParams as input. Then it adds result back to ConfigObject.
private ConfigObject evaluateCalculatedProperties(ConfigObject config) {
config.calculatedProperties.flatten().each { k, v ->
def value = Eval.me("calculationParams", config.calculationParams, v.toString());
config.put(k, value);
}
return config;
}
Finally, we can pass result of ConfigSlurper to evaluateCalculatedProperties method to get value of computed logRetentionBytes
println evaluateCalculatedProperties(new ConfigSlurper("dev").parse(EnvironmentConfigDynamic.class)).logRetentionBytes; //Outputs: 64
println evaluateCalculatedProperties(new ConfigSlurper("test").parse(EnvironmentConfigDynamic.class)).logRetentionBytes; //Outputs: 640
println evaluateCalculatedProperties(new ConfigSlurper("prod").parse(EnvironmentConfigDynamic.class)).logRetentionBytes; //Outputs: 6400
Source Code:
The complete source code of examples in this post is available in github.