Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content
HowToDoInJava
  • Java
  • Spring AI
  • Spring Boot
  • Hibernate
  • JUnit 5
  • Interview

Java WatchService Tutorial

Learn to register for changes in a directory, sub-directories and files using the Java WatchService API, handle the changes and cancel it.

Lokesh Gupta

February 7, 2023

Java 8
Java 8, Java WatchService
java 8

In this example, we will learn to watch a directory along with all sub-directories and files for changes, using Java WatchService API.

1. WatchService API

To Register WatchService, get the directory path and use path.register() method.

WatchService watchService = FileSystems.getDefault().newWatchService();
WatchKey watchKey =  Paths.get("pathToDir").register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);

The WatchKey instance returned after the registration is used to poll the events that we have registered for, if they occur.

WatchKey watchKey = watchService.poll();  //returns all occured events right away

WatchKey watchKey = watchService.poll(long timeout, TimeUnit units);  //wait for specified time before returning

If we want to block until the event is occurred, we can use the take() API.

WatchKey watchKey = watchService.take();

Finally, we must call reset() API after each take() or poll() execution else the WatchService does not detect any future changes.

2. How to Use Watch Key

To get the changes occurred on the directory and files inside it, use watchKey.pollEvents() method, which returns the collection of all change events in form of a Stream.

WatchKey watchKey = null;
while ((key = watchService.take()) != null) {
  watchKey.pollEvents().stream().forEach(event -> System.out.println(event.context()));
  watchKey.reset();

  //cancel the watch key
  if(some condition) {
    watchKey.cancel();
  }
}

The watch key remains valid until:

  • It is canceled explicitly by invoking its cancel() method, or
  • Canceled implicitly because the object is no longer accessible, or
  • By closing the watch service.

If you are reusing the same key to get change events multiple times inside a loop, then don’t forget to call watchKey.reset() method, which sets the key in the ready state again.

Please note that several things are highly dependent of underlying operating system such as how events are detected, their timeliness, and whether their ordering is preserved. Some changes may result in single entry in one OS, while similar changes may result into multiple events in another OS.

3. Watch Directories, Sub-directories and Files for Changes

In this example, we will see an example of watching a directory with all sub-directories and files inside it. We will maintain a map of watch keys and directories Map<WatchKey, Path> keys to correctly identify which directory has been modified.

The below method will register a single directory to the watcher and then store the directory and key inside a map.

private void registerDirectory(Path dir) throws IOException
{
	WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
	keys.put(key, dir);
}

We will call this method recursively while walking a directory structure and calling this for each directory we encounter.

private void walkAndRegisterDirectories(final Path start) throws IOException {
  // register directory and sub-directories
  Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
      registerDirectory(dir);
      return FileVisitResult.CONTINUE;
    }
  });
}

Please note that at any time a new directory is created, we will register it with WatchService and a new key will be added to Map.

WatchEvent.Kind kind = event.kind();
if (kind == ENTRY_CREATE) {
	try {
		if (Files.isDirectory(child)) {
			walkAndRegisterDirectories(child);
		}
	} catch (IOException x) {
		// do something useful
	}
}

Putting all the above together along with logic to process the events, the complete example looks like this:

import static java.nio.file.StandardWatchEventKinds.*;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
 
public class WatchServiceExample {
 
  private final WatchService watcher;
  private final Map<WatchKey, Path> keys;
 
  /**
   * Creates a WatchService and registers the given directory
   */
  WatchServiceExample(Path dir) throws IOException {
    this.watcher = FileSystems.getDefault().newWatchService();
    this.keys = new HashMap<WatchKey, Path>();
 
    walkAndRegisterDirectories(dir);
  }
 
  /**
   * Register the given directory with the WatchService; This function will be called by FileVisitor
   */
  private void registerDirectory(Path dir) throws IOException 
  {
    WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
    keys.put(key, dir);
  }
 
