I´m trying to watch a specific folder for changes, and then if any addition/edition/removal happens inside of it, I need to get the change type of all files in that folder and its subfolders. I'm using WatchService
for this but it only watches a single path, it doesn't handle subfolders.
Here's my approach:
try {
WatchService watchService = pathToWatch.getFileSystem().newWatchService();
pathToWatch.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
// loop forever to watch directory
while (true) {
WatchKey watchKey;
watchKey = watchService.take(); // This call is blocking until events are present
// Create the list of path files
ArrayList<String> filesLog = new ArrayList<String>();
if(pathToWatch.toFile().exists()) {
File fList[] = pathToWatch.toFile().listFiles();
for (int i = 0; i < fList.length; i++) {
filesLog.add(fList[i].getName());
}
}
// Poll for file system events on the WatchKey
for (final WatchEvent<?> event : watchKey.pollEvents()) {
printEvent(event);
}
// Save the log
saveLog(filesLog);
if(!watchKey.reset()) {
System.out.println("Path deleted");
watchKey.cancel();
watchService.close();
break;
}
}
} catch (InterruptedException ex) {
System.out.println("Directory Watcher Thread interrupted");
return;
} catch (IOException ex) {
ex.printStackTrace(); // Loggin framework
return;
}
Like I said before, I'm getting the log only for the files in the selected path, and I want to watch all folders and subfolders files, something like:
Example 1:
FileA (Created)
FileB
FileC
FolderA FileE
FolderA FolderB FileF
Example 2:
FileA
FileB (Modified)
FileC
FolderA FileE
FolderA FolderB FileF
Is there any better solution?
A WatchService
only watches the Path
s you register. It does not go through those paths recursively.
Given /Root
as a registered path
/Root
/Folder1
/Folder2
/Folder3
If there is a change in Folder3
, it won't catch it.
You can register the directory paths recursively yourself with
private void registerRecursive(final Path root) throws IOException {
// register all subfolders
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
return FileVisitResult.CONTINUE;
}
});
}
Now the WatchService
will notify all changes in all subfolders of Path root
, ie. the Path
argument you pass.
Registering recursively will work as Sotirios has indicated. This effectively registers each directory/sub-directory that currently exists.
You can alternatively import and use *com.sun.nio.file.ExtendedWatchEventModifier.FILE_TREE* as in:
dir.register(watcher, standardEventsArray, ExtendedWatchEventModifier.FILE_TREE);
This will watch the entire sub-tree for change AND account for added directories and sub-directories.
Otherwise you will have to monitor for any new directories/sub-directories and register them also. There can also be an issue with deleting parts of the directory hierarchy since each registered directory has a handle watching it so the (lowest) sub-directories need to be removed first when deleting parts of the structure.
I have implemented something like this using Java 8 streams and lambdas.
The recursive folder discovery is implemented as a Consumer @FunctionalInterface:
final Map<WatchKey, Path> keys = new HashMap<>();
Consumer<Path> register = p -> {
if (!p.toFile().exists() || !p.toFile().isDirectory()) {
throw new RuntimeException("folder " + p + " does not exist or is not a directory");
}
try {
Files.walkFileTree(p, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
LOG.info("registering " + dir + " in watcher service");
WatchKey watchKey = dir.register(watcher, new WatchEvent.Kind[]{ENTRY_CREATE}, SensitivityWatchEventModifier.HIGH);
keys.put(watchKey, dir);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
throw new RuntimeException("Error registering path " + p);
}
};
The above code is called every time a new folder is created to dynamically add folders at later stages. Full solution and more details here.
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