codeconsole opened a new pull request, #16089:
URL: https://github.com/apache/grails-core/pull/16089

   ## Problem
   
   `DirectoryWatcher` probes for JNA to decide whether the native macOS 
FSEvents watcher is usable:
   
   ```java
   Class.forName("com.sun.jna.Pointer");
   ```
   
   JNA is not what that watcher needs. `MacOsWatchServiceDirectoryWatcher` 
needs `io.methvin:directory-watcher`, which `grails-bootstrap` declares 
`compileOnly` and therefore never puts on an application's runtime classpath.
   
   Applications routinely acquire JNA transitively — 
`org.testcontainers:testcontainers` → 
`com.github.docker-java:docker-java-transport-zerodep` → `net.java.dev.jna:jna` 
is a common path. On macOS those applications are sent down a load of 
`MacOsWatchServiceDirectoryWatcher` that cannot succeed. The resulting 
`NoClassDefFoundError` is caught by a single catch-all that drops straight to 
`PollingDirectoryWatcher`, so they silently poll every watched directory on a 
1s timer instead of receiving file system events:
   
   ```
   Exception while trying to load WatchServiceDirectoryWatcher (this is 
probably Java 6
   and WatchService isn't available). Falling back to PollingDirectoryWatcher.
   Caused by: java.lang.NoClassDefFoundError: 
io/methvin/watchservice/MacOSXListeningWatchService
   ```
   
   The fallback ordering makes this worse than having no JNA at all: without 
JNA the `else` branch selects `WatchServiceDirectoryWatcher` and works 
correctly. Adding an unrelated dependency downgrades file watching.
   
   ## Change
   
   Probe for `io.methvin.watchservice.MacOSXListeningWatchService` — the class 
that actually backs the native watcher — and degrade in steps: native macOS → 
JDK `WatchService` → polling.
   
   Polling is now only reached if the JDK cannot supply a `WatchService`, which 
cannot happen on the Java 21 baseline. The message no longer claims Java 6. 
Absence of the optional dependency is logged at debug rather than warn, since 
it is an opt-in performance improvement rather than a fault.
   
   ## Verification
   
   Selected delegate, comparing this branch against `8.0.x` under three 
classpaths on macOS:
   
   | classpath | before | after |
   |---|---|---|
   | JNA present, `directory-watcher` absent | `PollingDirectoryWatcher` | 
`WatchServiceDirectoryWatcher` |
   | neither present | `WatchServiceDirectoryWatcher` | unchanged |
   | both present | `MacOsWatchServiceDirectoryWatcher` | unchanged |
   
   The broken case is fixed and both already-working cases are untouched.
   
   `DirectoryWatcherSpec` covers the three outcomes and fails on the current 
implementation (2 of 3 cases). Reproducing the reported shape needs JNA present 
and `directory-watcher` absent, so JNA is added to the test runtime classpath.
   
   `:grails-bootstrap:test` (142 tests), `codeStyle` and `rat` all pass.
   
   ## Note
   
   An alternative fix would be promoting `io.methvin:directory-watcher` to a 
real runtime dependency of `grails-bootstrap`, giving every macOS application 
native watching. That adds JNA and a native library to all application 
classpaths, so I kept the dependency optional here and fixed only the selection 
logic. Happy to take that route instead if preferred.


-- 
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