Skip to content

Commit 59824d1

Browse files
committed
wip
1 parent b99a1d6 commit 59824d1

File tree

2 files changed

+187
-163
lines changed

2 files changed

+187
-163
lines changed

api/src/main/java/io/kafbat/ui/service/app/ConfigReloadService.java

Lines changed: 39 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,24 @@
11
package io.kafbat.ui.service.app;
22

3-
import io.kafbat.ui.util.ApplicationRestarter;
4-
import io.kafbat.ui.util.DynamicConfigOperations;
3+
import io.kafbat.ui.util.MultiFileWatcher;
54
import jakarta.annotation.PostConstruct;
65
import jakarta.annotation.PreDestroy;
76
import java.io.IOException;
8-
import java.nio.file.ClosedWatchServiceException;
9-
import java.nio.file.FileSystems;
10-
import java.nio.file.Files;
11-
import java.nio.file.Path;
12-
import java.nio.file.StandardWatchEventKinds;
13-
import java.nio.file.WatchEvent;
14-
import java.nio.file.WatchKey;
15-
import java.nio.file.WatchService;
16-
import java.util.Arrays;
17-
import java.util.HashMap;
18-
import java.util.Iterator;
19-
import java.util.Map;
7+
import java.nio.file.Paths;
8+
import java.util.LinkedHashSet;
209
import java.util.Objects;
2110
import java.util.stream.Collectors;
11+
import java.util.stream.Stream;
12+
import java.util.stream.StreamSupport;
2213
import lombok.RequiredArgsConstructor;
2314
import lombok.extern.slf4j.Slf4j;
24-
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2515
import org.springframework.boot.env.OriginTrackedMapPropertySource;
2616
import org.springframework.boot.origin.Origin;
27-
import org.springframework.boot.origin.OriginLookup;
2817
import org.springframework.boot.origin.OriginTrackedValue;
2918
import org.springframework.boot.origin.TextResourceOrigin;
30-
import org.springframework.context.ApplicationContext;
31-
import org.springframework.core.env.AbstractEnvironment;
3219
import org.springframework.core.env.ConfigurableEnvironment;
33-
import org.springframework.core.env.EnumerablePropertySource;
34-
import org.springframework.core.env.MapPropertySource;
35-
import org.springframework.core.env.PropertySource;
20+
import org.springframework.core.io.Resource;
3621
import org.springframework.stereotype.Service;
37-
import org.stringtemplate.v4.ST;
3822

