package envoy.data; import java.io.File; import java.io.IOException; import java.util.*; import java.util.function.Function; import java.util.logging.Level; import java.util.stream.Collectors; import envoy.util.EnvoyLog; /** * Manages all application settings that are set during application startup by * either loading them from the {@link Properties} file * {@code client.properties} or parsing them from the command line arguments of * the application.
*
* Project: envoy-client
* File: Config.java
* Created: 12 Oct 2019
* * @author Kai S. K. Engelbart * @since Envoy Common v0.1-beta */ public class Config { protected Map> items = new HashMap<>(); private boolean modificationDisabled; protected Config(String folderName) { final var rootDirectory = new File(System.getProperty("user.home"), folderName); put("homeDirectory", "home", File::new, true); ((ConfigItem) get("homeDirectory")).setValue(rootDirectory); put("fileLevelBarrier", "fb", Level::parse, true); put("consoleLevelBarrier", "cb", Level::parse, true); } /** * Parses config items from a properties object. * * @param properties the properties object to parse * @since Envoy Common v0.1-beta */ private void load(Properties properties) { items.entrySet() .stream() .filter(e -> properties.containsKey(e.getKey())) .forEach(e -> e.getValue().parse(properties.getProperty(e.getKey()))); } /** * Parses config items from an array of command line arguments. * * @param args the command line arguments to parse * @throws IllegalStateException if a malformed command line argument has been * supplied * @since Envoy Common v0.1-beta */ private void load(String[] args) { for (int i = 0; i < args.length; i++) for (final ConfigItem item : items.values()) if (args[i].startsWith("--")) { if (args[i].length() == 2) throw new IllegalStateException("Malformed command line argument at position " + i + ": " + args[i]); final String commandLong = args[i].substring(2); if (item.getCommandLong().equals(commandLong)) { item.parse(args[++i]); break; } } else if (args[i].startsWith("-")) { if (args[i].length() == 1) throw new IllegalStateException("Malformed command line argument at position " + i + ": " + args[i]); final String commandShort = args[i].substring(1); if (item.getCommandShort().equals(commandShort)) { item.parse(args[++i]); break; } } else throw new IllegalStateException("Malformed command line argument at position " + i + ": " + args[i]); } /** * Supplies default values from the given .properties file and * parses the configuration from an array of command line arguments. * * @param declaringClass the class calling this method * @param propertiesFilePath the path to where the .properties file can be found * - will be only the file name if it is located * directly inside the {@code src/main/resources} * folder * @param args the command line arguments to parse * @throws IllegalStateException if this method is getting called again or if a * malformed command line argument has been * supplied * @since Envoy Common v0.1-beta */ public void loadAll(Class declaringClass, String propertiesFilePath, String[] args) { if (modificationDisabled) throw new IllegalStateException("Cannot change config after isInitialized has been called"); // Load the defaults from the given .properties file first final var properties = new Properties(); try { properties.load(declaringClass.getClassLoader().getResourceAsStream(propertiesFilePath)); } catch (final IOException e) { EnvoyLog.getLogger(Config.class).log(Level.SEVERE, "An error occurred when reading in the configuration: ", e); } load(properties); // Override configuration values with command line arguments if (args.length > 0) load(args); // Check if all mandatory configuration values have been initialized isInitialized(); // Disable further editing of the config modificationDisabled = true; } /** * @throws IllegalStateException if a mandatory {@link ConfigItem} has not been * initialized * @since Envoy Common v0.1-beta */ private void isInitialized() { if (items.values().stream().filter(ConfigItem::isMandatory).map(ConfigItem::get).anyMatch(Objects::isNull)) throw new IllegalStateException("config item(s) has/ have not been initialized:" + items.values() .stream() .filter(configItem -> configItem.isMandatory() && configItem.get() == null) .map(ConfigItem::getCommandLong) .collect(Collectors.toSet())); } /** * @param name the name of the config item to return * @return the config item with the specified name * @since Envoy Common v0.1-beta */ public ConfigItem get(String name) { return items.get(name); } /** * Shorthand for
* {@code items.put(commandName, new ConfigItem<>(commandName, commandShort, parseFunction, defaultValue, mandatory))}. * * @param the type of the {@link ConfigItem} * @param commandName the key for this config item as well as its long name * @param commandShort the abbreviation of this config item * @param parseFunction the {@code Function} that parses the value * from a string * @param mandatory indicated that this config item must be initialized with * a non-null value * @since Envoy Common v0.2-beta */ protected void put(String commandName, String commandShort, Function parseFunction, boolean mandatory) { items.put(commandName, new ConfigItem<>(commandName, commandShort, parseFunction, mandatory)); } /** * Shorthand for
* {@code put(commandName, commandShort, parseFunction, false)}. * * @param the type of the {@link ConfigItem} * @param commandName the key for this config item as well as its long name * @param commandShort the abbreviation of this config item * @param parseFunction the {@code Function} that parses the value * from a string * @since Envoy Common v0.2-beta */ protected void put(String commandName, String commandShort, Function parseFunction) { put(commandName, commandShort, parseFunction, false); } /** * @return the directory in which all local files are saves * @since Envoy Client v0.2-beta */ public File getHomeDirectory() { return (File) items.get("homeDirectory").get(); } /** * @return the minimal {@link Level} to log inside the log file * @since Envoy Client v0.2-beta */ public Level getFileLevelBarrier() { return (Level) items.get("fileLevelBarrier").get(); } /** * @return the minimal {@link Level} to log inside the console * @since Envoy Client v0.2-beta */ public Level getConsoleLevelBarrier() { return (Level) items.get("consoleLevelBarrier").get(); } }