If your
application is closely working with files and directories, you may come to a
situation, where you need to monitor a directory (or) file for any changes like
file updates, deletes, new sub directory creations etc.,
Watch Service API
Java NIO
package provides Watch service api to monitor a directory for changes. All you
need to do is register your directory with watch service to monitor changes.
One great flexibility of watch service is, while registering a directory to
watch service, you can specify the events that you are interested in like file
creation, file deletion, or file modification.
Following
step-by-step procedure explains, how to watch a directory using watch service.
Step 1: Get WatchService instance by using
following statement 'FileSystems.getDefault().newWatchService()'.
WatchService
watcher = FileSystems.getDefault().newWatchService();
WatchService
is an interface, it watch all the registered objects for any changes. For
example, you can monitor a directory for all the operations like create,
update, delete and rename operations happened on the directory.
Step 2: Register the directory to the
WatchService instance to monitor.
Path dir =
Paths.get("/Users/harikrishna_gurram");
WatchKey
key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
‘register’
method return 'WatchKey', it represents a token representing the registration
of a Watchable (Here it is a directory) object with a WatchService.
This key
remains valid, until
a. It is cancelled, explicitly, by
invoking its cancel method.
b. Cancelled implicitly, because the
object is no longer accessible,
c. By closing the watch service using WatchService
close method.
A watch
key has a state, at the time of creation key is in ready state. When an event
is detected then the key is signalled and queued so that it can be retrieved by
invoking the watch service's poll() or take methods. Key remains in this state
until its reset method is invoked to return the key to the ready state.
What happen if my key is in signalled
state and some other event (like creation, deletion (or) updation on directory)
on this key?
Events
detected while the key is in the signalled state are queued but do not cause
the key to be re-queued for retrieval from the watch service. Events are
retrieved by invoking the key's {@link #pollEvents pollEvents} method. This
method retrieves and removes all events accumulated for the object. When
initially created, a watch key has no pending events.
When can I call reset method on watch
key?
Make sure ‘reset’
method is only invoked after the events for the object have been processed.
Following
table summarizes the methods of WatchKey interface.
Method
|
Description
|
boolean
isValid()
|
Return
true if the watch key is valid, else false
|
List<WatchEvent<?>>
pollEvents()
|
Retrieves
and removes all pending events for this watch key
|
boolean
reset()
|
Reset
this watch key. If the watch key is in ready state (or) already cancelled,
calling this method has no effect.
|
void cancel()
|
Cancels
the registration with the watch service.
|
Watchable
watchable()
|
Returns
the object for which this watch key was created. This method return the
object, even after calling the cancel method.
|
Step 3: First two steps register the directory
to watcher service. It is time for us to retrieve the signaled events. WatchService
interface provides take method, which retrieves and removes next watch key,
waiting if none are yet present.
WatchKey
key = watcher.take();
Step 4: Once you got the signaled WatchKey,
you can able to get all the events associated with this watch key by calling ‘pollEvents’
method. pollEvents method retrieves and removes all pending events for this
watch key, returning a List of the events that were retrieved.
List<WatchEvent<?>> pollEvents = key.pollEvents();
Following
is the complete working application.
import static java.nio.file.LinkOption.NOFOLLOW_LINKS; 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; 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.List; import java.util.Map; public class WatchDir { private final WatchService watcher; private final Map<WatchKey, Path> keys; private final boolean recursive; private boolean trace = false; @SuppressWarnings("unchecked") static <T> WatchEvent<T> cast(WatchEvent<?> event) { return (WatchEvent<T>) event; } /** * Register the given directory with the WatchService */ private void registerDirectory(Path dir) throws IOException { WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); if (trace) { Path prev = keys.get(key); if (prev == null) { System.out.format("register: %s\n", dir); } else { if (!dir.equals(prev)) { System.out.format("update: %s -> %s\n", prev, dir); } } } keys.put(key, dir); } /** * Register the given directory, and all its sub-directories, with the * WatchService. */ private void registerDirectoryTree(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; } }); } /** * Creates a WatchService and registers the given directory */ WatchDir(Path dir, boolean recursive) throws IOException { this.watcher = FileSystems.getDefault().newWatchService(); this.keys = new HashMap<WatchKey, Path>(); this.recursive = recursive; if (recursive) { registerDirectoryTree(dir); } else { registerDirectory(dir); } // enable trace after initial registration this.trace = true; } /** * Process all events for keys queued to the watcher */ void startService() { for (;;) { // wait for key to be signalled WatchKey key; try { key = watcher.take(); } catch (InterruptedException x) { return; } Path dir = keys.get(key); if (dir == null) { System.err.println("WatchKey not recognized!!"); continue; } List<WatchEvent<?>> pollEvents = key.pollEvents(); for (WatchEvent<?> event : pollEvents) { WatchEvent.Kind kind = event.kind(); // TBD - provide example of how OVERFLOW event is handled if (kind == OVERFLOW) { continue; } // Context for directory entry event is the file name of entry WatchEvent<Path> ev = cast(event); Path name = ev.context(); Path child = dir.resolve(name); System.out.println("event.kind().name() = " + event.kind().name()); System.out.println("child : " + child); // if directory is created, and watching recursively, then // register it and its sub-directories if (recursive && (kind == ENTRY_CREATE)) { try { if (Files.isDirectory(child, NOFOLLOW_LINKS)) { registerDirectoryTree(child); } } catch (IOException x) { // ignore to keep sample readbale } } else if (kind == ENTRY_CREATE) { // Created Event handling } else if (kind == ENTRY_DELETE) { // Write code for entry delete } else if (kind == ENTRY_MODIFY) { // Write code for entry modify } else if (kind == OVERFLOW) { // Write code for overflow condition } else { // Do nothing } } // 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; } } } } static void usage() { System.err.println("usage: java WatchDir [-r] dir"); System.exit(-1); } public static void main(String[] args) throws IOException { // parse arguments if (args.length == 0 || args.length > 2) { usage(); } boolean recursive = false; int dirArg = 0; if (args[0].equals("-r")) { if (args.length < 2) { usage(); } recursive = true; dirArg++; } // register directory and process its events Path dir = Paths.get(args[dirArg]); new WatchDir(dir, recursive).startService(); } }
Sample Output
C:>java
WatchDir -r C:\\Users\\Documents\\testing
event.kind().name()
= ENTRY_DELETE
child :
C:\Users\Documents\testing\1\2\a.txt
event.kind().name()
= ENTRY_MODIFY
child :
C:\Users\Documents\testing\1\2
No comments:
Post a Comment