From 7fb5d5246631f024eeed4c2d3322d6f01473a74c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Wed, 5 Aug 2020 10:46:20 +0200 Subject: [PATCH] adding config loader --- content-config/build.gradle | 3 + content-config/src/main/java/module-info.java | 4 + .../org/xbib/content/config/ConfigLoader.java | 130 ++++++++++++++++++ .../org/xbib/content/config/ConfigLogger.java | 18 +++ .../content/config/ExceptionFormatter.java | 56 ++++++++ .../content/config/SystemConfigLogger.java | 37 +++++ .../xbib/content/json/JsonSettingsLoader.java | 5 +- .../xbib/content/yaml/YamlSettingsLoader.java | 4 +- gradle.properties | 2 +- settings.gradle | 1 + 10 files changed, 252 insertions(+), 8 deletions(-) create mode 100644 content-config/build.gradle create mode 100644 content-config/src/main/java/module-info.java create mode 100644 content-config/src/main/java/org/xbib/content/config/ConfigLoader.java create mode 100644 content-config/src/main/java/org/xbib/content/config/ConfigLogger.java create mode 100644 content-config/src/main/java/org/xbib/content/config/ExceptionFormatter.java create mode 100644 content-config/src/main/java/org/xbib/content/config/SystemConfigLogger.java diff --git a/content-config/build.gradle b/content-config/build.gradle new file mode 100644 index 0000000..1ce8b51 --- /dev/null +++ b/content-config/build.gradle @@ -0,0 +1,3 @@ +dependencies { + api project(':content-yaml') +} diff --git a/content-config/src/main/java/module-info.java b/content-config/src/main/java/module-info.java new file mode 100644 index 0000000..33caa64 --- /dev/null +++ b/content-config/src/main/java/module-info.java @@ -0,0 +1,4 @@ +module org.xbib.content.config { + exports org.xbib.content.config; + requires transitive org.xbib.content.yaml; +} \ No newline at end of file diff --git a/content-config/src/main/java/org/xbib/content/config/ConfigLoader.java b/content-config/src/main/java/org/xbib/content/config/ConfigLoader.java new file mode 100644 index 0000000..65074fd --- /dev/null +++ b/content-config/src/main/java/org/xbib/content/config/ConfigLoader.java @@ -0,0 +1,130 @@ +package org.xbib.content.config; + +import org.xbib.content.json.JsonSettingsLoader; +import org.xbib.content.settings.Settings; +import org.xbib.content.settings.SettingsLoader; +import org.xbib.content.yaml.YamlSettingsLoader; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +/** + * A configuration file loader for JSON/YAML configuration files. + */ +public class ConfigLoader { + + private static final String JSON = ".json"; + + private static final String YML = ".yml"; + + private static final String YAML = ".yaml"; + + private final ConfigLogger logger; + + public ConfigLoader(ConfigLogger logger) { + this.logger = logger; + } + + public Settings.Builder loadSettings(ClassLoader classLoader, + String applicationName, + String... fileNamesWithoutSuffix) throws IOException { + for (String fileNameWithoutSuffix : fileNamesWithoutSuffix) { + Settings.Builder settings = createSettingsFromFile(createListOfLocations(applicationName, fileNameWithoutSuffix)); + if (settings != null) { + return settings; + } + settings = createClasspathSettings(classLoader, applicationName, fileNameWithoutSuffix); + if (settings != null) { + return settings; + } + settings = createClasspathSettings(ConfigLoader.class.getClassLoader(), applicationName, fileNameWithoutSuffix); + if (settings != null) { + return settings; + } + settings = createClasspathSettings(ClassLoader.getSystemClassLoader(), applicationName, fileNameWithoutSuffix); + if (settings != null) { + return settings; + } + } + throw new IllegalArgumentException("no config found for " + applicationName + " "+ Arrays.asList(fileNamesWithoutSuffix)); + } + + private Settings.Builder createClasspathSettings(ClassLoader classLoader, String applicationName, String fileNameWithoutSuffix) + throws IOException { + for (String suffix : List.of(YML, YAML, JSON)) { + InputStream inputStream = classLoader.getResourceAsStream(applicationName + '-' + fileNameWithoutSuffix + suffix); + if (inputStream != null) { + logger.info("found resource: " + applicationName + '-' + fileNameWithoutSuffix + suffix); + Settings.Builder settings = createSettingsFromStream(inputStream, suffix); + if (settings != null) { + return settings; + } + } + } + return null; + } + + private Settings.Builder createSettingsFromFile(List settingsFileNames) throws IOException { + for (String settingsFileName: settingsFileNames) { + int pos = settingsFileName.lastIndexOf('.'); + String suffix = (pos > 0 ? settingsFileName.substring(pos) : "").toLowerCase(Locale.ROOT); + Path path = Paths.get(settingsFileName); + logger.info("trying " + path.toString()); + if (Files.exists(path)) { + logger.info("found path: " + path); + return createSettingsFromStream(Files.newInputStream(path), suffix); + } + } + return null; + } + + private Settings.Builder createSettingsFromStream(InputStream inputStream, String suffix) throws IOException { + if (inputStream == null) { + logger.error("unable to open input stream"); + return null; + } + SettingsLoader settingsLoader = isYaml(suffix) ? new YamlSettingsLoader() : + isJson(suffix) ? new JsonSettingsLoader() : null; + if (settingsLoader != null) { + Settings.Builder settings; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + String content = reader.lines().collect(Collectors.joining("\n")); + settings = Settings.settingsBuilder().put(settingsLoader.load(content)); + } + return settings; + } else { + logger.error("suffix is invalid: " + suffix); + } + return null; + } + + private static List createListOfLocations(String applicationName, String fileNameWithoutSuffix) { + return List.of( + applicationName + '-' + fileNameWithoutSuffix + YML, + applicationName + '-' + fileNameWithoutSuffix + YAML, + applicationName + '-' + fileNameWithoutSuffix + JSON, + System.getProperty("user.home") + "/." + applicationName + '/' + fileNameWithoutSuffix + YML, + System.getProperty("user.home") + "/." + applicationName + '/' + fileNameWithoutSuffix + YAML, + System.getProperty("user.home") + "/." + applicationName + '/' + fileNameWithoutSuffix + JSON, + "/etc/" + applicationName + '/' + fileNameWithoutSuffix + YML, + "/etc/" + applicationName + '/' + fileNameWithoutSuffix + YAML, + "/etc/" + applicationName + '/' + fileNameWithoutSuffix + JSON); + } + + private static boolean isYaml(String suffix) { + return YAML.equals(suffix) || YML.equals(suffix); + } + + private static boolean isJson(String suffix) { + return JSON.equals(suffix); + } +} diff --git a/content-config/src/main/java/org/xbib/content/config/ConfigLogger.java b/content-config/src/main/java/org/xbib/content/config/ConfigLogger.java new file mode 100644 index 0000000..a8563d1 --- /dev/null +++ b/content-config/src/main/java/org/xbib/content/config/ConfigLogger.java @@ -0,0 +1,18 @@ +package org.xbib.content.config; + +import java.io.OutputStream; + +public interface ConfigLogger { + + void setLevel(String level); + + void info(String string); + + void error(String message); + + void error(String message, Throwable throwable); + + OutputStream getStderrOutputStream(); + + OutputStream getStdoutOutputStream(); +} diff --git a/content-config/src/main/java/org/xbib/content/config/ExceptionFormatter.java b/content-config/src/main/java/org/xbib/content/config/ExceptionFormatter.java new file mode 100644 index 0000000..cfa64b1 --- /dev/null +++ b/content-config/src/main/java/org/xbib/content/config/ExceptionFormatter.java @@ -0,0 +1,56 @@ +package org.xbib.content.config; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Format java exception messages and stack traces. + */ +public final class ExceptionFormatter { + + private ExceptionFormatter() { + } + + /** + * Format exception with stack trace. + * + * @param t the thrown object + * @return the formatted exception + */ + public static String format(Throwable t) { + StringBuilder sb = new StringBuilder(); + append(sb, t, 0, true); + return sb.toString(); + } + + /** + * Append Exception to string builder. + */ + private static void append(StringBuilder sb, Throwable t, int level, boolean details) { + if (((t != null) && (t.getMessage() != null)) && (!t.getMessage().isEmpty())) { + if (details && (level > 0)) { + sb.append("\n\nCaused by\n"); + } + sb.append(t.getMessage()); + } + if (details) { + if (t != null) { + if ((t.getMessage() != null) && (t.getMessage().isEmpty())) { + sb.append("\n\nCaused by "); + } else { + sb.append("\n\n"); + } + } + StringWriter sw = new StringWriter(); + if (t != null) { + t.printStackTrace(new PrintWriter(sw)); + } + sb.append(sw.toString()); + } + if (t != null) { + if (t.getCause() != null) { + append(sb, t.getCause(), level + 1, details); + } + } + } +} diff --git a/content-config/src/main/java/org/xbib/content/config/SystemConfigLogger.java b/content-config/src/main/java/org/xbib/content/config/SystemConfigLogger.java new file mode 100644 index 0000000..257e47a --- /dev/null +++ b/content-config/src/main/java/org/xbib/content/config/SystemConfigLogger.java @@ -0,0 +1,37 @@ +package org.xbib.content.config; + +import java.io.OutputStream; + +public class SystemConfigLogger implements ConfigLogger { + + @Override + public void setLevel(String level) { + // d nothing + } + + @Override + public void info(String string) { + System.out.println(string); + } + + @Override + public void error(String message) { + System.err.println(message); + } + + @Override + public void error(String message, Throwable throwable) { + System.err.println(message); + System.err.println(ExceptionFormatter.format(throwable)); + } + + @Override + public OutputStream getStderrOutputStream() { + return System.err; + } + + @Override + public OutputStream getStdoutOutputStream() { + return System.out; + } +} diff --git a/content-core/src/main/java/org/xbib/content/json/JsonSettingsLoader.java b/content-core/src/main/java/org/xbib/content/json/JsonSettingsLoader.java index 7cf35ff..c9e3155 100644 --- a/content-core/src/main/java/org/xbib/content/json/JsonSettingsLoader.java +++ b/content-core/src/main/java/org/xbib/content/json/JsonSettingsLoader.java @@ -2,9 +2,6 @@ package org.xbib.content.json; import org.xbib.content.XContent; import org.xbib.content.settings.AbstractSettingsLoader; - -import java.util.Collections; -import java.util.HashSet; import java.util.Set; /** @@ -13,7 +10,7 @@ import java.util.Set; */ public class JsonSettingsLoader extends AbstractSettingsLoader { - private static final Set JSON_SUFFIXES = new HashSet<>(Collections.singletonList("json")); + private static final Set JSON_SUFFIXES = Set.of("json"); @Override public XContent content() { diff --git a/content-yaml/src/main/java/org/xbib/content/yaml/YamlSettingsLoader.java b/content-yaml/src/main/java/org/xbib/content/yaml/YamlSettingsLoader.java index afb3d77..78d4f64 100644 --- a/content-yaml/src/main/java/org/xbib/content/yaml/YamlSettingsLoader.java +++ b/content-yaml/src/main/java/org/xbib/content/yaml/YamlSettingsLoader.java @@ -3,8 +3,6 @@ package org.xbib.content.yaml; import org.xbib.content.XContent; import org.xbib.content.settings.AbstractSettingsLoader; import java.io.IOException; -import java.util.Arrays; -import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -14,7 +12,7 @@ import java.util.Set; */ public class YamlSettingsLoader extends AbstractSettingsLoader { - private static final Set YAML_SUFFIXES = new HashSet<>(Arrays.asList("yml", "yaml")); + private static final Set YAML_SUFFIXES = Set.of("yml", "yaml"); @Override public XContent content() { diff --git a/gradle.properties b/gradle.properties index 1aa110a..a3197f3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ group = org.xbib name = content -version = 2.3.2 +version = 2.4.0 gradle.wrapper.version = 6.4.1 xbib.net.version = 2.1.0 diff --git a/settings.gradle b/settings.gradle index ab2b98b..33aeeb8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,4 @@ +include 'content-config' include 'content-core' include 'content-resource' include 'content-language'