Java WatchService Examples [Monitor Directory and Files]

Java WatchService Examples [Monitor Directory and Files]

Introduction to Java WatchService

The Java NIO.2 API includes theWatchServiceframework for monitoring changes to directories in real time. In modern operating systems, it is common for two processes to access the same files in the same directory during their execution. If we expect a directory or its contents to be changed over time by another process, we can use theWatchServiceAPI to monitor the directory for changes and react to those changes as soon as they occur.

Applying theWatchServiceAPI to monitor a directory requires a number of steps. The following is a high-level overview of theWatchServiceprocess:

  • Create an instance ofWatchServicefrom the file system.
  • Register each directory and event type.
  • Create a loop that repeatedly queries theWatchServicefor changes.
  • Retrieve aWatchKey.
  • Retrieve all pending events for theWatchKeyand do something with them.
  • Reset theWatchKey.
  • Once you no longer need theWatchService, close the resource.

Creating and ShuttingDown theWatchService

The first step is the easiest. We use theFileSystemshelper class to obtain a reference to the defaultFileSystem. We then use theFileSystemobject to obtain a newWatchServiceinstance, as shown in the following code snippet:

WatchService service = FileSystems.getDefault().newWatchService();

If theWatchServiceis being created and closed within a single method, we can apply the try-with-resource syntax, asWatchServiceextendsCloseablein order to complete the first and last steps in an abridged fashion:

import java.io.IOException;
import java.nio.file.*;

public class WatchServiceSample {
   public static void main(String[] args) throws IOException {
      try (WatchService service = FileSystems.getDefault().newWatchService()) {
         ...
      }
   }
}

Alternatively, if we are not creating and closing theWatchServiceinstance within a single method, we need to explicitly call theclose()method on theWatchServiceinstance after we have finished using it. Failure to close theWatchServiceafter we have finished with it could lead to resource-contention issues within the file system.


Different Events to monitor with Java Watcher

Next we should know the list of events which we would like to monitor.TheWatchServicecan be used on any class that implements theWatchableinterface, which requires the class to implementregister()methods. In the NIO.2 API, thePathinterface extends theWatchableinterface; therefore we can use ourWatchServiceinstance to monitor any number ofPathobjects by calling aregister()method.

Along with theWatchServiceinstance, theregister()method takes a vararg ofStandardWatchEventKinds enumvalues, which indicates the events for which we want to listen.

TheWatchService API supports the four event types listed in below table:

Enum Value Description
StandardWatchEventKinds.ENTRY_CREATE An element is added to the directory.
StandardWatchEventKinds.ENTRY_DELETE An element is removed from the directory.
StandardWatchEventKinds.ENTRY_MODIFY An existing element is modified in the directory.
StandardWatchEventKinds.OVERFLOW An event may have been lost. It is possible to receive this event even if it is not registered for.

How to create a Java Watcher?

The java watcher program will use java watchService API where you first register the directory to be watched. And then the events that you want to be notified for has to be registered into the program.

  1. Create an instance of the watchService WatchService watcher = pathDirectory.getFileSystem().newWatchService();
  2. Register a path using the nio.path Path pathDirectory = Paths.get("your directory path");
  3. Register the events that you want to be notified for StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,StandardWatchEventKinds.ENTRY_MODIFY
  4. Create a java watch key.
  5. Create a java list in which the events occurring can be stored.
  6. Record the file name
  7. Check if it is updated, deleted or modified.
  8. Show the results.

Java WatchService Example

Monitor single directory

