I prefer flat key-value based configuration structures. They are not domain specific and can be widely used in different development environments. Also flat key-value based configuration structures are widley used in a lot of commercial project. But they have some disadvantages. The development team must maintain a central document, where the keys are stored. It always happens that developer forget to keep documentatiom up to date. Sourcecode documentation like javadoc is a poor administrator documentation, even for bigger projects with a bunch of moduls. As a solution, the development team must maintain the semantic documentation of the key-value pair away from the sourcecode. Maybe in a wiki or other technical documentations sheets.
A solution may be the metadata programming model of languages like Java (Annotation since Java 5) or C# (System.Attribute). In the following, I describe a simple system to get out of the dilemma, based on my experience with Java. The idiom is implemented in 5 different projects – only the hibernating εos-toolkit is open source. At this time there are no negative side effects.
I define the configuration keys in the sourcecode as String constants. Other types are also possible, but the make no sense. Also none constant fields make no sense. Constants in Java are static final fields. The may be public, private or protected. The public visibility has less side effects as you'll see later. The value of the constant defines the configuration key. To avaoid naming conflicts, I prefer full qualified names.
Example:
public class ConstantExample {
public static final String CONFIGURATION_KEY =
ConstantExample.class.getName() + ".something";
}
In the root package (namespace), the value is "ConstantExample.something". This technique is safe enough in order to avoid naming conflicts. The constant may be refered somewhere in the sourcecode to access the configured value. Its out of scope of this articel how to do that.
In the εos-tooklkit project I have defined a annotation to mark a constant as configuration key. The annotation contains also some information to document the semantic of the configuration key, supporting an optional default value, a type of the value and an optional predicate to check the value of the configuration key. In the εos-toolkit project the documentation is also optional. But it is always a good idea to make the documentation mandatory.
The current implementation:
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Documented
@Retention(value = RUNTIME)
@Target(value = FIELD)
public @interface ConfigurationKey {
String description();
String defaultValue() default "";
Type type() default Type.STRING;
Class<? extends Predicate<String>>[] validators() default AlwaysTruePredicate.class;
public enum Type {
BOOLEAN,
INTEGER,
FLOAT,
STRING,
CLASSNAME
}
}
The annotation field definitions are marked. If you don't understand the annotations of the annotation definition, please refer the Javadoc pages. The Retention was set to RUNTIME to get the default value at runtime if no value is configured (e.g. null). Cause of this the key constant should also be public.
The usage is quite easy:
public class ConstantExample {
@ConfigurationKey(description = "Something is a simple random value.")
public static final String CONFIGURATION_KEY =
ConstantExample.class.getName() + ".something";
}
The constant field is now marked as a configuration key. The other possible settings are:
defaultValue-
During my software development experience I've found, that many configuration values have a meaningful value. With this field it is possible to define such a default value. In a further artcile I'll describe a utility class to lookup the configuration value in the from the configuration system or returns the default value if the configuration system does not contain such a value.
If the developer does not define a default value, the value is an empty
String. type-
Defines the possible type of the configuration value. The field is for configuration tool support. Based of the value a configuration tool can supply spezialized widgets for entering configuration values.
The defined
enums above are only a recommendation of possible values. Also possible and used are the typeCLASSNAMEfor factory configurations orDATEfor a⦠yes date or IP4 and so on. validators-
Defines a list of
Predicateclass that validates the configuration value. This part is also to support configuration tools. APredicaterecieves a value, validates it and returns only atrueorfalse. The implementation should support a default constructor.If the developer does not support a
Predicateor reuse an existing one, the defalut used implementation should always returntrue.
More complex example:
public class Ip4SubnetConfigurator {
@ConfigurationKey(description = "Useable IP4 net.",
defaultValue = "10.0.0.10/20",
type = IP4,
validators = {Ip4NetPredicate.class})
public static final String CONFIGURATION_KEY =
Ip4SubnetConfigurator.class.getName() + ".start.subnet.ip4";
}
Currently there are no other useful values known. An experiment with a module definition has been identified as not useful. The module can be represented with the package or JAR. Also in an upcomming blog entry, the module is represented by the Maven artifact identifier.
With special tools, it is now possible, to introspect a bulk off classes, to generate a key-value file with the configuration values. Its also possible generate a file for configure a system and to generate documentation pages in APT, Docbook or DITA. It's also possible to supply the value directly during a nightly build via Webservices to an existing wiki system like Cobertura or Trac.
Conclusion
The ConfigurationKey annotation is a simple mechanism to mark keys for key-value configuration systems. With tools it's easy to collect them back and aggregate them. No centralized repository (e.g. wiki) are needed to document the keys.
Upcomming
Tool support with Maven.
