Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determining type of deleted file from WatchEvent

I have a WatchService that watches a directory tree for ENTRY_CREATE, ENTRY_DELETE AND ENTRY-MODIFY events. The problem is that the context of a WatchEvent<?> gives only a Path object. On delete events, I'm unsure of if the path referred to a directory of a regular file.

WatchKey key = null;

try {
    key = watcher.take();
} catch (InterruptedException e) {
    e.printStackTrace();
}

for (WatchEvent<?> event : key.pollEvents()) {
    if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
        continue;
    }

    Path parent = (Path) key.watchable();
    Path p = parent.resolve((Path) event.context());

    for (DirectoryModifiedListener listener : listeners) {
        if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
            // only do this if p referred to a file, not a directory
            listener.onFileCreated(p);
        }
    }
}

My question is how to determine if the path p referred to a deleted file or directory? Is this even possible with what the API exposes?

like image 398
efritz Avatar asked Oct 21 '22 22:10

efritz


1 Answers

Files.isDirectory(path) is - as the name suggests - a realtime check.

The Path object (respectively, the implementation WindowsPath) itself has no state (or cached history). It's not much more than a placeholder for a path.

Therefore the sad answer is: you can't determine if a Path had referred to a directory or not after it was deleted.

One solution would be to remember the kind of the Path objects as long they exist. That means gather a list before start watching:

Path parent = Paths.get("/my/hotfolder");

Set<String> subdirs = new HashSet<String>();

for (Path p : Files.newDirectoryStream(parent)) {
    if (Files.isDirectory(p))
        subdirs.add(p.getFileName().toString());
}

Register ENTRY_CREATE to keep the list up to date

WatchKey key = hotfolder.register(watcher, ENTRY_CREATE, ENTRY_DELETE);

Now you can determine if a child object was a directory or not:

for (WatchEvent<?> event : key.pollEvents())
{
    if (event.kind() == StandardWatchEventKinds.OVERFLOW)
        continue;

    Path p = ((Path) key.watchable()).resolve((Path) event.context());

    String filename = p.getFileName().toString();

    if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE && Files.isDirectory(p))
        subdirs.add(filename);

    if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE)
    {
        if (subdirs.contains(filename))
        {
            for (DirectoryModifiedListener listener : listeners)
                listener.onFileDeleted(p);

            subdirs.remove(filename);
        }
    }
}
like image 119
mkdev Avatar answered Oct 29 '22 01:10

mkdev