Adventures in HttpContext All the stuff after 'Hello, World'

Using Typesafe’s Config for Scala (and Java) for Application Configuration

I recently leveraged Typesafe’s Config library to refactor configuration settings for a project. I was very pleased with the API and functionality of the library.

The documentation is pretty solid so there’s no need to go over basics. One feature I like is the clear hierarchy when specifying configuration values. I find it helpful to put as much as possible in a reference.conf file in the /resources directory for an application or library. These can get overridden in a variety of ways, primarily by adding an application.conf file to the bundled output’s classpath. The sbt native packager, helpful for deploying applications, makes it easy to attach a configuration file to an output. This is helpful if you have settings which you normally wouldn’t want to use during development, say using remote actors with akka. I find placing a reasonable set of defaults in a reference.conf file allows you to easily transport a configuration around while still overriding it as necessary. Otherwise you can get into copy and paste hell by duplicating configurations across multiple files for multiple environments.

Alternative Overrides

There are two other interesting ways you can override configuration settings: using environment variables or java system properties. The environment variable approach comes in very handy when pushing to cloud environments where you don’t know what a configuration is beforehand. Using the ${?VALUE} pattern a property will only be set if a value exists. This allows you to provide an option for overriding a value without actually having to specify one.

Here’s an example in a conf file using substitution leveraging this technique:

http {
 port = 8080
 port = ${?HTTP_PORT}
}

We’re setting a default port of 8080. If the configuration can find a valid substitute it will replace the port value with the substitute; otherwise, it will keep it at 8080. The configuration library will look up its hierarchy for an HTTP_PORT value, checking other configuration files, Java system properties, and finally environment variables. Environment variables aren’t perfect, but they’re easy to set and leveraged in a lot of places. If you leave out the ? and just have ${HTTP_PORT} then the application will throw an exception if it can’t find a value. But by using the ? you can override as many times as you want. This can be helpful when running apps on Heroku where environment variables are set for third party services.

Using Java System Properties

Java system properties provide another option for setting config values. The shell script created by sbt-native-packager supports java system properties, so you can also set the http port via the command line using the -D flag:

bin/bash_script_from_native_packager -Dhttp.port=8081

This can be helpful if you want to run an akka based application with a different log level to see what’s going on in production:

bin/some_akka_app_script -Dakka.loglevel=debug

Unfortunately sbt run doesn’t support java system properties so you can’t tweak settings with the command line when running sbt. The sbt-revolver plugin, which allows you to run your app in a forked JVM, does allow you to pass java arguments using the command line. Once you’re set up with this plugin you can change settings by adding your Java overrides after ---:

re-start --- -Dhttp.port=8081

With c3p0

I was really excited to see that the c3p0 connection pool library also supports Typesafe Config. So you can avoid those annoying xml-based files and merge your c3p0 settings directly with your regular configuration files. I’ve migrated an application to a docker based development environment and used this c3p0 feature with docker links to set mysql settings:

app {
 db {
  host = localhost
  host = ${?DB_PORT_3306_TCP_ADDR}
  port = "3306"
  port = ${?DB_PORT_3306_TCP_PORT}
 }
}

c3p0 {
 named-configs {
  myapp {
      jdbcUrl = "jdbc:mysql://"${app.db.host}":"${app.db.port}"/MyDatabase"
  }
 }
}

When I link a mysql container to my app container with --link mysql:db Docker will inject the DB_PORT_3306_TCP_* environment variables which are pulled by the above settings.

Accessing Values From Code

One other practice I like is having a single “Config” class for an application. It can be very tempting to load a configuration node from anywhere in your app but that can get messy fast. Instead, create a config class and access everything you need through that:

object MyAppConfig {
  private val config =  ConfigFactory.load()

  private lazy val root = config.getConfig("my_app")

  object HttpConfig {
    private val httpConfig = config.getConfig("http")

    lazy val interface = httpConfig.getString("interface")
    lazy val port = httpConfig.getInt("port")
  }
}

Type safety, Single Responsibility, and no strings all over the place.

Conclusion

When dealing with configuration think about what environments you have and what the actual differences are between those environments. Usually this is a small set of differing values for only a few properties. Make it easy to change just those settings without changing–or duplicating–anything else. This could done via environment variables, command line flags, even loading configuration files from a url. Definitely avoid copying the same value across multiple configurations: just distill that value down to a lower setting in a hierarchy. By minimizing configuration files you’ll be making your life a lot easier.

If you’re developing an app for distribution, or writing a library, providing a well-documented configuration file (spray’s spray-can reference.conf is an excellent example) you can allow users to override defaults easily in a manner that is suitable for them and their runtimes.