Skip to content

Instantly share code, notes, and snippets.

@mcgivrer
Last active October 29, 2024 11:09
Show Gist options
  • Save mcgivrer/5cd1d102b71ec02df467e72a84243a48 to your computer and use it in GitHub Desktop.
Save mcgivrer/5cd1d102b71ec02df467e72a84243a48 to your computer and use it in GitHub Desktop.
Configuration (V2)

README

Here is a proposal for a simple Configuration file loading/managing some Key/values with typing, and controlled as a CLI command line with helper to understand usage.

Keys and Values

Each item in the ConfigAttribute have :

  • a attrName defining the configruation attribute name,
  • a helpDescription explaining the nature and usage of this attribute,
  • a cliArgName a shortcute used on CLI to set value on this attribute,
  • a configAttrName the name of this attribute in the configruation file (a standard properties file),
  • a defaultValue a default value fotr this attribute to de set if nothing is done on configuration file or from the CLI,
  • a Function<String, Object> parserFunction a function implmening the parsing of the valie from CLI and config file to convert the string value to a typed value object.

Configuration loading and parsing

The Configuration class provide a loadFrom method to load configuration values from a properties file. It also provide a helper parseArguments to parse the famous String[] args array from the CLI and assign the right configuration value to the right key.

Usage

private boolean init(String[] args) {

    // load configuration from file.
    List<String> lArgs = Arrays.asList(args);
    config = new Configuration(ConfigAttribute.values());
    if (!lArgs.isEmpty()) {
        config.parseArguments(lArgs);
    } else {
        System.out.println(I18n.getMessage("app.message.execution.no.argument"));
    }
    String configFilePath = (String) Configuration.get(ConfigAttribute.CONFIG_FILE_PATH);
    return config.loadFrom(configFilePath);
}

That's all.

McG.

public enum ConfigAttribute {
CONFIG_FILE_PATH("configFile",
"cf",
"app.config.filepath",
"The properties file path to be loaded as configuration",
(v) -> v,
"/config.properties"),
TEST_MODE_EXIT("exit", "x",
"app.exit",
"Test mode only auto exit after initialization",
Boolean::parseBoolean,
false),
TEST_MODE_COUNTER("testCounter",
"tc",
"app.test.looping.counter",
"Define a number of loop execution during the main process.",
Integer::parseInt, -1),
DEBUG_LEVEL("debugLevel",
"dl",
"app.debug.level",
"Set the level of debugging information on log",
Integer::parseInt, 0);
private final String attrName;
private final String cliArgName;
private final String configAttrName;
private final String helpDescription;
private final Object defaultValue;
private final Function<String, Object> parserFunction;
ConfigAttribute(String attrName,
String cliArgName,
String configAttrName,
String HelpDescription,
Function<String, Object> parserFunc,
Object defaultValue) {
this.attrName = attrName;
this.cliArgName = cliArgName;
this.configAttrName = configAttrName;
this.helpDescription = HelpDescription;
this.parserFunction = parserFunc;
this.defaultValue = defaultValue;
}
public String getAttrName() {
return attrName;
}
public String getCliArgName() {
return cliArgName;
}
public String getConfigAttrName() {
return configAttrName;
}
public String getHelpDescription() {
return helpDescription;
}
public Function<String, Object> getParserFunction() {
return parserFunction;
}
public Object getDefaultValue() {
return this.defaultValue;
}
}
public class Configuration {
private static final Map<ConfigAttribute, Object> configValues = new HashMap<>();
boolean found = false;
public Configuration(ConfigAttribute[] attributes) {
loadDefaultConfigurationValues(attributes);
}
private void loadDefaultConfigurationValues(ConfigAttribute[] values) {
for (ConfigAttribute ca : values) {
configValues.put(ca, ca.getDefaultValue());
}
}
public void parseArguments(List<String> lArgs) {
lArgs.forEach(s -> {
String[] keyValue = s.split("=");
System.out.println(I18n.getMessage("app.message.execution.argument", keyValue[0], keyValue[1]));
extractConfigurationValue(keyValue[0], keyValue[1]);
});
}
private void extractConfigurationValue(String key, String value) {
Arrays.stream(ConfigAttribute.values()).forEach(ca -> {
if (key.equals(ca.getAttrName()) || key.equals(ca.getConfigAttrName()) || key.equals(ca.getCliArgName())) {
configValues.put(ca, ca.getParserFunction().apply(value));
System.out.println(
I18n.getMessage("app.message.configuration.attribute",
ca.attrName,
configValues.get(ca),
ca.getHelpDescription(),
ca.getDefaultValue())
);
found = true;
}
});
debug = (int) configValues.get(ConfigAttribute.DEBUG_LEVEL);
if (!found) {
System.out.println(I18n.getMessage("app.message.argument.unknown", key, value));
}
}
public boolean loadFrom(String filePath) {
boolean status = false;
Properties config = new Properties();
try {
System.out.println(I18n.getMessage("app.message.configuration.load", filePath));
config.load(this.getClass().getResourceAsStream(filePath));
config.entrySet().stream()
.filter(e -> e.getKey() != null)
.forEach(e -> extractConfigurationValue((String) e.getKey(), (String) e.getValue()));
status = true;
} catch (IOException e) {
System.out.println(I18n.getMessage("app.message.error.configuration.reading", e.getMessage()));
}
return status;
}
public void dispose() {
configValues.clear();
}
public static Object get(ConfigAttribute configAttribute) {
return configValues.get(configAttribute);
}
}
public static class I18n {
private static final ResourceBundle messages = ResourceBundle.getBundle("i18n/messages");
private I18n() {
// prevent from instantiate this utility class.
}
/**
* return the I18n translated message entry for the <code>key</code>.
*
* @param key the key for translated I18n message to be retrieved.
* @return the string corresponding to the I18n translated message.
*/
public static String getMessage(String key) {
return messages.getString(key);
}
/**
* return the I18n translated message entry for the <code>key</code> where <code>args</code> have been
* replaced.
*
* @param key the key for translated I18n message to be retrieved.
* @param args an unlimited list of arguments to be used into the I18n translated messages.
* @return the string corresponding to the I18n translated message with replaced arguments.
*/
public static String getMessage(String key, Object... args) {
return String.format(messages.getString(key), args);
}
}
app.message.execution.no.argument=INF | without argument
app.message.execution.argument=INF | Argument %s set to %s
app.message.configuration.attribute=INF | attribute: %s=%s (%s, default=%s)
app.message.argument.unknown=ERR | Command line argument %s set to %s: unknown, not processed
app.message.configuration.load=INF | Load configuration from file '%s'
app.message.error.configuration.reading=ERR | Unable to read configuration file: %s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment