One common task in developing bookkeeper is to make sure all the configuration settings are well documented, and the configuration file we ship in each release is in-sync with the code itself.
However maintaining things in-sync is non-trivial. This proposal is exploring a new way to manage configuration settings for better documentation.
ConfigKey
for defining a configuration key. A configuration key will include informations, such as required/optional, deprecated, documentation and etc.public class ConfigKey { /** * Flag indicates whether the setting is required. */ @Default private boolean required = false; /** * Name of the configuration setting. */ private String name; /** * Type of the configuration setting. */ @Default private Type type = Type.STRING; /** * Description of the configuration setting. */ @Default private String description = ""; /** * Documentation of the configuration setting. */ @Default private String documentation = ""; /** * Default value as a string representation. */ @Default private Object defaultValue = null; /** * The list of options for this setting. */ @Default private List<String> optionValues = Collections.emptyList(); /** * The validator used for validating configuration value. */ @Default private Validator validator = NullValidator.of(); /** * The key-group to group settings together. */ @Default private ConfigKeyGroup group = ConfigKeyGroup.DEFAULT; /** * The order of the setting in the key-group. */ @Default private int orderInGroup = Integer.MIN_VALUE; /** * The list of settings dependents on this setting. */ @Default private List<String> dependents = Collections.emptyList(); /** * Whether this setting is deprecated or not. */ @Default private boolean deprecated = false; /** * The config key that deprecates this key. */ @Default private String deprecatedByConfigKey = ""; /** * The version when this settings was deprecated. */ @Default private String deprecatedSince = ""; /** * The version when this setting was introduced. */ @Default private String since = ""; }
ConfigKeyGroup
for grouping configuration keys together.public class ConfigKeyGroup { /** * Name of the key group. */ private String name; /** * Description of the key group. */ @Default private String description = ""; /** * The list of sub key-groups of this key group. */ @Default private List<String> children = Collections.emptyList(); /** * The order of the key-group in a configuration. */ @Default private int order = Integer.MIN_VALUE; }
Besides introducing ConfigKey
and ConfigKeyGroup
, this BP will also introduce a class ConfigDef
- it defines the keys for a configuration.
The ConfigDef
will be generated via ConfigDef.of(Configuration.class)
. It will retrieve all the static fields of ConfigKey
defined in the configuration class and build the configuration definition.
The ConfigDef
will also provide a save
method for saving the configuration definition as a configuration file.
Following is an example how to use ConfigKey
and ConfigKeyGroup
to organize configuration settings.
// Ledger Storage Settings private static final ConfigKeyGroup GROUP_LEDGER_STORAGE = ConfigKeyGroup.builder("ledgerstorage") .description("Ledger Storage related settings") .order(10) // place a place holder here .build(); protected static final String LEDGER_STORAGE_CLASS = "ledgerStorageClass"; protected static final ConfigKey LEDGER_STORAGE_CLASS_KEY = ConfigKey.builder(LEDGER_STORAGE_CLASS) .type(Type.CLASS) .description("Ledger storage implementation class") .defaultValue(SortedLedgerStorage.class.getName()) .optionValues(Lists.newArrayList( InterleavedLedgerStorage.class.getName(), SortedLedgerStorage.class.getName(), DbLedgerStorage.class.getName() )) .validator(ClassValidator.of(LedgerStorage.class)) .group(GROUP_LEDGER_STORAGE) .build();
Example on how to generate the ConfigDef
and use the configuration definition to validate if a configuration instance is valid.
// generate config def ConfigDef configDef = ConfigDef.of(ServerConfiguration.class); try { configDef.validate(this); } catch (ConfigException e) { throw new ConfigurationException(e.getMessage(), e.getCause()); }
Example on how to save the configuration definition to a configuration file.
ConfigDef configDef = ConfigDef.of(TestConfig2.class); String savedConf; try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { configDef.save(baos); savedConf = baos.toString(); }
It only changes the way how we organize configuration settings and how we document them. It doesn't change the public interfaces for existing configuration. So there is nothing to deprecate and migrate.
Existing testing is good enough to cover code changes. No new tests are needed.
Alternatively, we have to manually maintain the configuration files and update each time when a new configuration setting is added.