Skip to content

Instantly share code, notes, and snippets.

@mcgivrer
Last active April 5, 2024 22:15
Show Gist options
  • Save mcgivrer/16685a20dcdd77f7278807d22114fbad to your computer and use it in GitHub Desktop.
Save mcgivrer/16685a20dcdd77f7278807d22114fbad to your computer and use it in GitHub Desktop.
Java Simple Application Template (batch like)
app.exit=false
app.debug.level=2
package com.java.simple.app;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* Main class for project JavaSimpleAppTemplate
*
* @author Frédéric Delorme
* @since 1.0.0
*/
public class JavaSimpleAppTemplate implements Runnable {
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);
}
}
private int debug = 0;
private Configuration config;
public JavaSimpleAppTemplate() {
//messages = ResourceBundle.getBundle("i18n/messages");
System.out.println(
I18n.getMessage("app.message.execution.start",
I18n.getMessage("app.name"),
I18n.getMessage("app.version")));
}
public void run(String[] args) {
if (init(args)) {
Thread process = new Thread(this);
process.start();
}
}
@Override
public void run() {
mainLoop();
dispose();
}
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);
}
private void mainLoop() {
boolean exit = (boolean) Configuration.get(ConfigAttribute.TEST_MODE_EXIT);
int testCounter = (int) Configuration.get(ConfigAttribute.TEST_MODE_COUNTER);
int counter = 1;
System.out.println(I18n.getMessage("app.message.execution.loop.start"));
long startTime = System.nanoTime();
long previousTime = startTime;
long internalTime = 0;
Map<String, Object> stats = new ConcurrentHashMap<>();
stats.put("time_start", startTime);
while (!exit && counter <= testCounter) {
long currentTime = System.nanoTime();
long elapsed = currentTime - previousTime;
if (testCounter != -1) {
if (isDebugLevelGreaterThan(1)) {
System.out.println(
I18n.getMessage(
"app.message.execution.loop",
counter, testCounter, internalTime, elapsed));
}
counter += 1;
}
stats.put("counter", counter);
stats.put("time_elapsed", elapsed);
stats.put("time_internal", internalTime);
//processing !
process(internalTime, elapsed, stats);
previousTime = currentTime;
internalTime += elapsed;
}
System.out.println(I18n.getMessage("app.message.execution.loop.done"));
}
private boolean isDebugLevelGreaterThan(int debugLevel) {
return debug > debugLevel;
}
public void process(long currentTime, long elapsed, Map<String, Object> stats) {
// Here is where the processing happened!
}
private void dispose() {
config.dispose();
System.out.println(
I18n.getMessage("app.message.execution.end",
I18n.getMessage("app.name")));
}
public static void main(String[] argc) {
JavaSimpleAppTemplate app = new JavaSimpleAppTemplate();
app.run(argc);
}
}
app.name=JavaSimpleAppTemplate
app.version=1.0.0
app.message.execution.start=Start initialization of %s (%s)
app.message.execution.no.argument=|_ INF without argument
app.message.execution.argument=|_ INF Argument %s set to %s
app.message.execution.loop.start=Start looping
app.message.execution.loop="| - loop %1/%2 : %3 ns (d=%4 ns)"
app.message.execution.loop.done=Looping Done
app.message.execution.end=End of execution of %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=Load configuration from file '%s'
app.message.error.configuration.reading=Unable to read configuration file: %s
/**
* The core App class and its subclasses to let develop a simple java CLI application.
*
* @author Frédéric Delorme
* @since 1.0.0
*/
package com.java.simple.app;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment