Java WatchService Examples [Monitor Directory and Files]


JAVA

Reviewer: Deepak Prasad

Introduction to Java WatchService

The Java NIO.2 API includes the WatchService framework 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 the WatchService API to monitor the directory for changes and react to those changes as soon as they occur.

Applying the WatchService API to monitor a directory requires a number of steps. The following is a high-level overview of the WatchService process:

  • Create an instance of WatchService from the file system.
  • Register each directory and event type.
  • Create a loop that repeatedly queries the WatchService for changes.
  • Retrieve a WatchKey.
  • Retrieve all pending events for the WatchKey and do something with them.
  • Reset the WatchKey.
  • Once you no longer need the WatchService, close the resource.

 

Creating and Shutting Down the WatchService

The first step is the easiest. We use the FileSystems helper class to obtain a reference to the default FileSystem. We then use the FileSystem object to obtain a new WatchService instance, as shown in the following code snippet:

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

If the WatchService is being created and closed within a single method, we can apply the try-with-resource syntax, as WatchService extends Closeable in 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 the WatchService instance within a single method, we need to explicitly call the close() method on the WatchService instance after we have finished using it. Failure to close the WatchService after 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. The WatchService can be used on any class that implements the Watchable interface, which requires the class to implement register() methods. In the NIO.2 API, the Path interface extends the Watchable interface; therefore we can use our WatchService instance to monitor any number of Path objects by calling a register() method.

Along with the WatchService instance, the register() method takes a vararg of StandardWatchEventKinds enum values, which indicates the events for which we want to listen.

The WatchService 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 either p.txt or c.txt is 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 a FileVisitor. The FileswalkFileTree() method takes a starting path and performs a depth-first traversal of the file hierarchy, giving the provided FileVisitor a 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 the WatchService API altogether.
  • Finally, some JVMs implementations of the WatchService API 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

She is proficient in multiple programming languages, including C++, GO, 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. You can check her professional profile on GitHub which captures her experience and portfolio.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment