Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Event fired only once when watching a directory

Tags:

java

nio

Using the java.nio watching service, I try to watch a directory and all of its subdirectories:

Files.walkFileTree(projectPath, new SimpleFileVisitor<Path>() {
   @Override
   public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        WatchKey key = dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
                StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
        watched.put(key, new WatchableDirectory(dir, projectPath));
        return FileVisitResult.CONTINUE;
    }
});

Then I wait for events:

executor.submit(new Runnable() {
        @Override
        public void run() {
            try {
                WatchKey key;
                while ((key = watcher.take()) != null) {
                    List<WatchEvent<?>> events = key.pollEvents();
                    WatchableDirectory watchableDirectory = watched.get(key);
                    for (WatchEvent<?> event : events) {
                         ....
                    }
             }
          ....
         }
    }

(watched is a Map that holds mappings from a key to metadata about the directory)

However, only the first event for a given directory is fired. Whenever I change another file in a directory, where a file has already been changed, nothing happens (I validate that by both putting a breakpoint and expecting the logic in the for-loop to take place).

However, if I modify a file in another directory, then everything works (again, only the first time).

No exceptions are thrown (there is a catch clause for java.lang.Exception), and the loop obviously continues to run.

I thought that probably once consumed, the directory might be deregistered. So I added a line to re-register it after its file is being handled. No effect.

Windows 7, Java 7.

Any ideas why?

like image 903
Bozho Avatar asked Nov 24 '13 20:11

Bozho


1 Answers

Don't forget to call

key.reset();

after you are done using it in your while loop.

The docs state

Otherwise if there are pending events for the object then this watch key is immediately re-queued to the watch service. If there are no pending events then the watch key is put into the ready state and will remain in that state until an event is detected or the watch key is cancelled.

and

Watch keys are safe for use by multiple concurrent threads. Where there are several threads retrieving signalled keys from a watch service then care should be taken to ensure that the reset method is only invoked after the events for the object have been processed. This ensures that one thread is processing the events for an object at any time.

So if you don't reset, it's as if your watch is disabled.

WatchKey#reset() returns a boolean value for if it is valid or not. Handle that case as explained in the tutorial.

Marko emphasises:

After the events for the key have been processed, you need to put the key back into a ready state by invoking reset. If this method returns false, the key is no longer valid and the loop can exit. This step is very important. If you fail to invoke reset, this key will not receive any further events.

like image 173
Sotirios Delimanolis Avatar answered Oct 19 '22 12:10

Sotirios Delimanolis