Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java WatchService: register, consume, and reset each key

Last updated: 4 Oct 20265 min read
tutorial
AdvancedBy AITrove Editorial

WatchService reports changes to registered watchable objects through signaled keys. The consumer retrieves a key, reads its events, and resets it so later changes can be signaled.

Operational contract

A watched directory is a hint source, not a durable message queue. Register the directory for the event kinds needed by the application, process a key's current batch, then inspect reset's boolean result. A false result means the registration is no longer valid and requires a new ownership decision. The event context for a Path registration is relative to the watched directory. The code keeps resolution local to that directory and returns whether observation remains active; the owner closes the service on shutdown.

Failure case

A depot process watches a staging folder where 47 manifests may arrive over a shift. One create notification prompts an intake check, but a modify notification can arrive while the producer is still writing. The consumer records candidate paths and waits for the publication contract described in the next lesson. It does not parse each event as a complete manifest or promise that every short-lived file will be observed.

Java code

Java
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.Set;

public class StagingWatchConsumer {
    public record Outcome(boolean rescan, boolean active) {}

    public static WatchService register(Path staging) throws IOException {
        WatchService service = FileSystems.getDefault().newWatchService();
        try {
            staging.register(service, StandardWatchEventKinds.ENTRY_CREATE,
                StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
            return service;
        } catch (IOException | RuntimeException failure) {
            try { service.close(); }
            catch (IOException closeFailure) { failure.addSuppressed(closeFailure); }
            throw failure;
        }
    }

    public static Outcome takeOne(WatchService service, Path staging, Set<Path> candidates)
            throws InterruptedException {
        WatchKey key = service.take();
        boolean rescan = false;
        for (WatchEvent<?> event : key.pollEvents()) {
            if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
                rescan = true;
                continue;
            }
            Object context = event.context();
            if (context instanceof Path) {
                Path relative = (Path) context;
                if (!relative.isAbsolute()) candidates.add(staging.resolve(relative).normalize());
            }
        }
        return new Outcome(rescan, key.reset());
    }
}

Performance and ownership cost

Processing E delivered events is O(E) plus set insertion. The set retains O(U) distinct candidate paths, where U is the number of unique names. If the producer outruns the consumer, event loss can occur; reset and overflow handling must be paired with a directory reconciliation path.

Common Mistakes

  • Do not forget to reset the key after processing its batch.
  • Do not resolve event context against an unrelated working directory.
  • Do not equate a create event with a finished file.

Connected lessons

java
filesystem observation
watch-service-key-lifecycle
Storage details