Search this site


 Home  I/O  Buffering  Character streams  NIO intro  Buffers  Channels  Buffer performance

Search this site:
Threads Database Profiling Regular expressions Random numbers Compression Exceptions C Equivalents in Java

 What do you think of this article? Did it help you? Found a mistake? Feedback and suggestions here

Using the WatchService API

As mentioned on the previous page, filing system notifications are sent to your Java application via the WatchService API. The API works in the following stages:

  • you ask the filing system for an instance of WatchService;
  • you take Path objects representing the directories you want to watch and ask them to register themselves with your WatchService instance;
  • usually in a separate thread, you poll the WatchService for notifications; calls to poll for notifications block until a new notification comes in, at which point the polling thread is "woken up" with the notification.

Getting an instance of WatchService

The first stage of using filing system notifications is to get an instance of WatchService. You will generally do this by calling:

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

The watch service will then be ready to register paths (directories) with and to poll for events.

Registering directories for modification notifications

Logically, you might then expect to call a method on your newly acquired WatchService object to register particular directories with it. In fact, registration happens the other way round: you call the register() method on the Path objects representing the directories you wish to listen for notifications on.

In practice, this means that you register a File object (representing the directory you wish to listen for notifications on) as follows:

import static java.nio.file.StandardWatchEventKind.*;

...
public class MyWatcher {
  private final WatchService watcher;
  private final Map keyPaths =
    new ConcurrentHashMap();

  public void watchDir(File dir) throws IOException {
    Path p = dir.toPath();
    WatchKey key = p.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
    keyPaths.put(key, p);
  }
}

Notice that when registering the Path object (in this case derived from a corresponding File object), we supply three constants to say which types of events we wish to listen for. These constants are defined in StandardWatchEventKind. (In case you're not familiar with the import static syntax, added in Java 5, it allows you to "import" the definitions of static constants from a particular class as though they were defined in your class, so that they can be referenced without any class prefix.)

In reality, it is unlikely that you would only register for a subset of these events. Even if you are only interested in "modifications", some events you which logically think of as "modifications" may in reality be notified to your application as creation/deletion events. Notably, the renaming of a file is likely to be notified to your application as a deletion/creation pair.

The Path.register() method returns a WatchKey object. It is up to us to associate this key with the original path registered if this is important to us. Later on, our application will receive notifications of modifications relative to a particular key, but we will not be able to retrieve the identity of the original directory: now is our opportunity to record this information if we need it. Because we will typically poll for modifications in another thread, we need access this map in a thread-safe manner. Using a ConcurrentHashMap is a good choice.

Listening for modifications

Now, we are ready to listen or "poll" for file modifications.

comments powered by Disqus

Written by Neil Coffey. Copyright © Javamex UK 2012. All rights reserved.