40public final class ReloadableConfigSource
implements ConfigSource, AutoCloseable {
46 private final String name;
47 private final Path path;
48 private final Format format;
49 private final AtomicReference<Map<String, String>> values;
50 private final AtomicBoolean running;
51 private final WatchService watchService;
52 private final Thread watcherThread;
54 private ReloadableConfigSource(
final Path path,
final Format format)
throws IOException {
55 this.path = Objects.requireNonNull(path,
"path");
56 this.format = Objects.requireNonNull(format,
"format");
57 name =
"reloadable:" + format.name().toLowerCase() +
":" + path.toAbsolutePath();
58 values =
new AtomicReference<>(load(path, format));
59 running =
new AtomicBoolean(
true);
60 watchService = path.getParent().getFileSystem().newWatchService();
61 path.getParent().register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
62 StandardWatchEventKinds.ENTRY_MODIFY);
63 watcherThread = Thread.ofVirtual().name(
"ether-config-reload-" + path.getFileName()).start(this::watchLoop);
66 public static ReloadableConfigSource
of(
final Path path,
final Format format)
throws IOException {
67 return new ReloadableConfigSource(path, format);
76 public Optional<String>
get(
final String key) {
77 return Optional.ofNullable(values.get().get(key));
86 values.set(load(path, format));
90 public void close() throws IOException {
92 watcherThread.interrupt();
96 private void watchLoop() {
97 while (running.get()) {
99 final var key = watchService.take();
100 for (
final WatchEvent<?> event : key.pollEvents()) {
101 final var changed = (Path) event.context();
102 if (path.getFileName().equals(changed)) {
105 }
catch (IOException _) {
111 }
catch (InterruptedException _) {
112 Thread.currentThread().interrupt();
118 private static Map<String, String> load(
final Path path,
final Format format)
throws IOException {
119 return switch (format) {
120 case PROPERTIES ->
new PropertiesFileConfigSource(path).entries();
121 case JSON -> StructuredConfigSource.loadJson(path);
122 case YAML -> StructuredConfigSource.loadYaml(path);
123 case TOML -> StructuredConfigSource.loadToml(path);