The target of this recipe is to show how to use JSON as a format for configuring a Groovy application.
The requirements for a configuration file format are:
JSON fully satisfies the above requirements, thus making a great candidate for configuring an application.
For this recipe, we work on a fictional application that maintains a connection to multiple databases. Each database has a number of parameters to configure such as host, port, credentials, and connection pool initial size.
The following is a sample JSON based configuration file for our application:
{ "configuration":{ "database":[ { "name":"database1", "host":"10.20.30.40", "port":"4930", "user":"user-alpha", "password":"secret", "pool-initial-size":"10", "pool-max-size":"10" }, { "name":"database2", "host":"192.168.10.30", "port":"5001", "user":"user-beta", "password":"secret", "pool-initial-size":"5", "pool-max-size":"30" } ] } }
It takes very little code (compared to Java) to build a simple configuration handler in Groovy.
Let's start by copying the JSON configuration displayed above to a file named db-config.json
. For the sake of conciseness, let's keep all the code in one single Groovy script: create a new file named configurator.groovy
.
import groovy.json.* import groovy.transform.TupleConstructor @TupleConstructor class Database { String name String host Integer port String user String password Integer initPool Integer maxPool }
This is a terse Groovy Bean (see the Writing less verbose Java Beans with Groovy Beans recipe in Chapter 3, Using Groovy Language Features) with a bunch of attributes which are mapped 1-1 to the configuration data.
Database
class definition:class AppConfig { def databases = [] AppConfig(conf) { if (conf) { conf.configuration .database.each { databases << new Database( it.name, it.host, it.port.toInteger(), it.user, it.password, it.'pool-initial-size'.toInteger(), it.'pool-max-size'.toInteger()) } } } def getDatabaseConfig = { name -> databases.find { it.name == name } } }
def reader = new FileReader('db-config.json') def conf = new JsonSlurper().parse(reader) def dbConfig = new AppConfig(conf) // Execute application logic println dbConfig.getDatabaseConfig('database2').host println dbConfig.getDatabaseConfig('database1').user ...
192.168.10.30 user-alpha
The AppConfig
class takes java.util.HashMap
as constructor parameter, which is the outcome of a parsed JSON payload using JsonSlurper
.
For each database found in the data structure, a new Database
object is built and added to the list of databases to configure. The Database
object doesn't force us to specify the constructor argument name, because the POGO class is annotated with @TupleConstructor
.
The getDatabaseConfig
closure elegantly returns the database configuration object that matches the given name of the database to configure.
If found, the database properties are accessible as Groovy Bean getters.
Another popular format similar to JSON for dealing with the configuration data is YAML.
YAML is a lightweight data serialization format designed with readability in mind and with support for hierarchical data structures. YAML stands for YAML Ain't Markup Language. It is easier on the eyes than JSON and sports a simple typing system.
Additionally, YAML can be considered a superset of JSON. That means, a valid JSON file is also a valid YAML file (but not vice versa).
The ultimate goal of YAML is to be very comfortable on the eyes. The following is an example of YAML formatted data.
firstname: Mike lastname: Ross position: CFO
Groovy doesn't have any native YAML parser. So, the most convenient approach is to use a third-party Java library, such as JYaml.
@Grab('org.jyaml:jyaml:1.3') import org.ho.yaml.Yaml yamlData = ''' firstname: Mike lastname: Ross position: CFO '' ' class Staff { def firstname, lastname, position } Staff s = Yaml.loadType(yamlData, Staff) println s.firstname println s.position
The script will print:
Mike CFO
This solution doesn't use any Groovy specific feature, but it nevertheless represents a good compromise if YAML is among the requirements of your application.
Another alternative to configure your scripts is to use Groovy's native ConfigSlurper
class, which is capable of consuming configuration data defined in the form of Groovy scripts.