This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new d9f888a814e4 CAMEL-25042: camel-core - the reload scan skips dot 
directories, and scans every 2s again (#26925)
d9f888a814e4 is described below

commit d9f888a814e4cf8a018c324466d29f3587bd7cd1
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 28 09:06:41 2026 +0200

    CAMEL-25042: camel-core - the reload scan skips dot directories, and scans 
every 2s again (#26925)
    
    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
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../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.

Reply via email to