I was a bit surprised that Watch Service should be implemented as a process/thread with a polling loop. I don't remember any other API in Java that is supposed to do it.
Wouldn't it be much better to implement it as a set of callbacks (listeners, observers, etc..)?
BTW - is there any third-party library that does pretty much the same, but using callbacks model?
Apache Commons has some file watching services which I think are much better than the ones in Java 7. Im not sure if they use callbacks, but they are much more intuitive in my opinion.
And yes, I think that an observer model would be much better. I read somewhere that its a bit tougher to do this with Java because it runs through a virtual machine, and to get listeners on files you need to talk directly to the OS. Im not sure of the details or validity of this though.
I build on Alexeis answer, but I don't think he was clear enough... look at this example code:
WatchService service = dir.getFileSystem().newWatchService();
WatchKey key = dir.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
System.out.println(key);
for(;;) {
WatchKey k = service.take();
System.out.println(k);
for(WatchEvent<?> ev:k.pollEvents()) {
System.out.println(ev.kind());
if(ev.kind() == OVERFLOW) continue;
//TODO
System.out.println(ev.context());
}
if(!k.reset()) break;
}
key.cancel();
If you check the println(key)
/ println(k)
outputs, you'll see that the same object is returned over and over; take()
returns every time that key is signalled, so to use multiple keys with the same WatchService
, just check what key was returned every time. That also means that your loop blocks as long as the key was not signalled - exactly what you want.
the if(!k.reset()) break;
is crucial too; this is what I forgot when writing my comment on Alexeis answer. My assumption was that take()
is called by register()
, and thus no key should be returned in my user code, because register() already consumed it. With that mis-assumption, I didn't recognize what was actually going on.
You don't have to use polling, you can use WatchService.take(), which returns immediately when a change occur. Yes it requires a thread, but single thread can be used for multiple objects being watched. So it is easy to implement a singleton which owns a watching thread and allows to register callbacks for each object.
As for third party libraries, look at Guava EventBus. I did not try it and am not sure if it fits your needs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With