Thursday, April 3, 2008

Boring stuff you have to implement: Configuration, part 1

I don't need to tell you that configuration is a must-have for any application; if it doesn't have any configuration it is either extremely dumb, or it is is extremely smart (i.e., figuring out how to configure itself at runtime).

I don't consider my applications dumb enough to not need configuration, and I don't consider myself smart enough to develop applications that doesn't need configuration. So, where does that leave me? In the realm of not-so-expressive syntaxes with implicit semantics and hardcoded defaults scattered and hidden deep inside the source code, of course. Fasten your seat belts -- configuration hell, here we come!

Ok, to get to the point, this serie of posts will focus on how to abstractly express the configuration needed by a piece of source code within the source code itself (locality is the shit). Details, such as how to read configuration files, are way too boring for me to discuss on my spare time... yeah, really. How to handle a read configuration, on the other hand, that's interesting enough for me.

I guess that you, like me, often see code like this:

/**
 * The configuration of the result/output of the application.
 */
public interface ResultConfiguration {

  /**
   * Get the filename of the file to write the result to.
   * This is configured by the user before startup.
   * If not set, /dev/null is returned.
   */
  String nameOfOutputFile();
}

which is actually quite nice because it's an interface that can be stubbed in tests, and its also quite well documented in a way that is understandable for someone who has not seen the code before.

What's not so very nice is that the description of the configuration is implicitly given in comments. The same is true for the description of the class, and, even worse, for the default value which is likely to change.

Ok, so documenting the configuration is good, but its bad to use comments. How do we get the best of both worlds? We could use java.util.Properties or simething similar, and specify default values in the source; but how fun is that? Not at all. Programmers just want to have fun, as Cyndi sang back in '84. Let's use annotations!

The code above can be expressed as:

@ConfigCategory(
  description = "Controls various aspects of the output.",
  name = "result/output")
public interface ResultConfiguration {


  @ConfigParam(description = "The name of the output file.",
    settable = Settable.BeforeStartUp,
    defaultValue = "/dev/null")
  String nameOfOutputFile();
}


I'll go into details later how to actually use the annotations, right now I'll just say that it involves reflection and dynamic proxies. Or, to paraphrase Fermat, "I have a truly marvellous implementation of this interface which this post is too short to contain." :)

No comments: