Layering Ratpack Configuration
Over the last few weeks I have upgraded my pet project CellarHQRatpack from the ancient Ratpack 0.9.15 to the current 1.1.1, and also moved it from being hosted on AWS Elastic Beanstalk to Heroku. In the process I redid the configuration more than 1 time. The documentation while accurate, lacks any concrete examples.
Loading configuration in Ratpack
Ratpack can be configured in several different ways, including properties files, yaml, system properties and environment variables. Here’s how I configure CellarHQ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
All configuration is added through the
ServerConfig an requiring specific stanzas of configuration including cellarhq, db etc. An instance of each config object is added to the registry. This lets the
bindings block stay very simple. Let’s examine the configuration line by line:
Heroku provides an environment variable which has all the fields for the JDBC connection which needs to be parsed and added to the Ratpack configuration manually. This is a helper utility I gratuitously stole from Modern Java Web Development.
1 2 3 4 5
This will load two configuration files, one is a properties file and one is a yaml file, from the ratpack
BaseDir. You’ll want to define this by including a
.ratpack file somewhere in your source tree. Mine is in
1 2 3
Now the herku specific configuration, system properties, environment variables are layered on top of the configuration files, in the order specified. System properties override configuration from app.properties and environment variables override those.
1 2 3 4
Finally, specific configuration objects are built using data-binding from the specified properties. An instance of each of these classes is added to the registry and available just like any other object added to the registry.
Setting the configuration
Now let’s look at the configuration itself. In
db.yaml sensible defaults are configured for every configuration item that isn’t secret like AWS or Twitter API keys. Those items are always configured at runtime and stored some place not in source control.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
1 2 3 4 5 6 7 8
Overriding with system properties or env vars
The defaults above work great for running locally, but I do override some of them for tests. I use the gradle build to set environment variables or system properties. Here’s how I configure functional tests:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
The build detects if it is running in a CI environment and maps configuration appropriately. Otherwise it sets a new database name and loads secrets from the gradle properties. I keep secrets in
~/.gradle/gradle.properties so they will never be added to source control accidentally.
There are some important things to notice here:
- Environment variables start with
RATPACK_, system properties with
- For env vars, TWO underscores are used to separate objects like CELLARHQ__
- For env vars, ONE underscore is used to separate words that are built into camel cased field names, like AWS_ACCESS_KEY.
- For system properties, the
.is used for both object and field separators.
ratpack.db.dataSourceProperties.databaseName are equivalent. Using system properties or environment variables is mostly a matter of preference.
What about lists?
I don’t have any properties which data-bind to lists, but this is possible in ratpack. Let’s say I had multiple S3 storage buckets configured, that could be configured in a properties file like this:
1 2 3
It could be done similarly with system properties. However, this DOES NOT WORK with environment variables. You cannot set items in a list using environment variable configuration.
Ratpack configuration is simple and elegant, and it is easy to layer environment specific configuration on top of defaults, I just thought there needed to be a few real life examples out there.