Last active
April 5, 2024 22:15
-
-
Save mcgivrer/16685a20dcdd77f7278807d22114fbad to your computer and use it in GitHub Desktop.
Java Simple Application Template (batch like)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
app.exit=false | |
app.debug.level=2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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