6.6.  Watch a directory for changes by using WatchService

[Note]

The new NIO.2 Java 7 WatchService allows you to track whether any files or directories in a particular directory (or directories) are being created, modified, or deleted. You might use this information to update a file listing in a GUI display or perhaps to detect the modification of configuration files that could then be reloaded. In previous Java versions, you must implement an agent running in a separate thread that keeps track of all the contents of the directories you wish to watch, constantly polling the file system to see if anything relevant has happened. In Java 7, the WatchService API provides the ability to watch directories. It removes all the complexity of writing your own file system poller and is based upon existing native system APIs for better performance.

The first step is to create a WatchService instance via the java.nio.file.FileSystems class. In most cases you will want to get the default file system and then invoke its newWatchService() method:

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

Now that we have our watch service instance, we want to register a path to watch. We create a Path object for the directory we wish to watch:

Path watchDir = Paths.get("C:\\home");
					

The Path class implements the java.nio.file.Watchable interface, and that interface defines two register(...) methods:


public interface Watchable {

	WatchKey register(WatchService watcher, WatchEvent.Kind<?>[] events, WatchEvent.Modifier... modifiers) throws IOException;

	WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) throws IOException;

}

					

The WatchKey register(WatchService watchService, WatchEvent.Kind<?>... events) registers the Path this method is called on with the specified WatchService for the specific events given. Events trigger a notification only if they are specified in the register call.

For the default WatchService implementation, the java.nio.file.StandardWatchEventKinds class defines four static implementations of WatchEvent.Kind that can be used in the register(...) calls:

For example, let's watch the ENTRY_CREATE and ENTRY_MODIFY events:

WatchKey watchKey = watchDir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY);
					

Our Path is now registered to be watched, and the WatchService will work away silently in the background, watching that directory intently. The same WatchService instance can watch multiple directories.

The register(...) method call returns a WatchKey class instance. This class represents your registration with the WatchService. Whether you store this reference or not is up to you, because the WatchService returns the relevant WatchKey to you when an event is triggered. When you are done with a particular WatchKey and the events it's registered for, you can cancel its registration with the WatchService simply by calling its cancel() method.

Now that our Path is registered, we can check in with the WatchService at our convenience to see if any of the events we were interested in has occurred. WatchService provides three methods for checking if anything exciting has happened:

Once a WatchKey has been returned by one of these three methods, it WILL NOT be returned by a further poll() or take() call UNTIL you put the key back into a Ready state by invoking its reset() method, even if events it is registered for occur.

A WatchKey has a state. At any given time, its state might be one of the following:

Once a WatchKey is returned by the WatchService, you can inspect and remove all its pending events that have been triggered by calling the WatchKey's pollEvents() method, which returns a List of WatchEvents.

Simple example:


// Create a file inside our watched directory
File newFile = new File(watchDir.toFile(), "newFile.txt");
newFile.createNewFile();

// Call take() and see if the event has been registered
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
    System.out.print(String.format("An event was found of kind %s.%n", event.kind()));
    System.out.print(String.format("The event occurred on file '%s'.%n", event.context()));
}

// Reset the key - this step is critical if you want to receive further watch events !!!
key.reset();

					

output is:

An event was found of kind ENTRY_CREATE.
The event occurred on file 'newFile.txt'.
					

As you can see, we got the ENTRY_CREATE event for the newly created newFile.txt as expected.

The Watch Service API is designed for applications that need to be notified about file change events. It is well suited for any application, like an editor or IDE, that potentially has many open files and needs to ensure that the files are synchronized with the file system.

This API is NOT designed for indexing a hard drive. Most file system implementations have native support for file change notification. The Watch Service API takes advantage of this support where available. However, when a file system does not support this mechanism, the Watch Service will poll the file system, waiting for events.

Professional hosting         Free 'Oracle Certified Expert Web Services Developer 6' Guide     Free SCDJWS 5.0 Guide