WatcherThread Class — spring-boot Architecture
Architecture documentation for the WatcherThread class in FileWatcher.java from the spring-boot codebase.
Entity Profile
Source Code
core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/FileWatcher.java lines 164–260
private class WatcherThread extends Thread implements Closeable {
private final WatchService watchService = FileSystems.getDefault().newWatchService();
private final Map<WatchKey, List<Registration>> registrations = new ConcurrentHashMap<>();
private volatile boolean running = true;
WatcherThread() throws IOException {
setName("ssl-bundle-watcher");
setDaemon(true);
setUncaughtExceptionHandler(this::onThreadException);
}
private void onThreadException(Thread thread, Throwable throwable) {
logger.error("Uncaught exception in file watcher thread", throwable);
}
void register(Registration registration) throws IOException {
Set<Path> directories = new HashSet<>();
for (Path path : registration.paths()) {
if (!Files.isRegularFile(path) && !Files.isDirectory(path)) {
throw new IOException("'%s' is neither a file nor a directory".formatted(path));
}
Path directory = Files.isDirectory(path) ? path : path.getParent();
directories.add(directory);
}
for (Path directory : directories) {
WatchKey watchKey = register(directory);
this.registrations.computeIfAbsent(watchKey, (key) -> new CopyOnWriteArrayList<>()).add(registration);
}
}
private WatchKey register(Path directory) throws IOException {
logger.debug(LogMessage.format("Registering '%s'", directory));
return directory.register(this.watchService, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
}
@Override
public void run() {
logger.debug("Watch thread started");
Set<Runnable> actions = new HashSet<>();
while (this.running) {
try {
long timeout = FileWatcher.this.quietPeriod.toMillis();
WatchKey key = this.watchService.poll(timeout, TimeUnit.MILLISECONDS);
if (key == null) {
actions.forEach(this::runSafely);
actions.clear();
}
else {
accumulate(key, actions);
key.reset();
}
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
catch (ClosedWatchServiceException ex) {
logger.debug("File watcher has been closed");
this.running = false;
}
}
logger.debug("Watch thread stopped");
}
private void runSafely(Runnable action) {
try {
action.run();
}
catch (Throwable ex) {
logger.error("Unexpected SSL reload error", ex);
}
}
private void accumulate(WatchKey key, Set<Runnable> actions) {
List<Registration> registrations = this.registrations.get(key);
Path directory = (Path) key.watchable();
for (WatchEvent<?> event : key.pollEvents()) {
Path file = directory.resolve((Path) event.context());
Assert.state(registrations != null, "'registrations' must not be null");
for (Registration registration : registrations) {
if (registration.manages(file)) {
actions.add(registration.action());
}
}
}
}
@Override
public void close() throws IOException {
this.running = false;
this.watchService.close();
}
}
Source
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free