Configuring the Typesafe Stack
by havoc
Update: see also this post on JSON-like config formats.
My latest work project was a quick side-track to unify the config file handling for Akka 2.0 and Play 2.0. The result is on GitHub and feels pretty well-baked. Patches have now landed in both Akka and Play, thanks to Patrik and Peter.
I can’t make this project seem glamorous. It was a code cleanup that reinvented one of the most-reinvented wheels around. But I thought I’d introduce this iteration of the wheel for those who might encounter it or want to adopt it in their own project.
The situation with Akka 1.2 and Play 1.2 was:
- Akka 1.2 used a custom syntax that was JSON-like semantically, but prettier to human-edit. It supported features such as including one file in another.
- Play 1.2 used a Java properties file that was run through Play’s template engine, and supported some other custom stuff such as substituting environment variables (the syntax looked like
${HOME}
).
Akka’s format looked like this:
actor { timeout = 5 serialize-messages = off }
While Play was like this:
application.name=yabe db=${DATABASE_URL} date.format=yyyy-MM-dd
With the new 2.0 setup, both Akka and Play support your choice of three formats: JSON, Java properties, or a new one called “Human-Optimized Config Object Notation“. You can mix and match; if you have multiple files in different formats, their contents are combined.
HOCON has a flexible syntax that can be JSON (it’s a superset), or look much like Akka’s previous file format, or look much like a Java properties file. As a result, some existing Akka and Play config files will parse with no changes; others will require minor changes.
A single configuration file for the whole app
Play 1.2 has a single configuration file; everything you might want to set up is done in application.conf
. We wanted to keep a single configuration, even as Play 2.0 adds a dependency on Akka.
With the new setup, once Play moves to Akka 2.0, you should be able to set Akka settings in your Play application.conf
. If other libraries in your app also use the config lib, you should be able to set their settings from this single config file, as well.
To make this happen, apps and libraries have to follow some simple conventions. A configuration is represented by an object called a Config, and after loading a Config
applications should provide it to all of their libraries. Libraries should have a way to accept a Config object to use, as shown in this example.
Applications can avoid having to pass a Config
instance around by using the default Config
instance; to make this possible, all libraries should use the same default, obtained from ConfigFactory.load(). This default loads application.conf
, application.json
, and application.properties
from the classpath, along with any resources called reference.conf
.
For a given app, either everyone gets a Config
passed down from the app and uses that, or everyone defaults to the same “standard” Config
.
Keeping useful features from the existing formats
Akka allowed you to split up the config into multiple files assembled through include
statements, and the new format does too.
Play allowed you to grab settings such as ${DATABASE_URL}
from the system environment, and the new format does too.
In the spirit of those two features, the new format also allows ${}
references within the config, which enables “inheritance” and otherwise avoids cut-and-paste; there are some examples in the README.
Migration path
Some existing Akka and Play config files will parse unchanged in the new format. Handling of special characters, escaping, and whitespace does differ, however, and you could encounter those differences. To migrate from an existing Play application.conf
, you can use one of two strategies:
- Rename the file to
application.properties
, which will make the escaping rules more like the old format. However, you won’t be able to use environment variable substitution, it’s just a plain vanilla properties file. - Add quoting and escaping. If you get parse errors, add JSON-style double quotes around the strings causing the problem.
Akka is similar; if you have parse errors, you might need quoting and escaping to avoid them. The error messages should be clear: if they are not, let me know.
There’s a section in the HOCON spec (search for “Note on Java properties”, near the end) with a list of ways the new format differs from a Java properties file.
Override config at deploy time
After compiling your app, you may want to modify a configuration at deploy time. This can be done in several ways:
- With environment variables if you refer to them using
${DATABASE_URL}
syntax in your config. - System properties override the config, by default. Set
-Dfoo.bar=42
on the command line and it will replacefoo.bar
in the app’s config. - Force an alternative config to load using the system properties
config.file
,config.resource
, orconfig.url
. (Only works on apps using the defaultConfigFactory.load()
, or apps that independently implement support for these properties.)
A more machine-friendly syntax
To generate the previous Play or Akka formats, you would need custom escaping code. Now you can just generate JSON or a properties file, using any existing library that supports those standard formats.
Implemented in Java
The config library is implemented in Java. This allows Java libraries to “join” the config lib way of doing things. In general the Typesafe stack (including Play and Akka) has both Java and Scala APIs, and in this case it seemed most appropriate to implement in Java and wrap in Scala.
That said, I haven’t implemented a Scala wrapper, since it seems barely necessary; the API is small and not complicated. You can easily create an implicit enhancement of Config with any convenience methods you would like to have. While the API is a Java API, it does some things in a Scala-inspired way: most notably the objects are all immutable.
The implementation is much larger and more complex than it would have been if it were implemented in Scala. But I endured the Java pain for you.
Conventional way of managing defaults
By convention, libraries using the config lib should ship a file called reference.conf
in their jar. The config lib loads all resources with that name into ConfigFactory.defaultReference() which is used in turn by ConfigFactory.load()
. This approach to loading defaults allows all libraries to contribute defaults, without any kind of runtime registration which would create ordering problems.
(By the way: all of these conventions can be bypassed; there are also methods to just parse an arbitrary file, URL, or classpath resource.)
Well-defined merge semantics
The HOCON spec defines semantics for merging two config objects. Merging happens for duplicate keys in the same config file, or when combining multiple config files.
The API exports the merge operation as a method called withFallback()
. You can combine any two config objects like this:
val merged = config.withFallback(otherConfig)
And you can combine multiple config objects with chained invocations of withFallback()
, for example:
val merged = configs.reduce(_.withFallback(_))
withFallback()
is associative and config objects are immutable so the potentially-parallel reduce()
should work fine.
Retrieving settings
This is straightforward:
val foobar = config.getInt("foo.bar")
The getters such as getInt()
throw an exception if the setting is missing or has the wrong type. Typically you have a reference.conf
in your jar, which should ensure that all settings are present. There’s also a method checkValid()
you can use to sanity-check a config against the reference config up front and fail early (this is nicer for users).
Each Config object conceptually represents a one-level map of paths to non-null values, but also has an underlying JSON-parse-tree-style representation available via the root()
method. root()
gives you a ConfigObject which corresponds pretty exactly to a JSON object, including null values and nested child object values. Config
and ConfigObject
are alternative views on the same data.
Any subtree of a config is just as good as the root; handy if you want multiple separately-configurable instances of something.
Configuration as data or code
I know many people are experimenting with configuration as Scala code. For this cleanup, we kept configuration as data and implemented the library in plain Java. My impression is that often a machine-manipulable-as-data layer ends up useful, even though there’s a code layer also. (See your .emacs
file after using M-x customize
, for example, it inserts a “do not edit this” section; or, SBT’s equivalent of that section is the .sbt
file format.) But we did not think about this too hard here, just kept things similar to the way they were in 1.2, while improving the implementation.
Have fun
Not a whole lot else to it. Please let me know if you have any trouble.
where are the various environments/”framework ids” play supported???
like
%dev.db.url=…
%server.db.url=….
%customerpreview.db.url=…
I NEED this feature!
at the moment that would be a Play-specific transformation it would apply to the Config object prior to using it or handing it off to any libraries such as Akka. I don’t think it needs to be in the generic file format or library, but I could be wrong. I haven’t looked at the Play code for this.
the implementation could be, for any key starting with % in root(), if it matches the current mode, pull the subtree underneath that key up to the root. so if you have { “%prod” : { “foo” : 42 } } and are in prod mode, you’d merge { “foo” : 42 } with the root. nice use of withFallback() and subtree nesting. There are other ways to code it though.
I posted some more detail to http://groups.google.com/group/play-framework/browse_thread/thread/8548cb53826c146e?hl=en
I wasn’t aware that Play 2.0 had dropped this feature (before switching to the new config library).
Yes, indeed, the abilitity to define custom configuration by framework id, and the way it’s implemented in play framework, is very useful.
I think it could be added to the generic file format, any project can certainly benefit from an easy and clear way to define custom conf for different environments…
What I’m trying to figure out about doing it generically is where the mode would come from. my only idea is a config.mode within the config itself. there are some other questions too like when to apply the mode settings while building the ConfigFactory.load merged config (before or after resolve, with or without reference config and overrides).
config/src/main/java/com/typesafe/config/ConfigException.java > ValidationFailed.makeMessage() should check for the problems count to be greater than 0 (StringBuilder length to be greater than 2) before chopping last comma and space, shouldn’t it? regards, Max
thanks – I might throw ConfigException.BugOrBroken if an empty problem count is provided, since the exception won’t really make sense with no problems in it.
[…] Pennington (@havocp) blogged about ‘Configuring the Typesafe Stack‘, covering the changes to akka.conf and Play application.conf in #playframework and #akka […]
For per-enviroment configuration what about using the withFallback mechanism? You would have one standard config plus N other configs. One of those configs would be the config for the environment. Another could be for a specific JVM in that environment that needs special tweaks.
You would specify the list of configs as JVM system properties.
Hi guys,
I’d like to configure protobuf to be the default serialization for my messages.
Here what I do in attempt to make it happen:
import akka.actor._
import akka.pattern.ask
import akka.serialization._
import akka.util._
import play.api._
import play.libs.Akka
import akka.util.Timeout
object Engine {
implicit val timeout = Timeout(1 second)
val ngin = Akka.system.actorOf(Props[Engine])
}
class Engine extends Actor {
val ser = SerializationExtension(context.system)
override def preStart() = {
println(“Got %s serializer for %s”
.format(ser.findSerializerFor(classOf[java.lang.String]).getClass().getName(),
classOf[java.lang.String].getClass().getName()))
}
def receive = { … }
}
While my application.conf file contains the following section:
play { akka { actor {
serializers { proto = “akka.serialization.ProtobufSerializer” }
serialization-bindings { “java.io.Serializable” = proto
“java.lang.String” = proto }
} } }
Though I always get JavaSerializer: “Got
akka.serialization.JavaSerializer serializer for java.lang.String”
What am I doing wrong?!
Random survey of options (4/2013):
I like Configrity because of the reader monad, and ability to simply implement your own value converters. Though really one should only need a few custom value converters (dates, big decimals, some other primitives) because any compound object could be represented by the hierachical/nesting of configs.
Configrity——
Features:
Configuration chaining.
Reader monad api.
Automatic value conversion via type classes.
Complex values: List, File, Color, URL, URI.
Hierarchical config blocks. YAML. Java properties.
Load from any Source, sys prop, env var, or classpath resource.
Save to file.
Examples:
val v = conf[String](“key”)
conf.contains(“key”)
val v:Option[Int] = conf.get[Int](“key”)
val v = conf(“key”, 3)
val c2 = conf.set(“key”,true)
val c3 = conf.clear(“key”)
Simplicity for basic usage/documentation: Great
Test suite confidence:
Age/Activity:
TypeSafe Config———–
Features:
Complex values: Duration, conversions between primitive types.
Substitutions
Java Properites, JSON, human friendly JSON. Hierarchical/nesting.
Merge files, any format.
Load from file, URL, classpath, sys prop, env var
Tighter integration with overriding general library configurations?
Examples:
int v = conf.getInt(“key”)
Config c = conf.getConfig(“key”)
Simplicity for basic usage/documentation: Great
Grizzled Configuration———-
Features: basic, some conveniences
Simplicity for basic usage/documentation: Good
Bee Config————–
Features: basic, conveniences, some extra features like merging
Fig—————
Features: very basic, json only
Java libraries? ————-
[…] know of Configgy. Also, Akka/Play 2.0 will be controlling Config, that looks good too. See blog about a […]
[…] I found a nice real-world example, JSON-like configuration file formats, where reasonable developers have implemented many points on a complexity spectrum. (Full disclosure: I implemented one of these.) […]