Java NIO2 WatchService

Java NIO2 – Watching a directory for changes

Java NIO2 WatchService was introduced in Java 7 as part of the NIO2. WacthService provides the ability for monitoring a file or directory for change.

In this articles, we will explore this exciting yet less known feature introduced under Java 7 NIO.  

 

1. Introduction 

WatchService can also be called as notification API, It allows the user to register directory(s) with the service with the ability for us to define a type of event we are interested (like file creation, deletion etc. ), it will notify user when it will detect change in the directory(s).

e.g. Let’s say you want to detect any if a new file(s) are being created in a given directory, you can use WacthService to register this directory and tell service to notify you if a new file is being created.

Most of the modern IDE provide such feature which enables them to detect any file change happening in the workbench (Have you ever seen a popup indicating file has been changed, please update ). If we want to implement a similar feature, we can create a polling API but the solution might not be perfect.

Java 7 NIO2 WacthService is a scalable solution to achieve above objectives.

 

2. WatchService Overview

The first step is to create a WatchService instance using FileSystems class

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

Register all directories with WatchService, which we are planning to monitor, during this registration we also need to specify what kind of event  we are interested in

Path directory = Paths.get("/Users/umesh/personal/tutorials/source");
WatchKey key = directory.register(watchService,
        ENTRY_CREATE,
        ENTRY_DELETE,
        ENTRY_MODIFY);

Pay close attention to following 2 points

  1. Path’s register method took WatchService as the first method
  2. The second argument to the method is of type StandardWatchEventKinds, which indicates what kind of event we are interested.

We can only register Watchable interface with WacthService and since Path class implements it, so this directory/path got registered with WacthService.

 

3.  StandardWatchEventKinds

While registering with WacthService, we need to specify what event(s) we are interested in. You can pass any or all of the following value during registration.

 

Event Name Description
ENTRY_CREATE Triggered when a new entry is created in the watched directory (file or directory creation).
ENTRY_DELETE Triggered when an entry is deleted/ moved in watched directory.
ENTRY_MODIFY Triggered when an entry is modified in the watched directory.
OVERFLOW Indicates that events might have been lost or discarded. You do not have to register for the OVERFLOW event to receive it.

You won’t be able to register individual files in WacthService. Service will throw NotDirectoryException in case you will register files with WacthService.

 

3.  WatchKey

When we register with WacthService, it returned WacthKey as a token, WacthKey contains certain state.

  1. When we first register with WatchService, WacthKey will be in ready state.
  2. In the case of an event, the key is signaled and queued so that it can be retrieved.
  3. WatchKey will remain in the same state until we call reset method.

 

4.  Processing Event

WatchService do not have any callback feature, it provided number of different ways to poll for getting this information

WatchKey pollEvent= watchService.poll();

poll() method will return queued key if available or will return null if unavailable.

WatchKey watchKey = watchService.poll(long timeout, TimeUnit units);

Above will return key immediately if available, in case it is not available, API will wait till the time specified in “timeout” parameter.

If we want to wait till the event occurred, We can use wait() method of the API. This method will return key immediately (if available) else it will wait for the event

WatchKey wait = watchService.take();

For WacthService to work correctly, we need to put the event back in the ready state once it has been processed. Call reset method on the WacthKey to reset it

key.reset();

In order to process rest of events, we need to fetch List of WatchEvent from the pollEvent() method. Here is a small example indicating how to handle and process these events in a real life use case.

WatchKey wait;
while ((wait = watchService.take()) != null) {
    for (WatchEvent<?> event : key.pollEvents()) {
        if (event.kind() == ENTRY_CREATE) {
            //handle create
        }
        if (event.kind() == ENTRY_DELETE) {
            //handle delete
        }
    }
}

5.  Complete Example

package com.umeshawasthi.tutorials.corejava.io.nio2;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

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

/**
 * Created by umesh on 6/15/17.
 */
public class NIOWatchService {

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

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

        Path directory = Paths.get("/Users/umesh/personal/tutorials/source");
        directory.register(watchService,
                ENTRY_CREATE,
                ENTRY_DELETE,
                ENTRY_MODIFY);

        WatchKey key;
        while ((key = watchService.take()) != null) {
            for (WatchEvent<?> event : key.pollEvents()) {
                if (event.kind() == ENTRY_CREATE) {
                    //handle create
                }
                if (event.kind() == ENTRY_DELETE) {
                    //handle delete
                }

                System.out.println(event.kind()+ " Event Happened on "+event.context());
            }

            key.reset();
        }
    }
}

When we run this program and done some changes in the source directory, we saw following output

ENTRY_MODIFY Event Happened on index2.html
ENTRY_MODIFY Event Happened on .DS_Store
ENTRY_CREATE Event Happened on untitled folder
ENTRY_MODIFY Event Happened on .DS_Store
ENTRY_CREATE Event Happened on WatchService
ENTRY_DELETE Event Happened on untitled folder

 

6.  When to Use WatchService

Watch Service API provides really some interesting features and  can be used in following places easily

  1. Processing files (e.g. Processing product price file), If price file is dropped in the directory, it will notify custom program to process it.
  2. An application server that watches a directory, waiting for some file to redeploy.

 

In this article, we explore one of the interesting feature introduced under Java 7 NIO Package. We explored various features of WatchService API and discussed how to use the different poll method to get information about the event. We also created a complete example to demonstrate how all these pieces work together.

All the code of this article is available Over on Github. This is a Maven-based project.

 

References

WatchService

Comments are closed.