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