How to use Java's WatchService, I made a note because it fit unexpectedly when I started with a light feeling. The code is Kotlin, but is it the same in Java?
class Watcher private constructor(
val path: Path,
val listener: (e: WatchEvent<Path>, target: Path) -> Unit
) : AutoCloseable {
private val executor = Executors.newSingleThreadExecutor()
private val watchService = path.fileSystem.newWatchService()
init {
val watchKey = path.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY)
executor.submit {
while (true) {
val key = watchService.take()
assert(watchKey == key, { "Only one key is registered, so it should always match" })
for (e in watchKey.pollEvents()) {
@Suppress("UNCHECKED_CAST") //Whenever the WatchKey event is above, it is guaranteed to be Path
listener(
e as WatchEvent<Path>,
path.resolve(e.context())
)
}
val valid = watchKey.reset()
if (!valid) throw RuntimeException("Shouldn't happen normally")
}
}
}
override fun close() {
executor.shutdownNow()
executor.awaitTermination(1, TimeUnit.SECONDS) //This value is appropriate
watchService.close()
}
companion object {
fun watch(
path: Path,
listener: (e: WatchEvent<Path>, target: Path) -> Unit
): Watcher {
return Watcher(path, listener)
}
}
}
. Because
WatchService.take ()blocks until a change is detected.
poll ()does not block and returns
null`, but in any case it is more convenient to do it in a separate thread.WatchKey.pollEvents ()
returns a list of wildcard-type WatchEvent
s. However, WatchEvent.context ()
is Document and the event As long as is one of ʻENTRY_CREATE, ʻENTRY_DELETE
, ʻENTRY_MODIFY, it guarantees to always return
Path. In short, it will always be
Path`, so there is no problem casting it unconditionally.Path
returned byWatchEvent.context ()
is returned as a relative path from the directory being monitored. Therefore, if you try to use the returned Path
as it is, it will try to find the file based on the current directory. Therefore, the file cannot be found (unless the watch directory is current) and an exception is thrown. This behavior is rather unintuitive and inconvenient, so it returns to the listener a separate Path
that resolves to the Path
of the monitored directory.WatchKey.reset ()
. The return value indicates whether the reset was successful, but as far as you read Document The pattern is almost the same as ʻis Valid ()`. The condition that the key is valid is
- Explicitly canceled by calling the cancel method
- Implicitly canceled because the object is no longer accessible
- Canceled by closing the monitoring service
So, this code doesn't usually happen ...
WatchService.take ()
to return a value (about 5 seconds in my environment). slow.close ()
when there are no more referencesWatcher
?Difficult to use ...
Recommended Posts