Initial commit
This commit is contained in:
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
63
pom.xml
Normal file
63
pom.xml
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>space.b00tload.utils</groupId>
|
||||
<artifactId>ConfigurationUtilities</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<name>ConfigurationUtilities</name>
|
||||
<description>A library to load config from cli-args, .env, toml-config and default values.</description>
|
||||
<inceptionYear>2024</inceptionYear>
|
||||
<url>https://github.com/B00tLoad/ConfigurationUtilities</url>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>B00tLoad_</id>
|
||||
<name>Alix von Schirp</name>
|
||||
<email>alix.von-schirp@bootmedia.de</email>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
<timezone>Europe/Berlin</timezone>
|
||||
<properties>
|
||||
<disordHandle>@b00tload_</disordHandle>
|
||||
<mastodonhandle>@b00tload_</mastodonhandle>
|
||||
<mastodoninstance>order-of-gathering.de</mastodoninstance>
|
||||
<blueskyHandle>@b00tload.space</blueskyHandle>
|
||||
<pronouns>she/they</pronouns>
|
||||
</properties>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<issueManagement>
|
||||
<system>GitHub</system>
|
||||
<url>https://github.com/B00tLoad/SnowflakeService/issues</url>
|
||||
</issueManagement>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<!-- logging-->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.5.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- config-->
|
||||
<dependency>
|
||||
<groupId>com.electronwill.night-config</groupId>
|
||||
<artifactId>toml</artifactId>
|
||||
<version>3.6.7</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,52 @@
|
||||
package space.b00tload.utils.configuration;
|
||||
|
||||
|
||||
/**
|
||||
* An interface to implement in enums containing all configurable values.
|
||||
*
|
||||
* @author Alix von Schirp
|
||||
* @version 1.0.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface ConfigValues {
|
||||
|
||||
/**
|
||||
* The command line flag for this config value.
|
||||
*
|
||||
* @return the command line flag
|
||||
* @example --discord-token
|
||||
*/
|
||||
String getFlag();
|
||||
|
||||
/**
|
||||
* The command line alias for this config value.
|
||||
*
|
||||
* @return the command line alias
|
||||
* @example -d
|
||||
*/
|
||||
String getFlagAlias();
|
||||
|
||||
/**
|
||||
* The environment variable name for this config value.
|
||||
*
|
||||
* @return the environment variable name
|
||||
* @example DISCORD_APP_TOKEN
|
||||
*/
|
||||
String getEnvironmentVariable();
|
||||
|
||||
/**
|
||||
* The path to the config value in a toml file.
|
||||
*
|
||||
* @return the toml path.
|
||||
* @example discord.token
|
||||
*/
|
||||
String getTomlPath();
|
||||
|
||||
/**
|
||||
* The default value used if not set via other configuration means.
|
||||
*
|
||||
* @return the default value.
|
||||
*/
|
||||
String getDefaultValue();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package space.b00tload.utils.configuration;
|
||||
|
||||
import space.b00tload.utils.configuration.exceptions.ConfigException;
|
||||
import space.b00tload.utils.configuration.exceptions.ConfigIncompleteException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Loads the configuration from all supported sources (cli-args, .env, toml-file, default).
|
||||
*
|
||||
* @author Alix von Schirp
|
||||
* @version 1.0.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class Configuration {
|
||||
|
||||
/**
|
||||
* The singleton instance
|
||||
*/
|
||||
private static Configuration INSTANCE;
|
||||
/**
|
||||
* Holds all configured values
|
||||
*/
|
||||
private final HashMap<ConfigValues, String> values = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Loads all values from all config sources. Errors out if values are missing.
|
||||
*
|
||||
* @param args The cli-args provided at software launch
|
||||
* @param currentSoftwareVersion The current version number of the software using this util
|
||||
* @param applicationBaseDir The directory in which the config folder and file are to be created
|
||||
* @param configValues The enum.values() array of the ConfigValues enum.
|
||||
* @throws ConfigIncompleteException if not all values without a default value were set using other configuration methods.
|
||||
*/
|
||||
private Configuration(String[] args, String currentSoftwareVersion, String applicationBaseDir, ConfigValues[] configValues) throws ConfigIncompleteException {
|
||||
//load values set in cli-args
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
for (ConfigValues v : configValues) {
|
||||
if (v.getFlag().equalsIgnoreCase(args[i]) || v.getFlagAlias().equals(args[i])) {
|
||||
if (args[i + 1].startsWith("-")) continue;
|
||||
values.put(v, args[i + 1]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//load yet unset values from .env
|
||||
for (ConfigValues v : configValues) {
|
||||
if (values.containsKey(v)) continue;
|
||||
if (System.getenv().containsKey(v.getEnvironmentVariable()))
|
||||
values.put(v, System.getenv(v.getEnvironmentVariable()));
|
||||
}
|
||||
|
||||
//load yet unset values from toml config file
|
||||
try (TomlConfiguration tomlConfig = new TomlConfiguration(applicationBaseDir, currentSoftwareVersion)) {
|
||||
for (ConfigValues v : configValues) {
|
||||
if (values.containsKey(v)) continue;
|
||||
if (tomlConfig.contains(v.getTomlPath())) values.put(v, tomlConfig.getString(v.getTomlPath()));
|
||||
}
|
||||
}
|
||||
|
||||
//load default values for as of yet unset methods
|
||||
for (ConfigValues v : configValues) {
|
||||
if (values.containsKey(v)) continue;
|
||||
values.put(v, v.getDefaultValue());
|
||||
}
|
||||
|
||||
//find all unset config values
|
||||
List<ConfigValues> missingValues = new ArrayList<>();
|
||||
for (ConfigValues v : values.keySet()) {
|
||||
if (values.get(v) == null) missingValues.add(v);
|
||||
}
|
||||
|
||||
//Error out on incomplete config
|
||||
if (!missingValues.isEmpty()) throw new ConfigIncompleteException("Missing values: ", missingValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to get the singleton instance
|
||||
*
|
||||
* @return the singleton instance.
|
||||
*/
|
||||
public static Configuration getInstance() {
|
||||
if (INSTANCE == null) throw new ConfigException("Configuration not initialized");
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the singelton instance.
|
||||
*
|
||||
* @param args The cli-args provided at software launch
|
||||
* @param currentSoftwareVersion The current version number of the software using this util
|
||||
* @param applicationBaseDir The directory in which the config folder and file are to be created
|
||||
* @param configValues The enum.values() array of the ConfigValues enum.
|
||||
* @throws ConfigIncompleteException if not all values without a default value were set using other configuration methods.
|
||||
*/
|
||||
public static void init(String[] args, String currentSoftwareVersion, String applicationBaseDir, ConfigValues[] configValues) throws ConfigIncompleteException {
|
||||
INSTANCE = new Configuration(args, currentSoftwareVersion, applicationBaseDir, configValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the config
|
||||
*
|
||||
* @param v the key of the value to be returned
|
||||
* @return the requested value
|
||||
*/
|
||||
public String get(ConfigValues v) {
|
||||
return values.get(v);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package space.b00tload.utils.configuration;
|
||||
|
||||
import com.electronwill.nightconfig.core.file.FileConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import space.b00tload.utils.configuration.exceptions.ConfigException;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* Interfaces with the toml config file.
|
||||
*/
|
||||
public class TomlConfiguration implements AutoCloseable {
|
||||
|
||||
private static FileConfig config;
|
||||
private static Logger LOGGER;
|
||||
|
||||
/**
|
||||
* Loads the config file.
|
||||
*
|
||||
* @param APPLICATION_BASE The directory in which the config folder and file are to be created
|
||||
* @param currentSoftwareVersion The current version number of the software using this util
|
||||
*/
|
||||
TomlConfiguration(String APPLICATION_BASE, String currentSoftwareVersion) {
|
||||
LOGGER = LoggerFactory.getLogger(TomlConfiguration.class);
|
||||
Path configBase = Paths.get(APPLICATION_BASE, "config");
|
||||
if (!configBase.toFile().exists()) //noinspection ResultOfMethodCallIgnored
|
||||
configBase.toFile().mkdirs();
|
||||
config = FileConfig.builder(Paths.get(String.valueOf(configBase), "config.toml")).defaultResource("/empty.toml").autosave().autoreload().build();
|
||||
config.load();
|
||||
checkVersion(currentSoftwareVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves and closes the config file.
|
||||
*/
|
||||
public void close() {
|
||||
config.save();
|
||||
config.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the config version is the same as software version. Sends a warning to stderr if version was updated.
|
||||
* @param currentVersion The current software version.
|
||||
*/
|
||||
public void checkVersion(String currentVersion) {
|
||||
String configVersion = getString("file.version");
|
||||
if (currentVersion == null) {
|
||||
LOGGER.error("Error: Failed to retrieve current version. Assuming 0.0.1");
|
||||
currentVersion = "0.0.1";
|
||||
}
|
||||
if (configVersion == null) {
|
||||
throw new ConfigException("Invalid config. \"file.version\" is not set.");
|
||||
} else if (configVersion.equals("0.0.0")) {
|
||||
LOGGER.info("New installation detected. Creating config file.");
|
||||
setString("file.version", currentVersion);
|
||||
} else if (currentVersion.compareTo(configVersion) < 0) {
|
||||
LOGGER.warn("Software updated. Please check the wiki if and how to migrate.");
|
||||
setString("file.version", currentVersion);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the config contains a value at some path.
|
||||
* @param key the path to check, each part separated by a dot. Example "a. b. c"
|
||||
* @return <code>true</code> if the path is associated with a value, <code>false</code> if it's not.
|
||||
*/
|
||||
public boolean contains(String key) {
|
||||
return config.contains(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the config.
|
||||
* @param key the value's path, each part separated by a dot. Example "a. b. c"
|
||||
* @return the value at the given path, or <code>null</code> if there is no such value.
|
||||
*/
|
||||
public String getString(String key) {
|
||||
return config.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value to the config.
|
||||
* @param key the value's path, each part separated by a dot. Example "a. b. c"
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void setString(String key, String value) {
|
||||
config.set(key, value);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package space.b00tload.utils.configuration.exceptions;
|
||||
|
||||
/**
|
||||
* A wrapped {@code java.lang.RuntimeException} used for any generic errors while processing the config
|
||||
*
|
||||
* @author Alix von Schirp
|
||||
* @since 1.0.0
|
||||
* @version 1.0.0
|
||||
*/
|
||||
public class ConfigException extends RuntimeException {}
|
||||
@@ -0,0 +1,88 @@
|
||||
package space.b00tload.utils.configuration.exceptions;
|
||||
|
||||
|
||||
import space.b00tload.utils.configuration.ConfigValues;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The <code>ConfigIncompleteException</code> is thrown when the config has missing values.
|
||||
*
|
||||
* @author Alix von Schirp
|
||||
* @version 1.0.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class ConfigIncompleteException extends RuntimeException {
|
||||
|
||||
private final List<ConfigValues> missingValues;
|
||||
|
||||
/**
|
||||
* Constructs a new runtime exception with the specified detail message and missing values. The cause is not initialized, and may subsequently be initialized by a call to initCause.
|
||||
*
|
||||
* @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()} method.
|
||||
* @param missingValues a <code>java.util.List</code> of type <code>space.b00tload.utils.configuration.ConfigValues</code> containing all missing values
|
||||
*/
|
||||
public ConfigIncompleteException(String message, List<ConfigValues> missingValues) {
|
||||
super(message + System.lineSeparator() + "\t-\t" + missingValues.stream().map(Object::toString).collect(Collectors.joining(";" + System.lineSeparator() + "\t-\t")));
|
||||
this.missingValues = missingValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new runtime exception with the specified detail message, missing values and cause.
|
||||
* Note that the detail message associated with <code>cause</code> is not automatically incorporated in this runtime exception's detail message.
|
||||
*
|
||||
* @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()} method.
|
||||
* @param cause the cause (which is saved for later retrieval by the {@link #getMessage()} method).
|
||||
* (A <code>null</code> value is permitted, and indicates that the cause is nonexistent or unknown.)
|
||||
* @param missingValues a <code>java.util.List</code> of type <code>space.b00tload.utils.configuration.ConfigValues</code> containing all missing values
|
||||
*/
|
||||
public ConfigIncompleteException(String message, Throwable cause, List<ConfigValues> missingValues) {
|
||||
super(message + System.lineSeparator() + "\t-\t" + missingValues.stream().map(Object::toString).collect(Collectors.joining(";" + System.lineSeparator() + "\t-\t")), cause);
|
||||
this.missingValues = missingValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new runtime exception with the specified cause, the missing values and a detail message of {@code (cause==null ? null : cause.toString())}
|
||||
* (which typically contains the class and detail message of {@code cause}).
|
||||
* This constructor is useful for runtime exceptions that are little more than wrappers for other throwables.
|
||||
*
|
||||
* @param cause the cause (which is saved for later retrieval by the
|
||||
* {@link #getCause()} method). (A {@code null} value is
|
||||
* permitted, and indicates that the cause is nonexistent or
|
||||
* unknown.)
|
||||
* @param missingValues a <code>java.util.List</code> of type <code>space.b00tload.utils.configuration.ConfigValues</code> containing all missing values
|
||||
*/
|
||||
public ConfigIncompleteException(Throwable cause, List<ConfigValues> missingValues) {
|
||||
super(cause);
|
||||
this.missingValues = missingValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new runtime exception with the specified detail
|
||||
* message, missing values, cause, suppression enabled or disabled, and writable
|
||||
* stack trace enabled or disabled.
|
||||
*
|
||||
* @param message the detail message.
|
||||
* @param cause the cause. (A {@code null} value is permitted,
|
||||
* and indicates that the cause is nonexistent or unknown.)
|
||||
* @param enableSuppression whether or not suppression is enabled
|
||||
* or disabled
|
||||
* @param writableStackTrace whether or not the stack trace should
|
||||
* be writable
|
||||
* @param missingValues a <code>java.util.List</code> of type <code>space.b00tload.utils.configuration.ConfigValues</code> containing all missing values
|
||||
*/
|
||||
public ConfigIncompleteException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, List<ConfigValues> missingValues) {
|
||||
super(message + System.lineSeparator() + "\t-\t" + missingValues.stream().map(Object::toString).collect(Collectors.joining(";" + System.lineSeparator() + "\t-\t")), cause, enableSuppression, writableStackTrace);
|
||||
this.missingValues = missingValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all missing values.
|
||||
* @return a {@code java.util.List} with all missing values.
|
||||
*/
|
||||
public List<ConfigValues> getMissingValues() {
|
||||
return missingValues;
|
||||
}
|
||||
|
||||
}
|
||||
2
src/main/resources/empty.toml
Normal file
2
src/main/resources/empty.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[file]
|
||||
version = "0.0.0"
|
||||
Reference in New Issue
Block a user