3923
@Service
4024
//@ConditionalOnProperty(value = "dynamic.config.autoreload", havingValue = "true")
@@ -43,111 +27,51 @@
4327
public class ConfigReloadService {
4428

4529
private static final String THREAD_NAME = "config-watcher-thread";
46-
private static final long STARTUP_SUPPRESSION_MS = 1000;
47-
private final long appStartedAt = System.currentTimeMillis();
4830

49-
private final DynamicConfigOperations dynamicConfigOperations;
50-
private final ApplicationRestarter restarter;
31+
private final ConfigurableEnvironment environment;
5132

52-
private WatchService watchService;
5333
private Thread watcherThread;
54-
55-
private final ApplicationContext context;
56-
private final ConfigurableEnvironment environment;
34+
private MultiFileWatcher multiFileWatcher;
5735

5836
@PostConstruct
5937
public void init() {
60-
61-
var o = environment.getPropertySources()
62-
.stream()
63-
.filter(ps -> ps instanceof OriginTrackedMapPropertySource)
64-
.map(ps -> (OriginTrackedMapPropertySource) ps)
65-
.collect(Collectors.toUnmodifiableList())
66-
.stream()
67-
.findFirst()
68-
.get()
69-
.getSource()
70-
.values()
71-
.stream()
72-
.findFirst()
73-
.map(a -> (OriginTrackedValue) a)
74-
.get()
75-
.getOrigin();
76-
77-
var origin = (TextResourceOrigin) o;
78-
79-
origin.getResource();
80-
81-
/*
82-
environment.getPropertySources()
83-
.stream()
84-
.filter(ps -> ps instanceof OriginTrackedMapPropertySource)
85-
.map(ps -> (OriginTrackedMapPropertySource)ps)
86-
.map(ps -> ps.getSource())
87-
.map(source -> source.values())
88-
.map(values -> {
89-
return (HashMap<String, String>) values;
90-
})
91-
// .map(sourceValues -> sourceValues.)
92-
.collect(Collectors.toUnmodifiableList());
93-
*/
94-
95-
96-
// =============
97-
98-
/* environment.getPropertySources().stream()
99-
.filter(ps -> ps instanceof EnumerablePropertySource)
100-
.filter(ps -> ps instanceof OriginLookup)
101-
.flatMap(ps -> {
102-
EnumerablePropertySource<?> eps = (EnumerablePropertySource<?>) ps;
103-
OriginLookup<String> lookup = (OriginLookup<String>) ps;
104-
return Arrays.stream(eps.getPropertyNames())
105-
.map(name -> {
106-
Origin origin = lookup.getOrigin(name);
107-
return origin != null ? origin.toString() : null;
108-
});
38+
var propertySourcePaths = StreamSupport.stream(environment.getPropertySources().spliterator(), false)
39+
.filter(OriginTrackedMapPropertySource.class::isInstance)
40+
.map(OriginTrackedMapPropertySource.class::cast)
41+
.flatMap(ps -> ps.getSource().values().stream())
42+
.map(v -> (v instanceof OriginTrackedValue otv) ? otv.getOrigin() : null)
43+
.filter(Objects::nonNull)
44+
.flatMap(o -> Stream.iterate(o, Objects::nonNull, Origin::getParent))
45+
.filter(TextResourceOrigin.class::isInstance)
46+
.map(TextResourceOrigin.class::cast)
47+
.map(TextResourceOrigin::getResource)
48+
.filter(Objects::nonNull)
49+
.filter(Resource::exists)
50+
.filter(Resource::isReadable)
51+
.filter(Resource::isFile)
52+
.map(r -> {
53+
try {
54+
return r.getURI();
55+
} catch (IOException e) {
56+
log.error("can't retrieve resource URL", e);
57+
return null;
58+
}
10959
})
11060
.filter(Objects::nonNull)
111-
.distinct()
112-
.collect(Collectors.toUnmodifiableList());*/
113-
114-
// ===============
115-
116-
/* Map<String, Object> map = new HashMap();
117-
for(Iterator it = ((AbstractEnvironment) environment).getPropertySources().iterator(); it.hasNext(); ) {
118-
PropertySource propertySource = (PropertySource) it.next();
119-
if (propertySource instanceof MapPropertySource) {
120-
map.putAll(((MapPropertySource) propertySource).getSource());
121-
}
122-
}*/
123-
124-
// ====
125-
126-
/* SpringConfigurableEnvironment properties = new SpringConfigurableEnvironment(springEnv);
127-
SpringConfigurableEnvironment.PropertyInfo info = properties.get("profile.env");
128-
assertEquals("default", properties.get(info.getValue());
129-
assertEquals(
130-
"Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'",
131-
info.getSourceList.get(0));*/
61+
.map(Paths::get)
62+
.collect(Collectors.toCollection(LinkedHashSet::new));
13263

133-
134-
135-
136-
System.out.println();
137-
// environment.getPropertySources()
138-
// .stream()
139-
140-
var configPath = dynamicConfigOperations.dynamicConfigFilePath();
141-
if (!Files.exists(configPath) || !Files.isReadable(configPath)) {
142-
log.warn("Dynamic config file {} doesnt exist or is not readable. Auto reload is disabled", configPath);
64+
if (propertySourcePaths.isEmpty()) {
65+
log.debug("No config files found, auto reload is disabled");
14366
return;
14467
}
14568

14669
log.debug("Auto reload is enabled, will watch for config changes");
14770

14871
try {
149-
registerWatchService();
150-
startWatching();
72+
this.multiFileWatcher = new MultiFileWatcher(propertySourcePaths, this::reload);
73+
this.watcherThread = new Thread(multiFileWatcher::watchLoop, THREAD_NAME);
74+
this.watcherThread.start();
15175
} catch (IOException e) {
15276
log.error("Error while registering watch service", e);
15377
}
@@ -156,8 +80,8 @@ public void init() {
15680
@PreDestroy
15781
public void shutdown() {
15882
try {
159-
if (watchService != null) {
160-
watchService.close();
83+
if (multiFileWatcher != null) {
84+
multiFileWatcher.close();
16185
}
16286
} catch (IOException ignored) {
16387
}
@@ -166,56 +90,8 @@ public void shutdown() {
16690
}
16791
}
16892

169-
private void registerWatchService() throws IOException {
170-
this.watchService = FileSystems.getDefault().newWatchService();
171-
dynamicConfigOperations.dynamicConfigFilePath()
172-
.getParent()
173-
.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
174-
}
175-
176-
private void startWatching() {
177-
watcherThread = new Thread(this::watchLoop, THREAD_NAME);
178-
watcherThread.start();
179-
}
180-
181-
private void watchLoop() {
182-
final var watchedDir = dynamicConfigOperations.dynamicConfigFilePath().getParent();
183-
184-
while (true) {
185-
try {
186-
WatchKey key = watchService.take();
187-
for (WatchEvent<?> event : key.pollEvents()) {
188-
WatchEvent.Kind<?> kind = event.kind();
189-
Path changed = watchedDir.resolve((Path) event.context());
190-
191-
if (kind != StandardWatchEventKinds.ENTRY_MODIFY) {
192-
continue;
193-
}
194-
if (!changed.equals(dynamicConfigOperations.dynamicConfigFilePath())) {
195-
continue;
196-
}
197-
198-
var now = System.currentTimeMillis();
199-
if (now - appStartedAt < STARTUP_SUPPRESSION_MS) {
200-
continue;
201-
}
202-
203-
restart();
204-
}
205-
key.reset();
206-
} catch (ClosedWatchServiceException e) {
207-
log.trace("Watch service closed, exiting watcher thread");
208-
break;
209-
} catch (InterruptedException e) {
210-
Thread.currentThread().interrupt();
211-
break;
212-
}
213-
}
214-
}
93+
private void reload() {
21594

216-
private void restart() {
217-
log.info("Application config change detected, restarting");
218-
restarter.requestRestart();
21995
}
22096

22197

0 commit comments

Comments
 (0)