Implementing WatchService API in Java 7 to monitor the directory changes

SHARE & COMMENT :

In Java 7 there were quite a lot of new things added to the File NIO package (java.nio)- there was a new java.nio.file package and java.nio.file.attribute package. Main highlights of the java.nio.file package are the following classes:

  • Path: Its an object used to locate a file or directory in the file system. The value contained in the Path instance is platform dependent. For example on Windows the file separators would be “\” while in a Unix platform it would be “/”.
  • Files: This class contains static methods which operate on the Path instance to create files, delete or move the files around and to even get the BufferedReader, BufferedWriter, InputStream, OutputStream among other methods.
  • FileSystems: Provides static methods to create instance of FileSystem
  • WatchService: A service for watching different locations in the file system for events like modification, creation, deletion. Classes which implement Watchable can be used to register with a watch service. An instance of WatchService can be obtained from the FileSystem instance.
  • WatchKey: When a Watchable entity is registered with a WatchService a key which is a WatchKey is generated. Initially the key is in ready state waiting to be notified of any events on the Watchable entity. Once an event occurs the key goes into signaled state and allows to access the events using its pollEvents method. After processing the poll events the key has to be reset by invoking its reset method.

also read:

Lets look at an example of watching the “/tmp/nio” directory (Note: this example was executed on Unix platform, file separator would have to be changed for executing this in Windows platform) for Modification and Deletions happening in the directory.
We first have to create a Path instance to indicate this directory:

Path tmpPath = Paths.get("/tmp/nio/");

Obtain a WatchService from the FileSystem class:

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

Now we would have to register the Path created above with the WatchService created above and also specify the kind of events we are interested to watch. For the kind of events we make use of StandardWatchEventKinds class and its fields ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE.


/*
 * Watching the /tmp/nio/ directory
 * for MODIFY and DELETE operations
 */
tmpPath.register(
        watchService,
        StandardWatchEventKinds.ENTRY_MODIFY,
        StandardWatchEventKinds.ENTRY_DELETE);

To watch the directory we place all the processing login in an infinite loop (we can also spawn a different thread to watch, but in this example lets keep it simple). And perform the following tasks:
1. Wait for any key to be signaled by invoking the take() method of the WatchService class. One can even use poll() method but the difference is that take() is a blocking call and waits until some key is signaled.
2. Once a WatchKey is obtained, poll for the events available in the key.
3. For each event find out the type of the event using event.kind().type() and also the Path which the event is acting on by using event.context() method.

The above 3 steps in code would be:

/*
 * Keep polling for events on the watched directory,
 */
for(;;){
    WatchKey key = watchService.take();

    //Poll all the events queued for the key
    for ( WatchEvent<?> event: key.pollEvents()){
        WatchEvent.Kind kind = event.kind();
        switch (kind.name()){
            case "ENTRY_MODIFY":
                System.out.println("Modified: "+event.context());
                break;
            case "ENTRY_DELETE":
                System.out.println("Delete: "+event.context());
                break;
        }
    }
    //reset is invoked to put the key back to ready state
    boolean valid = key.reset();

    //If the key is invalid, just exit.
    if ( !valid){
        break;
    }
}

Dont forget to reset() the key once done with handling the events. Otherwise the key would not be in Ready state to listen to more events.

Combining all the parts discussed above, the complete program would be:

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.*;

public class DirectoryWatcherDemo {

    public static void main(String[] args) throws 
        URISyntaxException, 
        IOException, 
        InterruptedException {

        Path tmpPath = Paths.get("/tmp/nio/");
        WatchService watchService = 
            FileSystems.getDefault().newWatchService();

         //Watching the /tmp/nio/ directory
         //for MODIFY and DELETE operations
        tmpPath.register(
                watchService,
                StandardWatchEventKinds.ENTRY_MODIFY,
                StandardWatchEventKinds.ENTRY_DELETE);

        /*
         * Keep polling for events on the watched directory,
         */
        for(;;){
            WatchKey key = watchService.take();

            //Poll all the events queued for the key
            for ( WatchEvent<?> event: key.pollEvents()){
                WatchEvent.Kind kind = event.kind();
                switch (kind.name()){
                    case "ENTRY_MODIFY":
                        System.out.println("Modified: "+event.context());
                        break;
                    case "ENTRY_DELETE":
                        System.out.println("Delete: "+event.context());
                        break;
                }
            }
            //reset is invoked to put the key back to ready state
            boolean valid = key.reset();

            //If the key is invalid, just exit.
            if ( !valid){
                break;
            }
        }

    }
}

Running the following commands in the terminal in “/tmp/nio” directory:

mohamed@mohamed:/tmp/nio$ ls
mohamed@mohamed:/tmp/nio$ touch t1
mohamed@mohamed:/tmp/nio$ touch t2
mohamed@mohamed:/tmp/nio$ touch t3
mohamed@mohamed:/tmp/nio$ rm t1
mohamed@mohamed:/tmp/nio$ vim t2
mohamed@mohamed:/tmp/nio$ mkdir t4
mohamed@mohamed:/tmp/nio$ rm -r t4
mohamed@mohamed:/tmp/nio$ ls
t2  t3

results in the following output in the directory watching program:

Modified: t1
Modified: t2
Modified: t3
Delete: t1
Delete: .t2.swpx
Delete: .t2.swp
Modified: .t2.swp
Modified: .t2.swp
Modified: t2
Modified: t2
Delete: .t2.swp
Delete: t4

One thing to note here is that we did execute mkdir t4 to create a t4 directory but that wasn’t notified because we didn’t register the ENTRY_CREATE event. Also once the new directory is added one has to register the new directory with the WatchService. I will write about this in a separate post sometime later.

Do drop in your queries or any issues you are facing with the above example and explanation.

Comments

comments

About Mohamed Sanaulla

In his day job he works on developing enterprise applications using ADF. He is also the moderator of JavaRanch forums and an avid blogger.

Comments

  1. Hi,

    Can you update your post to include use of the Watchable interface?

    ~g1

  2. BUG REPORT 01 :
    Upon modification it reports twice as :
    Modified: JetAir.csv
    Modified: JetAir.csv

Trackbacks

  1. [...] recently wrote about watching a directory using the new APIs introduced as part of Java 7. In this post let me throw some light on another [...]

  2. [...] the enhancements to the java.nio package as part of Java 7 we wrote about watching a directory for changes and also looked at few methods in the Files [...]

  3. JavaPins says:

    Implementing WatchService API in Java 7…

    Thank you for submitting this cool story – Trackback from JavaPins…

Speak Your Mind

*

Close
Please support the site
By clicking any of these buttons you help our site to get better