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]