  /**
   * Register the given directory, and all its sub-directories, with the WatchService.
   */
  private void walkAndRegisterDirectories(final Path start) throws IOException {
    // register directory and sub-directories
    Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
      @Override
      public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        registerDirectory(dir);
        return FileVisitResult.CONTINUE;
      }
    });
  }
 
  /**
   * Process all events for keys queued to the watcher
   */
  void processEvents() {

    WatchKey key = null;
    while ((key = watchService.take()) != null) {
 
      Path dir = keys.get(key);
      if (dir == null) {
        System.err.println("WatchKey not recognized!!");
        continue;
      }
 
      for (WatchEvent<?> event : key.pollEvents()) {
        @SuppressWarnings("rawtypes")
        WatchEvent.Kind kind = event.kind();
 
        // Context for directory entry event is the file name of entry
        @SuppressWarnings("unchecked")
        Path name = ((WatchEvent<Path>)event).context();
        Path child = dir.resolve(name);
 
        // print out event
        System.out.format("%s: %s\n", event.kind().name(), child);
 
        // if directory is created, and watching recursively, then register it and its sub-directories
        if (kind == ENTRY_CREATE) {
          try {
            if (Files.isDirectory(child)) {
              walkAndRegisterDirectories(child);
            }
          } catch (IOException x) {
            // do something useful
          }
        }
      }
 
      // reset key and remove from set if directory no longer accessible
      boolean valid = key.reset();
      if (!valid) {
        keys.remove(key);
 
        // all directories are inaccessible
        if (keys.isEmpty()) {
          break;
        }
      }
    }
  }
 
  public static void main(String[] args) throws IOException {
    Path dir = Paths.get("c:/temp");
    new WatchServiceExample(dir).processEvents();
  }
}

After running this program and making changes in files and directories in the given input, you will notice the captured events in the console.

ENTRY_CREATE: c:\temp\New folder
ENTRY_DELETE: c:\temp\New folder
ENTRY_CREATE: c:\temp\data
ENTRY_CREATE: c:\temp\data\New Text Document.txt
ENTRY_MODIFY: c:\temp\data
ENTRY_DELETE: c:\temp\data\New Text Document.txt
ENTRY_CREATE: c:\temp\data\tempFile.txt
ENTRY_MODIFY: c:\temp\data
ENTRY_MODIFY: c:\temp\data\tempFile.txt
ENTRY_MODIFY: c:\temp\data\tempFile.txt
ENTRY_MODIFY: c:\temp\data\tempFile.txt

That’s all for this simple example of using Java 8 WatchService API to watch for file changes and handle them.

Happy Learning !!

Comments

Subscribe
Notify of
6 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

Java 8 Tutorial

  • Java 8 Features
  • Java 8 forEach
  • Java 8 Stream
  • Java 8 Boxed Stream
  • Java 8 Lambda Expression
  • Java 8 Functional Interface
  • Java 8 Method Reference
  • Java 8 Default Method
  • Java 8 Optional
  • Java 8 Predicate
  • Java 8 Regex as Predicate
  • Java 8 Date Time
  • Java 8 Iterate Directory
  • Java 8 Read File
  • Java 8 WatchService
  • Java 8 String to Date
  • Java 8 Difference Between Dates
  • Java 8 Join Array
  • Java 8 Join String
  • Java 8 Exact Arithmetic
  • Java 8 Comparator
  • Java 8 Base64
  • Java 8 SecureRandom
  • Internal vs External Iteration
Photo of author

Lokesh Gupta

A fun-loving family man, passionate about computers and problem-solving, with over 15 years of experience in Java and related technologies. An avid Sci-Fi movie enthusiast and a fan of Christopher Nolan and Quentin Tarantino.
Follow on Twitter Portfolio

Previous

Python Httplib2

Next

Listing All Files in a Directory in Java

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Tutorial Series

OOP

Regex

Maven

Logging

TypeScript

Python

Meta Links

About Us

Advertise

Contact Us

Privacy Policy

Our Blogs

REST API Tutorial

Follow On:

  • Github
  • LinkedIn
  • Twitter
  • Facebook
Copyright © 2026 | Sitemap