import java.util.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
public class javaWatcher {
public static void main(String[] args) {
// Java watcher : register the directory to be watched.
Path pathDirectory = Paths.get("C:/Users/Azka/Desktop/assignment 1");
while (true) {
try {
// java watcher : create an instance of the watch Service
WatchService watcher = pathDirectory.getFileSystem().newWatchService();
// java watcher : register the events that are to be notified.
pathDirectory.register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,StandardWatchEventKinds.ENTRY_MODIFY);
// java watcher : watch key
WatchKey watchKey = watcher.take();
// java watcher : enter the events into a list
List<WatchEvent<?>> eventsList = watchKey.pollEvents();
//  for all events create a loop that iterates till the end of the
// list
for (WatchEvent event : eventsList) {
//  get the file name for the event
Path fileWatched = (Path) event.context();
if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
//file is created
System.out.println("File created: " + fileWatched);
}
// file is deleted
if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
System.out.println("File deleted: " + fileWatched);
}
//  file is modified.
if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
System.out.println("File modified: " + fileWatched);
}
}
} catch (Exception e) {
System.out.println("Error: " + e.toString());
}
}
}
}

In my case the directory contains the following content

java watcher

Now lets change the content of the file “average.txt” that originally contains the following content

java watcher

Now lets change it to

java watcher

Lets see what is the output of the code

File modified: average.txt

Similarly if I delete a file, the output becomes

File modified: average.txt

File deleted: best.txt

All these actions are performed and updated on runtime which our Java WatchService was able to monitor and report.

Monitor sub-directories recursively

WatchService only watches the files and directories immediately beneath it. What if we want to watch to see if eitherp.txtorc.txtis modified?

/dir
  | - parent
        | - p.txt
        | - child
             | - c.txt

One way is to register both directories:

WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = Paths.get("/dir/parent")
dir.register(watcher, ENTRY_MODIFY);
Patch child = Paths.get("dir/parent/child");
child.register(watcher, ENTRY_MODIFY);

This works. You can type in all the directories you want to watch. If we had a lot of child directories, this would quickly get to be too much work. If we need to iterate through a whole directory hierarchy instead of just a single directory, we can use aFileVisitor. TheFileswalkFileTree()method takes a starting path and performs a depth-first traversal of the file hierarchy, giving the providedFileVisitora chance to “visit” each path element in the tree.

Path myDir = Paths.get("/dir/parent");
final WatchService watcher = FileSystems.getDefault().newWatchService();
Files.walkFileTree(myDir, new SimpleFileVisitor<Path> () {
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        dir.register(watcher, ENTRY_MODIFY);
        return FileVisitResult.CONTINUE;
    }
});

This code goes through the file tree recursively registering each directory with the watcher.


Java WatchService Functions

Java WatchService have some important methods

Modifier and Type Method and Description
Void close() Closes the service
WatchKey poll() Retrieves and removes the next watch key
WatchKey take() Retrieves and removes next watch key, waiting if none are yet present.

Limitations of Java WatchService

  • Even though the WatchService API allows us to monitor a directory for changes, it does so with a number of known drawbacks. First off, it is possible to miss directory change events, you can add code to check whether kind == OVERFLOW, but that just tells you something went wrong. You don’t know what events you lost.
  • Second, when events are lost, we do not get any information about the lost events, other than we know that something was lost. Receiving no information about precisely which events were lost might make some people refrain from using theWatchServiceAPI altogether.
  • Finally, some JVMs implementations of theWatchServiceAPI are inefficient, with significant delays between the time that the directory is modified and the moment that the application is notified about the change. Some developers have even reported delays of up to five seconds. This may not seem like a significant amount of time to you, but for someone writing an application that continuously monitors a directory for changes, this may have a drastic impact on their application.

Conclusion

We have studied how a program can be a java watcher program and can be notified about any changes in a directory. The algorithm is further discussed, we can create an instance and register a path, after that we define the events that are to be notified within a directory, a list maintains these events and can be shown on the terminal or front-end of the program, this service helps a lot for security purposes, any changes in files will be notified on runtime.


Further Reading

  1. WatchService
  2. Java FileChannel
Azka Iftikhar

Azka Iftikhar

Computer Scientist

Proficient in multiple programming languages, including C++, Golang, and Java, she brings a versatile skillset to tackle a wide array of challenges. Experienced Computer Scientist and Web Developer, she excels as a MERN Stack Expert.