Skip to Content
Course content

96: Watching the Filesystem with WatchService

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years back, I was working on a legacy system that relied on a massive XML configuration file. The problem was that the app took nearly two minutes to boot up, but we needed to tweak settings frequently during deployment. To avoid that two-minute wait, my teammate tried to implement a "hot-reload" feature by writing a background thread that checked the file's timestamp every five seconds. It worked, but it was clumsy. On a network drive, that constant polling created a noticeable amount of chatter and unnecessary I/O overhead. I remember telling him, "Why are we asking the filesystem if it's changed every five seconds when the operating system can just tell us the moment it happens?"

That's where WatchService comes in. Part of the java.nio.file package, the WatchService is essentially a registration desk. You tell the OS which directory you're interested in and what specific events you care about—like a file being created, modified, or deleted—and then your program just sits back and waits for a notification. It's significantly more efficient than polling because it leverages native OS capabilities (like inotify on Linux or ReadDirectoryChangesW on Windows).

Registering for Events

To get this working, you first need to create a WatchService from the filesystem. Once you have that, you take a Path object for the directory you want to monitor and register it. When you register, you pass in a set of StandardWatchEventKinds. Most of the time, you'll want ENTRY_CREATE, ENTRY_DELETE, and ENTRY_MODIFY.

import java.nio.file.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class FolderWatcher {
    public static void main(String[] args) throws IOException, InterruptedException {
        WatchService watchService = FileSystems.getDefault().newWatchService();
        Path path = Paths.get("C:/my-configs"); // Use a path that exists on your machine

        // Register the directory for create, delete, and modify events
        path.register(watchService, 
            StandardWatchEventKinds.ENTRY_CREATE, 
            StandardWatchEventKinds.ENTRY_DELETE, 
            StandardWatchEventKinds.ENTRY_MODIFY);

        System.out.println("Watching directory: " + path);
        // Now we enter the loop...
    }
}

The Event Loop and the Reset Trap

The WatchService doesn't push events to you via a callback; instead, it queues them. You have to poll the service for WatchKey objects. I've seen plenty of developers get stuck here because they forget one crucial detail: WatchKey.reset(). If you don't call reset() on the key after processing the events, the key remains in a "signaled" state and will never trigger again for that directory.

You'll typically use a while(true) loop and call watchService.take(), which blocks the thread until an event actually occurs. If you don't want your thread to hang forever, you can use poll(long timeout, TimeUnit unit) instead. Here is how the actual processing loop looks:

while (true) {
    WatchKey key = watchService.take(); // Wait for an event

    for (WatchEvent< Path> event : key.pollEvents()) {
        WatchEvent.Kind< Path> kind = event.kind();
        Path fileName = event.context();

        System.out.println(kind.name() + " happened to file: " + fileName);
    }

    // CRITICAL: Reset the key to receive further events
    boolean valid = key.reset();
    if (!valid) {
        System.out.println("Directory no longer accessible.");
        break;
    }
}

One little quirk to keep in mind: some editors (like IntelliJ or VS Code) don't just "modify" a file. They often create a temporary file, delete the original, and rename the temp file to the original name. This means a single "Save" action in your editor might trigger a flurry of ENTRY_CREATE and ENTRY_MODIFY events. If you're building a production system, you'll likely want to add a tiny debounce delay to avoid reloading your config five times in ten milliseconds.




📋 Practical Task

Build a Real-time Log Monitor

Create a Java application that monitors a specific folder named logs/. Your application should start a background thread using WatchService that monitors for new files. Whenever a file ending in .log is created or modified in that folder, the program should print a message to the console saying: "Update detected in [filename] - triggering log analysis...". Ensure your code correctly handles the WatchKey reset logic and gracefully exits if the directory is deleted or becomes inaccessible.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.