-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathConfigLoader.java
More file actions
108 lines (91 loc) · 4.17 KB
/
Copy pathConfigLoader.java
File metadata and controls
108 lines (91 loc) · 4.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package com.demcha.compose;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
public final class ConfigLoader {
private ConfigLoader() {
}
/**
* Load YAML or JSON from a classpath resource into the given class.
* Supports running from IDE and from a fat JAR.
* Optionally resolves {@code ${ENV}} or {@code ${ENV:default}} placeholders
* from environment variables.
*
* <p><strong>Optional dependency:</strong> the YAML path requires
* {@code com.fasterxml.jackson.dataformat:jackson-dataformat-yaml} on
* the classpath. GraphCompose declares it as an optional Maven
* dependency since {@code v1.6.7}; applications that load YAML
* configs through this helper must add the artifact to their own
* build. JSON configs work without it.
*
* @throws java.lang.NoClassDefFoundError if {@code fileName} ends with
* {@code .yaml} / {@code .yml} and
* {@code jackson-dataformat-yaml} is not on the classpath.
*/
public static <T> T loadConfigWithEnv(String fileName, Class<T> clazz, boolean resolveEnv) {
log.info("Initializing variables from '{}'", fileName);
ObjectMapper mapper;
if (fileName == null || fileName.isBlank()) throw new RuntimeException("fileName can`t be null or blank");
if (fileName.endsWith("yaml") || fileName.endsWith("yml")) {
mapper = new ObjectMapper(new YAMLFactory());
} else {
mapper = new ObjectMapper();
}
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) cl = ConfigLoader.class.getClassLoader();
URL resource = cl.getResource(fileName);
if (resource == null) {
throw new IllegalArgumentException("Config resource not found on classpath: " + fileName + "Url: " + resource);
}
log.info("Reading config from '{}'", resource);
String raw;
try (InputStream is = resource.openStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) sb.append(line).append('\n');
raw = sb.toString();
} catch (Exception e) {
log.error("Failed to read config '{}': {}", fileName, e.toString(), e);
throw new RuntimeException("Failed to read config: " + fileName, e);
}
String resolved = resolveEnv ? replaceEnvVariables(raw) : raw;
log.info("Creating a config YAML object '{}'", clazz.getSimpleName());
try {
log.info("Parsing config '{}'", fileName);
return mapper.readValue(resolved, clazz);
} catch (JsonProcessingException e) {
log.error("Failed to parse config '{}': {}", fileName, e.toString(), e);
throw new RuntimeException( "Failed to parse config: " + fileName, e);
}
}
/**
* Replace ${ENV} or ${ENV:default} with values from SystemECS.getenv().
* Unknown vars without default → empty string.
*/
private static String replaceEnvVariables(String text) {
// ${VAR} or ${VAR:default value}
Pattern p = Pattern.compile("\\$\\{([A-Za-z_][A-Za-z0-9_]*) (?:: ([^}]*))?\\}", Pattern.COMMENTS);
Matcher m = p.matcher(text);
StringBuffer out = new StringBuffer();
while (m.find()) {
String var = m.group(1);
String def = m.group(2); // may be null
String val = System.getenv(var);
if (val == null) val = (def != null ? def : "");
// Escape backslashes and dollars for appendReplacement
m.appendReplacement(out, Matcher.quoteReplacement(val));
}
m.appendTail(out);
return out.toString();
}
}