I have a simple Java utility class that observes the directory that I've registered. My objective: whenever a file changes in that directory, I want to capture that event and process that file...
The code I have registers a single directory for only ENTRY_MODIFY events - this is all running on my local Windows 7 machine with Java runtime 1.8.0_510-b16.
I launch the program, it starts up the monitoring process. I then change a file in that directory, it recognizes the change but for some reason it produces not one but two (2) notification events - both for the same file, both of the same ENTRY_MODIFY type, why does it do that?
For convenience, here is my complete code:
package com.xxx.yyy.zzz.lcm;
//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 java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
//import java.util.Arrays;
/**
* This program uses Java 7 java.nio.* package to Watch for CHANGEs on a set of files that you specify at
* startup using command line arguments
*
* Whenever a registered file is changed, the code will Copy that file to a target directory
* and give it a new name using the following pattern:
* Copy sourcefilepath/eventfilename to targetfilepath/yyyymmdd:HHmmssSSS_eventfilename
*/
public class FileWatcher {
public static void main(String[] args) {
// Make sure argument list is not empty
if (args.length == 0){
System.out.println("You must specify proper arguments for this utility to run");
useage();
}
/*
* Prolog - get the arguments specified
* First argument is the targetDir
* Arguments 2nd, 3rd, etc fieles to be monitored
*/
String targetDirArg = args[0];
String[] filesForMonitoring = new String[args.length - 1]; //
// Strip off the first entry from the arguments (that's target)
for(int i=1; i < args.length; i++) {
filesForMonitoring[i-1] = args[i];
}
// Simple Edits:
if (targetDirArg == null) {
System.out.println("Target directory you specified ('" + targetDirArg + "') + was not valid.");
useage();
}
if (filesForMonitoring.length < 1) {
System.out.println("You must specify at least one (or more) files to monitor using command line arguments\n");
useage();
}
// Printout the runtime specs
System.out.println("*** Runtime Parms ***");
System.out.println("Target directory:");
System.out.println("\t" + targetDirArg);
System.out.println("Filepaths to be watched:");
for (int i=0; i < filesForMonitoring.length; i++) {
System.out.println("\t" + filesForMonitoring[i]);
}
// Now create the WatcherServices and watch the specified assets
/*
* TO-DO Test it thoroughly and figure out why it sometimes produces 2 or 3 events for a single file change
* I have seen this numerous times but have not been able to reproduce consistently, but I've observed that
* when monitoring a single file, if I make a change to that file, sometimes that change will produce 2 or
* even 3 separate events, which is unusual and we need to find out why it's happening.
*/
try {
// Create the WatchService object
WatchService watcher = FileSystems.getDefault().newWatchService();
// Register the directory path that you want to monitor
// TO-DO: Modify this logic to register multiple files (if more than one specified) for monitoring
Path sourceDir = Paths.get(filesForMonitoring[0]);
sourceDir.register(watcher, ENTRY_MODIFY);
System.out.println("\nWatchService watching for file changes in dir: " + sourceDir.getParent() + "\\" + sourceDir.getFileName());
/*
* This infinite loop that monitors the specified directory
* Each directory that is registered will produce a watch key if changed
*/
while (true) {
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException ex) {
return;
}
/*
* For each directory modified, find out the Events
* (there may be multiple events) and the file name,
* then process accordingly
* TO DO: Find out WHY there are sometimes multiple events
* for a single filename.... this is problematic
*/
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
// Get the Event kind and the filename for that Event
@SuppressWarnings("unchecked")
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path eventFileName = ev.context();
System.out.println("\n" + kind.name() + ": " + eventFileName);
// This is how we can check for a change to the specific file
if (kind == ENTRY_MODIFY &&
eventFileName.toString().endsWith(".properties")) {
System.out.println("Content of property file '" + eventFileName.toString() + "' has changed!!!");
try {
processFile(Paths.get("C:sourcefile"), Paths.get("C:targetfile"));
} catch (IOException iox) {
iox.printStackTrace();
}
}
}
/*
* The reset() method is very important to ensure flow of events
* The only time it may be invalid is if the monitored directory got deleted
*/
boolean valid = key.reset();
if (!valid) {
break;
}
}
} catch (IOException ex) {
System.err.println(ex);
}
}
private static void processFile(Path source, Path target) throws IOException {
System.out.println("Processing file change. Source='" + source.getFileName() + "', Target=" + target.getFileName() + "'");
}
public static void useage() {
System.out.println("Useage: prompt>java FileWatcher c:/targetdir c:/sourcepath1/sourcefile1.txt c:/sourcepath1/sourcefile2.txt");
System.out.println("\t The above command would monitor sourcefile1.txt and sourcefile2.txt for any changes");
System.out.println("\t If any change to one of those files, the code would copy the conents of the changed file(s) from");
System.out.println("\t sourcepath1 to the target path specified in args[0]. The target file would also be given a new name with timestamp");
System.exit(1);
}
}