Monitoring a filesystem, as it turns out, is not that simple afterall. Given the plethora of considerations, like infinite data volume, less performance overhead, fast, light-weight etc. that we have already seen in the preceding posts of this series. By this time it was very clear that scanning through directories at regular intervals would not scale when the data set is huge. However, if there is some event based system that kicks in only when there is a change in the filesystem, that is our guy! And Java’s inherent nio package comes to the rescue. Why didn’t we think about it all this time? It was right there, always…
In layman terms, we would rely on the signals generated by the underlying OS (we can think of events generating pulses whenever an event occurs), and take necessary actions. Let’s see how.
Java.nio.file package contains interface WatchService that we are going to leverage in this case. We create a WatchService object.
WatchService watcher = FileSystems.getDefault().newWatchService();
We then register the WatchService object with the directory path that we are interested in.
Path dir = Paths.get("/path/...");
WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
Notice that register method takes the watcher object as arguement, along side other arguements that are basically the events that we are actually interested in (file creation, file deletion or file modified). It gives back a Watchkey object.
When the Watcherservice detects one of those events of interest, it is forwarded to the registered process. The registered process has a thread (or a pool of threads) dedicated to watching for any events it has registered for. When an event comes in, it is handled as needed.
Now, we create an infinite loop to repeatedly poll for any events.
while (true) {
WatchKey key;
try {
// wait for a key to be available
key = watcher.take();
} catch (InterruptedException ex) {
return;
}
Here, we wait for any incoming events. When the event occurs, the key is placed in the watcher’s queue. We have to retrieve the key and poll for all the events that have occurred.Key.pollevent() will give us an WatchEvent object. We can deduce the kind of the event (event.kind()). For each event kind, necessary actions may be taken. We can extract the file name from the context of WatchEvent.
for (WatchEvent event : key.pollEvents()) {
// get event type
WatchEvent.Kind kind = event.kind();
// get file name
WatchEvent ev = (WatchEvent) event;
Path fileName = ev.context();
System.out.println(kind.name() + ": " + fileName);
if (kind == OVERFLOW) {
continue;
} else if (kind == ENTRY_CREATE) {
// create event
} else if (kind == ENTRY_DELETE) {
// delete event
} else if (kind == ENTRY_MODIFY) {
// modify event
}
}
After the necessary actions have been taken, we can now reset the key and again start waiting for events. This part is very important, as it will not register further events if not reset.
boolean valid = key.reset();
if (!valid) {
break;
}
Note: This code watches only the parent directory that is registered by default. In order to watch sub-folders, we have to register all the directories individually. Visit the sample source code in github (mentioned in reference) that shows how to recursively watch subfolders.
Reference: