Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Java NIO’s WatchService can notify your application when entries in a directory are created, deleted, or modified. Register each directory, process events from its WatchKey, and reset the key so it can receive more events. Treat notifications as hints rather than a complete, lossless record: events can be combined, delayed, or lost, and a modification does not mean a writer has finished.

How to watch a directory

Create a WatchService from the default file-system provider, register a directory for the event types you need, then wait for signaled keys. Each event’s context is a relative path; resolve it against the directory that was registered to get the affected path. Oracle’s directory-watching tutorial describes this lifecycle.

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

import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;

public class DirectoryWatcher {
    public static void main(String[] args) throws IOException, InterruptedException {
        Path dir = Path.of("/path/to/watch");

        try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
            dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);

            while (true) {
                WatchKey key = watcher.take(); // Wait until a key is signaled.
                for (WatchEvent<?> event : key.pollEvents()) {
                    if (event.kind() == OVERFLOW) {
                        // Events may have been discarded. Re-scan or reconcile state.
                        continue;
                    }
                    if (!(event.context() instanceof Path relativePath)) {
                        continue;
                    }
                    Path changed = dir.resolve(relativePath);
                    // Validate, debounce, and process changed.
                }
                if (!key.reset()) {
                    break; // The directory is no longer accessible to this watch.
                }
            }
        }
    }
}

What the loop does

  1. newWatchService() creates the provider’s watcher.
  2. register() attaches a directory to the watcher for ENTRY_CREATE, ENTRY_DELETE, and ENTRY_MODIFY.
  3. take() blocks until a key is signaled. Use poll() instead when the application needs non-blocking checks.
  4. pollEvents() retrieves pending events for that key. For ordinary entry events, the context is a relative Path.
  5. reset() re-enables the key. If it returns false, the key is no longer valid and the loop should stop or arrange a new registration.
  6. The try-with-resources block closes the service when the watcher exits.

The API’s event context is not an absolute path, and application processing is deliberately left to the caller. Check event kinds before treating contexts as paths, handle I/O failures in your processing code, and avoid letting slow work prevent the loop from draining events promptly. See the Java SE WatchService API.

What events mean—and what they do not

  • ENTRY_CREATE: a directory entry was created.
  • ENTRY_DELETE: a directory entry was deleted.
  • ENTRY_MODIFY: an entry was modified. One underlying change can produce one or several notifications.
  • OVERFLOW: events may have been discarded. It can arrive even if it was not among the event kinds you registered.

These are change notifications, not a transaction log. A watcher can receive duplicate notifications, and a stream that overflows is incomplete. On OVERFLOW, rescan the affected directory or compare it with a persisted snapshot to reconstruct current state. If every change must be accounted for, use reconciliation or a producer-side protocol rather than assuming the event stream is lossless.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How to watch a directory tree recursively

A registration watches one directory, not all of its descendants. To cover a tree, walk it and register each directory. When a create event indicates a new subdirectory, register that directory too if it must be monitored. Otherwise, changes inside that new subtree will not be covered by the original root registration.

  1. Walk the existing tree and register every directory for the required event kinds.
  2. Keep a mapping from each WatchKey to the directory it represents. Resolve each event context against that directory, not always against the root.
  3. When a created entry is a directory, register it and its existing descendants before relying on events from that subtree.
  4. When a key becomes invalid, remove its mapping and decide whether the directory should be re-registered or the tree rescanned.
  5. After overflow, reconcile the relevant tree because events may have been lost in any registered directory.

The API does not provide a single recursive registration switch. The tutorial’s WatchService examples and guidance explain directory registration and key processing.

How to tell when a file is ready to read

A modify event does not prove that the process writing the file has finished. Oracle’s API documentation warns: “When an event is reported to indicate that a file in a watched directory has been modified then there is no guarantee that the program (or programs) that have modified the file have completed.” See the WatchService documentation.

Choose a readiness strategy that matches the producer:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Coordinate directly: have the writer signal completion through an application-level mechanism.
  • Publish by rename: where the filesystem and producer support it, write to a temporary name and rename to the final name only after the content is complete. Treat atomicity as a property to verify for the filesystem and workflow, not as a guarantee of WatchService itself.
  • Validate and retry: attempt to read and validate the expected content, retrying when it is incomplete or unavailable.
  • Use a file lock: use FileChannel locking only when the producer follows a compatible locking design.

Debounce repeated modify events when appropriate, but do not use a quiet-time delay alone as proof that no writer remains active.

Reliability, limits, and platform differences

Oracle notes that events can arrive faster than an application retrieves or processes them. Implementations may limit queued events and report OVERFLOW when discarding some. The OpenJDK WatchService implementation note says JDK implementations buffer up to 512 pending events for each registered watchable object. This is an implementation note, not a universal capacity guarantee for every provider or a promise that 512 events will always be available.

Providers may use native notification facilities or polling. As a result, timeliness, ordering, duplicate reporting, and detection of short-lived files vary. For network or other non-local filesystems, behavior is especially provider-dependent: the API does not require changes made by remote systems to be detected. Test against the actual filesystem and JDK provider used in deployment. Oracle describes WatchService as useful for cases such as editors and IDE synchronization, waiting for files to arrive, and deployment directories—not as a hard-drive indexing mechanism. See its API overview.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

When to use WatchService, polling, or another watcher

There is no universal performance winner established by the API documentation. Choose based on the guarantees and operating conditions your application needs:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Approach Useful when Key considerations
WatchService You want provider-backed change notifications for registered directories. Handle overflow, duplicates, non-recursive registration, provider-specific latency, and shutdown or re-registration.
Periodic polling You prefer scheduled state comparisons or need to recover from missed notifications through repeated scans. Choose a polling interval that balances detection delay against scan cost; compare state rather than assuming each intermediate change will be observed.
Third-party watcher You need a higher-level abstraction or additional recursive-watching behavior. Check its provider support, loss-recovery model, latency, CPU and I/O cost, duplicate handling, and lifecycle behavior for your target filesystems.

For any approach, define how the application detects readiness, recovers after restart, and reconciles current directory contents. WatchService can provide prompt hints where the provider supports them, but correctness should not depend on receiving exactly one event for every filesystem change.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.