The java.nio.file provides file/directory change notification mechanism. Followings are the steps to accomplish that.
-
WatchService watchService = FileSystems.getDefault().newWatchService() A single instance of WatchService is used to register multiple directories/subdirectories. Doing so enable us to receive external changes (create/delete/rename/modify) to those directories and the files which reside under them.
-
Registering a directory with the WatchService The instances of Watchable interface can register() themselves to WatchService, e.g. Path implements Watchable:
Path path = Paths.get("d:\\testDir");
WatchKey key = path.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
-
Polling events
while (true) {
WatchKey queuedKey = watchService.take();
for (WatchEvent<?> watchEvent : queuedKey.pollEvents()) {
System.out.printf("kind=%s, count=%d, context=%s Context type=%s%n ",
watchEvent.kind(),
watchEvent.count(), watchEvent.context(),
((Path) watchEvent.context()).getClass());
//do something useful with the modified file/folder here
if (!queuedKey.reset()) {
break;
}
}
}
WatchService#take() method blocks and waits until one or more keys are available in queue due to external changes. The returned key can be any of the directories registered with the service. The returned 'queuedKey' can be used to WatchKey#pollEvents(). Note that calling WatchKey#pollEvents() on the key return in step 2 above will not block and the 'while' loop will be constantly iterating which is not efficient at all, we should always use WatchService#take() method instead.
In above example creating/renaming/deleting files/directories will queue the WatchEvents. Here's the output on creating a new folder under 'd:\\testDir':
kind=ENTRY_CREATE, count=1, context=New folder Context type=class sun.nio.fs.WindowsPath
In the output, sun.nio.fs.WindowsPath is the implementation of Path for Windows operating system.
Recursively watching directories
Above example doesn't watch sub-directories under the main registered directory. Following is the complete code to achieve that:
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.util.HashMap;
import java.util.Map;
public class WatchServiceRecursiveExample {
private static Map<WatchKey, Path> keyPathMap = new HashMap<>();
public static void main (String[] args) throws Exception {
try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
registerDir(Paths.get("d:\\testDir"), watchService);
startListening(watchService);
}
}
private static void registerDir (Path path, WatchService watchService) throws
IOException {
if (!Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS)) {
return;
}
System.out.println("registering: " + path);
WatchKey key = path.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
keyPathMap.put(key, path);
for (File f : path.toFile().listFiles()) {
registerDir(f.toPath(), watchService);
}
}
private static void startListening (WatchService watchService) throws Exception {
while (true) {
WatchKey queuedKey = watchService.take();
for (WatchEvent<?> watchEvent : queuedKey.pollEvents()) {
System.out.printf("Event... kind=%s, count=%d, context=%s Context type=%s%n",
watchEvent.kind(),
watchEvent.count(), watchEvent.context(),
((Path) watchEvent.context()).getClass());
//do something useful here
if (watchEvent.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
//this is not a complete path
Path path = (Path) watchEvent.context();
//need to get parent path
Path parentPath = keyPathMap.get(queuedKey);
//get complete path
path = parentPath.resolve(path);
registerDir(path, watchService);
}
}
if(!queuedKey.reset()){
keyPathMap.remove(queuedKey);
}
if(keyPathMap.isEmpty()){
break;
}
}
}
}
Example Project
Dependencies and Technologies Used:
|
|