diff --git a/today-framework/src/main/java/infra/annotation/config/ssl/FileWatcher.java b/today-framework/src/main/java/infra/annotation/config/ssl/FileWatcher.java index e6aa554fb..d9c32c7e9 100755 --- a/today-framework/src/main/java/infra/annotation/config/ssl/FileWatcher.java +++ b/today-framework/src/main/java/infra/annotation/config/ssl/FileWatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2024 the original author or authors. + * Copyright 2017 - 2025 the original author or authors. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -91,7 +91,11 @@ void watch(Set paths, Runnable action) { this.thread = new WatcherThread(); this.thread.start(); } - this.thread.register(new Registration(paths, action)); + Set actualPaths = new HashSet<>(); + for (Path path : paths) { + actualPaths.add(resolveSymlinkIfNecessary(path)); + } + this.thread.register(new Registration(actualPaths, action)); } catch (IOException ex) { throw new UncheckedIOException("Failed to register paths for watching: " + paths, ex); @@ -99,6 +103,14 @@ void watch(Set paths, Runnable action) { } } + private static Path resolveSymlinkIfNecessary(Path path) throws IOException { + if (Files.isSymbolicLink(path)) { + Path target = Files.readSymbolicLink(path); + return resolveSymlinkIfNecessary(target); + } + return path; + } + public void destroy() throws IOException { synchronized(this.lock) { if (this.thread != null) { diff --git a/today-framework/src/test/java/infra/annotation/config/ssl/FileWatcherTests.java b/today-framework/src/test/java/infra/annotation/config/ssl/FileWatcherTests.java index b7dd99e6b..f88ac40cb 100755 --- a/today-framework/src/test/java/infra/annotation/config/ssl/FileWatcherTests.java +++ b/today-framework/src/test/java/infra/annotation/config/ssl/FileWatcherTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2024 the original author or authors. + * Copyright 2017 - 2025 the original author or authors. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -94,6 +94,32 @@ void shouldWatchFile(@TempDir Path tempDir) throws Exception { callback.expectChanges(); } + @Test + void shouldFollowSymlink(@TempDir Path tempDir) throws Exception { + Path realFile = tempDir.resolve("realFile.txt"); + Path symLink = tempDir.resolve("symlink.txt"); + Files.createFile(realFile); + Files.createSymbolicLink(symLink, realFile); + WaitingCallback callback = new WaitingCallback(); + this.fileWatcher.watch(Set.of(symLink), callback); + Files.writeString(realFile, "Some content"); + callback.expectChanges(); + } + + @Test + void shouldFollowSymlinkRecursively(@TempDir Path tempDir) throws Exception { + Path realFile = tempDir.resolve("realFile.txt"); + Path symLink = tempDir.resolve("symlink.txt"); + Path symLink2 = tempDir.resolve("symlink2.txt"); + Files.createFile(realFile); + Files.createSymbolicLink(symLink, symLink2); + Files.createSymbolicLink(symLink2, realFile); + WaitingCallback callback = new WaitingCallback(); + this.fileWatcher.watch(Set.of(symLink), callback); + Files.writeString(realFile, "Some content"); + callback.expectChanges(); + } + @Test void shouldIgnoreNotWatchedFiles(@TempDir Path tempDir) throws Exception { Path watchedFile = tempDir.resolve("watched.txt");