![]() | |
|
The new NIO.2 Java 7 WatchService
allows you to track whether any files or directories in a particular directory (or directories) are
being created, modified, or deleted. You might use this information to update a file listing in a GUI display or perhaps to detect the modification
of configuration files that could then be reloaded. In previous Java versions, you must implement an agent running in a separate thread that keeps
track of all the contents of the directories you wish to watch, constantly polling the file system to see if anything relevant has happened.
In Java 7, the WatchService
API provides the ability to watch directories. It removes all the complexity of writing your own file
system poller and is based upon existing native system APIs for better performance.
The first step is to create a WatchService
instance via the java.nio.file.FileSystems
class. In most cases you will want
to get the default file system and then invoke its newWatchService()
method:
WatchService watchService = FileSystems.getDefault().newWatchService();
Now that we have our watch service instance, we want to register a path to watch. We create a Path
object for the directory we wish to
watch:
Path watchDir = Paths.get("C:\\home");
The Path
class implements the java.nio.file.Watchable
interface, and that interface defines two register(...)
methods:
public interface Watchable { WatchKey register(WatchService watcher, WatchEvent.Kind<?>[] events, WatchEvent.Modifier... modifiers) throws IOException; WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) throws IOException; }
The WatchKey register(WatchService watchService, WatchEvent.Kind<?>... events)
registers the Path
this method is called on
with the specified WatchService
for the specific events given. Events trigger a notification only if they are specified in the register call.
For the default WatchService
implementation, the java.nio.file.StandardWatchEventKinds
class defines four static implementations
of WatchEvent.Kind
that can be used in the register(...)
calls:
StandardWatchEventKinds.ENTRY_CREATE
indicates that a file or directory has been created within the registered Path
. An
ENTRY_CREATE
event is also triggered when a file is renamed or moved into this directory.
The event count for this event is always 1.
StandardWatchEventKinds.ENTRY_MODIFY
indicates that a file or directory in the registered Path
has been modified.
Exactly which events constitute a modification is somewhat platform-specific, but suffice it to say that actually modifying the contents of
a file always triggers a modify event. On some platforms, changing attributes of files can also trigger this event.
The event count for this event is 1 or greater.
StandardWatchEventKinds.ENTRY_DELETE
indicates that a file or directory has been deleted from the registered Path
. An
ENTRY_DELETE
event is also triggered when a file is renamed or moved out of this directory.
The event count for this event is always 1.
StandardWatchEventKinds.OVERFLOW
indicates that events may have been lost or discarded. You DO NOT have to register
for the OVERFLOW
event to receive it.
The event count may be greater than 1.
For example, let's watch the ENTRY_CREATE
and ENTRY_MODIFY
events:
WatchKey watchKey = watchDir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY);
Our Path
is now registered to be watched, and the WatchService
will work away silently in the background, watching that directory
intently. The same WatchService
instance can watch multiple directories.
The register(...)
method call returns a WatchKey
class instance. This class represents your registration with the WatchService
.
Whether you store this reference or not is up to you, because the WatchService
returns the relevant WatchKey
to you when an event is
triggered. When you are done with a particular WatchKey
and the events it's registered for, you can cancel its registration with the
WatchService
simply by calling its cancel()
method.
Now that our Path
is registered, we can check in with the WatchService
at our convenience to see if any of the events we were interested
in has occurred. WatchService
provides three methods for checking if anything exciting has happened:
WatchKey WatchService.poll()
- returns and removes the next WatchKey
that has had some of its events occur, or null
if no registered events have happened.
WatchKey WatchService.poll(long timeout, TimeUnit unit)
- takes a timeout and time units (java.util.concurrent.TimeUnit
). If an
event occurs during the specified time period, this method exits, returning and removing the relevant WatchKey
. If there are no
WatchKeys
to return by the end of the timeout, this method returns null
.
WatchKey WatchService.take()
- similar to the preceding methods (returns and removes the next WatchKey
), except it will
wait INDEFINITELY until a WatchKey
is available to return.
Once a WatchKey
has been returned by one of these three methods, it WILL NOT be returned by a further poll()
or take()
call
UNTIL you put the key back into a Ready
state by invoking its reset()
method, even if events it is registered for occur.
A WatchKey
has a state. At any given time, its state might be one of the following:
Ready
- indicates that the key is ready to accept events. When first created, a key is in the ready state.
Signalled
- indicates that one or more events are queued. Once the key has been signalled, it is no
longer in the ready state until the reset()
method is invoked.
Invalid
- indicates that the key is no longer active. This state happens when one of the following
events occurs:
The process explicitly cancels the key by using the cancel()
method.
The directory becomes inaccessible.
The watch service is closed.
Once a WatchKey
is returned by the WatchService
, you can inspect and remove all its pending events that have been triggered by calling the
WatchKey
's pollEvents()
method, which returns a List
of WatchEvent
s.
Simple example:
// Create a file inside our watched directory File newFile = new File(watchDir.toFile(), "newFile.txt"); newFile.createNewFile(); // Call take() and see if the event has been registered WatchKey key = watchService.take(); for (WatchEvent<?> event : key.pollEvents()) { System.out.print(String.format("An event was found of kind %s.%n", event.kind())); System.out.print(String.format("The event occurred on file '%s'.%n", event.context())); } // Reset the key - this step is critical if you want to receive further watch events !!! key.reset();
output is:
An event was found of kind ENTRY_CREATE. The event occurred on file 'newFile.txt'.
As you can see, we got the ENTRY_CREATE
event for the newly created newFile.txt
as expected.
The Watch Service API is designed for applications that need to be notified about file change events. It is well suited for any application, like an editor or IDE, that potentially has many open files and needs to ensure that the files are synchronized with the file system.
This API is NOT designed for indexing a hard drive. Most file system implementations have native support for file change notification. The Watch Service API takes advantage of this support where available. However, when a file system does not support this mechanism, the Watch Service will poll the file system, waiting for events.
![]() ![]() ![]() |