0

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);
    }
}
schweerelos
  • 2,189
  • 2
  • 17
  • 25
JamesD
  • 679
  • 10
  • 36
  • I did find a thread that discussed a similar problem, but that user later admitted his own user error was at fault. This is a different situation. Please post the link you think it's duplicate, to make sure. – JamesD Oct 15 '15 at 22:38
  • I followed the thread listed on top and I got my answer. I ended up adding the Thread.sleep() block of code to make it all work for me on Windows: Thread.sleep(5000); // wait 5 seconds – JamesD Oct 19 '15 at 13:55

0 Answers0