WatchService - incorrectly resolved absolute path
Asked Answered
J

1

8

I've been playing around with the java.nio.file.WatchService and noticed that the Paths returned from WatchEvent.context() does not return correct .toAbsolutePath(). Here is an example application:

public class FsWatcher {
  public static void main(String[] args) throws IOException, InterruptedException {
    if (args.length != 1) {
      System.err.println("Invalid number of arguments: " + args.length);
      return;
    }
    //Run the application with absolute path like /home/<username>
    final Path watchedDirectory = Paths.get(args[0]).toAbsolutePath();
    final FileSystem fileSystem = FileSystems.getDefault();
    final WatchService watchService = fileSystem.newWatchService();
    watchedDirectory.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);

    while (true) {
      WatchKey watchKey = watchService.take();
      for (WatchEvent<?> watchEvent : watchKey.pollEvents()) {
        if (watchEvent.kind().equals(StandardWatchEventKinds.OVERFLOW)) {
          continue;
        }

        final Path createdFile = (Path) watchEvent.context();
        final Path expectedAbsolutePath = watchedDirectory.resolve(createdFile);
        System.out.println("Context path: " + createdFile);
        System.out.println("Context absolute path: " + createdFile.toAbsolutePath());
        System.out.println("Expected absolute path: " + expectedAbsolutePath);
        System.out.println("usr.dir: " + System.getProperty("user.dir"));
      }
      watchKey.reset();
    }
  }
}

Example output:

Context path: document.txt
Context absolute path: /home/svetlin/workspaces/default/FsWatcher/document.txt
Expected absolute path: /home/svetlin/document.txt
usr.dir: /home/svetlin/workspaces/default/FsWatcher

It seems that the absolute path is resolved against the user.dir system property instead of the Path used for the WatchService registration. That's a problem because when I try to use (for instance Files.copy()) the path returned from the WatchEvent I receive a java.nio.file.NoSuchFileException, which is expected as there is no such file at this path. Am I missing something or this is a bug in the JRE ?

Josephinajosephine answered 21/9, 2015 at 9:10 Comment(0)
B
14

This is not a bug, but certainly confusing.

If WatchEvent.context() returns a Path then it is relative:

In the case of ENTRY_CREATE, ENTRY_DELETE, and ENTRY_MODIFY events the context is a Path that is the relative path between the directory registered with the watch service, and the entry that is created, deleted, or modified.

Now if you turn such a path into an absolute path by calling toAbsolutePath() this happens not relative to the watched directory but to the default directory.

This method resolves the path in an implementation dependent manner, typically by resolving the path against a file system default directory. Depending on the implementation, this method may throw an I/O error if the file system is not accessible.

Therefore to turn the path into an absolute path you need to use

watchedDirectory.resolve(createdFile);
Boatright answered 21/9, 2015 at 9:38 Comment(2)
If, like me, you're mixing watched directories in one event loop (and thus run the risk of having multiple files by the same name in different directories, it's useful to be able to get the directory that's being evaluated by the current key: ((Path) key.watchable()).resolve( ((WatchEvent<Path>) event).context() )Hexarchy
I honestly think that this is definitely a bug. I get an absolute path to a file that is relative to the wrong directory, which definitely leads to an exception. When I call toAbsolutePath() I'm expecting to get the full path of the file that's just been created (the file that actually exists) and NOT to an inexistent file.Periwinkle

© 2022 - 2024 — McMap. All rights reserved.