davsclaus opened a new pull request, #26915:
URL: https://github.com/apache/camel/pull/26915

   Replaces the JDK `WatchService` in dev-mode route reloading with a scan of 
the watched folder that compares each file's modification time and length with 
the previous scan, the way the file component finds changed files. Supersedes 
#26907, whose batching and retry are carried in here as the first two commits.
   
   **Why a scan.** The reload strategy needs the files of one save *together*: 
a route is built with the properties of the moment, so a route and the property 
it uses must be reloaded together, or the route fails on `Property with key [x] 
not found` and is restored to the version that last ran. The watch service 
reports one file at a time with no batch boundary, so #26907 had to reconstruct 
one — a key's events, plus whatever arrived in the next few polls, bounded by a 
timeout. A scan returns exactly the set that changed since the last scan, and 
the batch comes for free.
   
   **It also removes the macOS penalty.** Java has no native file notification 
on macOS, so the watch service falls back to polling. Camel knew this and 
applied `com.sun.nio.file.SensitivityWatchEventModifier.HIGH` to get the 
interval to 2 s, warning about ten seconds when the class could not be 
resolved. On JDK 25 the class *does* resolve and HIGH *is* applied — and the 
interval is still about ten seconds. Measured from a dev-mode run, the gaps 
between distinct reloads:
   
   ```
   01:13:44.885
   01:13:52.881  (+8.0s)
   01:14:28.891  (+34.0s)   <- no change in between
   01:14:38.880  (+10.0s)
   01:14:48.882  (+10.0s)
   01:14:56.886  (+8.0s)
   ```
   
   So the workaround has no effect on a current JDK, and anyone developing 
Camel routes on a Mac waits up to ten seconds per save. The scan is within its 
interval on every platform, and the dependency on a `com.sun.nio.file` class 
that was never standard API is gone.
   
   **What the watch service needed and a scan does not:** registering every 
directory; registering one created while running — the workaround of 
CAMEL-24862, so `registerNewDirectory` goes; and depending on `ENTRY_DELETE` 
for a file that is gone, which is just an entry the next scan does not find. A 
scan also cannot drop changes to `OVERFLOW`.
   
   **Behaviour changes** (noted in the 4.23 upgrade guide):
   
   - `setPollTimeout` is now how often the folder is scanned, default 1000 
rather than 2000. On Linux and Windows a change is noticed within a second 
rather than immediately; on macOS within a second rather than up to ten.
   - New `setStableTimeout`, default 200 ms: a file modified within it is left 
for the next scan, so a save still being written is not reloaded half-finished. 
A modification time in the future (a clock askew on a network share, a `touch 
-t`) is reported rather than waited out.
   - The public surface is otherwise unchanged — 
`FileWatcherResourceReloadStrategy`, its options and the protected 
`WatchFileChangesTask` keep their names, so subclasses such as 
`RouteOnDemandReloadStrategy` and `OpenApiGeneratorReloadStrategy` are 
unaffected.
   
   **Carried from #26907, because a scan does not subsume them:**
   
   - the properties (and any recompiled Groovy) of a change are applied first, 
each with `reloadRoutes` false, and the routes of the whole change are then 
reloaded **once**;
   - if the batch fails, the files are reloaded one at a time, so the reload 
still fails on the one file that is wrong and names it (CAMEL-24860);
   - a file whose reload failed is re-read from disk when the properties later 
change. This is the fix made by hand — save the route, read the error, add the 
property, save again — which is two saves minutes apart and cannot be coalesced 
by any scan interval.
   
   Tests:
   
   - `FileScanReloadTest` (7) — one save of several files is one set of 
changes; a rewrite of the same length is still a change; a deleted file is a 
change, once; files the filter rejects are not; a subdirectory created while 
running is found without registering it; a save still being written waits for 
the next scan; a future modification time is not waited out.
   - `RouteReloadBatchTest` (2) — a route, a second route file and the 
properties saved together are **one** route reload that does not fail (it 
counts the `onRouteReload` calls, which is what distinguishes batching from 
merely ordering the files); a batch with one broken file still loads the others.
   - `RouteReloadPropertiesRetryTest` — a reload that failed on a missing 
property loads when the property arrives.
   
   Locally green: 200 camel-core `org.apache.camel.support` tests (including 
the live `FileWatcherResourceReloadStrategyTest`, which drives a real reload 
through the scan), 7 camel-yaml-dsl reload tests, camel-xml-io-dsl reload 
tests, and a full reactor build. The batch and retry tests were each checked 
against the unfixed code and fail there.
   
   Found while measuring how well a local model writes Camel routes through the 
camel-jbang-mcp server: the model adds a property and uses it in the same step, 
and the route silently kept running its previous version.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   
   https://claude.ai/code/session_01Bp3538HRBPMQkb5ta9xRaj
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to