This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-reload-hidden in repository https://gitbox.apache.org/repos/asf/camel.git
commit 7c196054db906402941d6022adba78bfe5711f3e Author: Claus Ibsen <[email protected]> AuthorDate: Sun Sep 27 11:40:44 2026 +0200 CAMEL-25042: camel-core - the reload scan skips dot directories, and scans every 2s again camel run keeps the properties of the run it is doing in .camel-jbang/camel-jbang-run.properties and writes it more than once while starting. The scan reported each write, the file ends in .properties, so the strategy treated it as a properties change and reloaded the routes for it. The 10 s poll of the watch service coalesced both writes into one batch, so it happened once and was merely pointless; at 1 s they are two changes, and the application reloaded twice a second apart. That is not harmless: an example whose file consumer calls a rest route in the same application had the call in flight answered 404 by a rest route that the second reload was rebuilding. A dot directory holds state, not sources, so the scan skips it, as it already skips the compile work directory (CAMEL-24862). That also stops it walking .git and .idea on every scan, which registering directories once never did. A route file inside a dot directory is no longer watched, which is intended. The scan interval goes back to the previous default of 2000 ms, so setPollTimeout's default is unchanged after all. A longer interval also groups more of one save together: two files written a second apart are one change at 2000 and two at 1000, and one change is what a reload wants. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01Bp3538HRBPMQkb5ta9xRaj --- .../java/org/apache/camel/support/FileScanReloadTest.java | 15 +++++++++++++++ .../camel/support/FileWatcherResourceReloadStrategy.java | 12 ++++++++++-- .../modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc | 13 ++++++++----- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/core/camel-core/src/test/java/org/apache/camel/support/FileScanReloadTest.java b/core/camel-core/src/test/java/org/apache/camel/support/FileScanReloadTest.java index 98f2f75d3b22..ee3c9e8505ce 100644 --- a/core/camel-core/src/test/java/org/apache/camel/support/FileScanReloadTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/support/FileScanReloadTest.java @@ -133,6 +133,21 @@ public class FileScanReloadTest extends ContextTestSupport { assertThat(scanNames()).containsExactly("shop.yaml"); } + @Test + public void testADotDirectoryIsNotScanned() throws Exception { + // .camel-jbang holds the properties of the run camel-jbang is doing and it rewrites them, which was reloading + // the routes; .git and .idea are not ours either (CAMEL-25042) + Path state = dir.resolve(".camel-jbang"); + Files.createDirectories(state); + Files.writeString(dir.resolve("shop.yaml"), "one"); + assertThat(scanNames()).containsExactly("shop.yaml"); + + Files.writeString(state.resolve("camel-jbang-run.properties"), "camel.jbang.dev=true"); + assertThat(scanNames()).isEmpty(); + Files.writeString(state.resolve("camel-jbang-run.properties"), "camel.jbang.dev=true\ncamel.jbang.x=1"); + assertThat(scanNames()).isEmpty(); + } + @Test public void testASaveStillBeingWrittenIsLeftForTheNextScan() throws Exception { strategy.setStableTimeout(60_000); diff --git a/core/camel-support/src/main/java/org/apache/camel/support/FileWatcherResourceReloadStrategy.java b/core/camel-support/src/main/java/org/apache/camel/support/FileWatcherResourceReloadStrategy.java index 59945b15365c..50f23685a771 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/FileWatcherResourceReloadStrategy.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/FileWatcherResourceReloadStrategy.java @@ -69,7 +69,7 @@ public class FileWatcherResourceReloadStrategy extends ResourceReloadStrategySup String folder; boolean isRecursive; boolean scheduler = true; - long pollTimeout = 1000; + long pollTimeout = 2000; /** * A file modified less than this ago is left for the next scan: a save still being written would otherwise be * reloaded half-finished. The file component leaves a file alone the same way. @@ -107,7 +107,8 @@ public class FileWatcherResourceReloadStrategy extends ResourceReloadStrategySup } /** - * Sets how often the folder is scanned for changed files, in millis. The default value is 1000. + * Sets how often the folder is scanned for changed files, in millis. The default value is 2000. A longer interval + * also groups more of one save together: two files written a second apart are one change at 2000 and two at 1000. */ public void setPollTimeout(long pollTimeout) { this.pollTimeout = pollTimeout; @@ -219,6 +220,13 @@ public class FileWatcherResourceReloadStrategy extends ResourceReloadStrategySup // and would trigger a reload, which compiles again, which writes again (CAMEL-24862) return FileVisitResult.SKIP_SUBTREE; } + if (!dir.equals(root) && dir.getFileName() != null + && dir.getFileName().toString().startsWith(".")) { + // a dot directory holds state, not sources: .camel-jbang is where camel-jbang keeps the + // properties of the run it is doing, and rewriting those was reloading the routes; .git and + // .idea are not ours either, and walking them every scan costs for nothing (CAMEL-25042) + return FileVisitResult.SKIP_SUBTREE; + } if (!isRecursive && !dir.equals(root)) { return FileVisitResult.SKIP_SUBTREE; } diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc index 5f750d4fb38e..a5f89a84886d 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc @@ -2963,8 +2963,11 @@ all the files of one save together, which is what a reload needs: a route is bui so a route and a property saved together must be reloaded together. It also removes the macOS penalty, where Java has no native file notification and the watch service fell back to a poll of about ten seconds. -`FileWatcherResourceReloadStrategy.setPollTimeout` is now how often the folder is scanned, and its default changed -from 2000 to 1000 milliseconds. On Linux and Windows a change is therefore noticed within a second rather than -immediately; on macOS within a second rather than up to ten. A new `setStableTimeout` (default 200 milliseconds) -leaves a file that was only just modified for the next scan, so a save still being written is not reloaded -half-finished. +A dot directory under the watched folder is no longer scanned. `.camel-jbang`, where `camel run` keeps the properties +of the run it is doing, was reloading the routes whenever it was written, and `.git` or `.idea` were walked on every +scan for nothing. A route file inside a dot directory is therefore no longer watched. + +`FileWatcherResourceReloadStrategy.setPollTimeout` is now how often the folder is scanned. Its default is unchanged at +2000 milliseconds, so on Linux and Windows a change is noticed within two seconds rather than immediately, and on macOS +within two seconds rather than up to ten. A new `setStableTimeout` (default 200 milliseconds) leaves a file that was +only just modified for the next scan, so a save still being written is not reloaded half-finished.
