简体   繁体   中英

WatchService Directory path incorrect after renaming Directory

The code registers any new directory created with the watch service to listen for all the events and runs for a duration of 60 seconds[which can be changed with running time]

final class Test 
{
 public static void main(String[] args)throws Exception
 {
  //Run the program for 60 seconds i.e * 1000
  long RUNNING_TIME=60*1000;
  
  try(WatchService service=FileSystems.getDefault().newWatchService())
  {
   //Register test directory for observation
   Path.of("E:","Folder A").register(service,StandardWatchEventKinds.ENTRY_CREATE,StandardWatchEventKinds.ENTRY_MODIFY,StandardWatchEventKinds.ENTRY_DELETE);
   
   long start=System.currentTimeMillis();
   while(System.currentTimeMillis()-start<RUNNING_TIME)
   {
    //Use poll not take else while loop will be blocked
    WatchKey key=service.poll();
    
    if(key==null){continue;}
    for(WatchEvent event:key.pollEvents())
    {
    //Get the absolute path from the event context by resolving it against it's parent directory
     Path
     eventPath=(Path)event.context(),
     absolutePath=((Path)key.watchable()).resolve(eventPath); //Wrong path after resolving against watchable after renaming directory
     Kind kind=event.kind();
     
     //If the event kind is CREATE and an new directory is created then register that directory with the service
     if(kind==StandardWatchEventKinds.ENTRY_CREATE && Files.isDirectory(absolutePath))
     {
      absolutePath.register
      (
       service,
       StandardWatchEventKinds.ENTRY_CREATE,
       StandardWatchEventKinds.ENTRY_MODIFY,
       StandardWatchEventKinds.ENTRY_DELETE
      );
     }
     
     //Debugging
     System.out.println("Event Path="+eventPath);
     System.out.println("Absolute Path="+absolutePath);
     System.out.println("Kind="+kind);
     System.out.println("==========================");
    }
   
    key.reset();
   }  
  }
 }
}

Here are the observed results

Sno                            Test                                  Output

1       Create Directory=>E:/Folder A/New Folder               Event Path=New folder
                                                          Absolute Path=E:\Folder A\New folder
                                                                 Kind=ENTRY_CREATE
                                                             ==========================



2        Rename E:/Folder A/New Folder                         Event Path=New folder
                       TO                                 Absolute Path=E:\Folder A\New folder
                     E:/Folder A/Test                           Kind=ENTRY_DELETE
                                                            ==========================
                                                               Event Path=Test
                                                         Absolute Path=E:\Folder A\Test
                                                               Kind=ENTRY_CREATE



3        Create Text File Under Test Folder i.e            Event Path=New Text Document.txt
     Create => E:/Folder A/Test/New Text Document.txt     Absolute Path=E:\Folder A\New folder\New Text Document.txt
                                                             Kind=ENTRY_CREATE
                                                            ==========================
                                                             Event Path=Test
                                                          Absolute Path=E:\Folder A\Test
                                                             Kind=ENTRY_MODIFY




4    Rename  E:/Folder A/Test/New Text Document.txt        Event Path=New Text Document.txt
                   To                                 Absolute Path=E:\Folder A\New folder\New Text Document.txt
            E:/Folder A/Test/Test.txt                      Kind=ENTRY_DELETE
                                                        ==========================
                                                          Event Path=Test.txt
                                                      Absolute Path=E:\Folder A\New folder\Test.txt
                                                           Kind=ENTRY_CREATE                                                          
                                                        ==========================
                                                            Event Path=Test
                                                      Absolute Path=E:\Folder A\Test
                                                          Kind=ENTRY_MODIFY
                                                        

Incorrect results appear from Test case 3 in the Absolute path Here are the incorrect absolute path values from test case 3 onwards

Absolute Path=E:\Folder A\New folder\New Text Document.txt
Absolute Path=E:\Folder A\New folder\Test.txt

As you can see In test case 2 i have renamed my Folder from New Folder To Test so why is it still appearing while resolving against the watchable directory.

Clearly the error lies in the Path returned from watchable() or maybe i should cancel this key during renaming or some other situation to get correct results.

Why is watchable() method not returning the correct path after renaming the directory?

You can avoid this problem if you unregister the previous watch setup for the renamed directory. When renaming, you get a DELETE then CREATE for the parent's folder watch, so you can amend the watched directories to cancel the old key, and register a new key.

Here is your main with small adjustments for logging the keys in use and handling the delete + create using a HashMap to keep track of directories added to the watch service:

var keys = new HashMap<Path,WatchKey>();

try (WatchService service = FileSystems.getDefault().newWatchService()) {
    // Register test directory for observation
    Path root = Path.of("C:/Temp/Folder A");

    WatchKey wk = root.register(service, StandardWatchEventKinds.ENTRY_CREATE,
            StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
    System.out.println("ROOT register=" + root +" wk="+wk);

    long start = System.currentTimeMillis();
    while (System.currentTimeMillis() - start < RUNNING_TIME) {
        // Use poll not take else while loop will be blocked
        WatchKey key = service.poll();

        if (key == null) {
            continue;
        }
        for (var event : key.pollEvents())  {
            // Get the absolute path from the event context by resolving it against it's
            // parent directory
            Path eventPath = (Path) event.context(), absolutePath = ((Path) key.watchable()).resolve(eventPath);

            var kind = event.kind();

            // Debugging
            System.out.println("==========================");
            System.out.println("Watch Key=" + key);
            System.out.println("Event Path=" + eventPath);
            System.out.println("Absolute Path=" + absolutePath);
            System.out.println("Kind=" + kind);

            // If the event kind is CREATE and an new directory is created then register
            // that directory with the service
            if (kind == StandardWatchEventKinds.ENTRY_CREATE && Files.isDirectory(absolutePath)) {
                var subkey = absolutePath.register(service, StandardWatchEventKinds.ENTRY_CREATE,
                        StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
                keys.put(absolutePath, subkey);
                System.out.println("register=" + absolutePath +" wk="+subkey);
            }
            // Deletes: wipe previously registered watch
            else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
                var prev = keys.remove(absolutePath);
                if (prev != null) {
                    System.out.println("unregister=" + absolutePath + " key="+prev);
                    prev.cancel();
                }
            }
        }

        key.reset();
    }
}

With above, starting then creating "subfolder" will report three different watch keys:

ROOT register=C:\Temp\Folder A wk=... $WindowsWatchKey@aaaaaaaaa

Watch Key=... $WindowsWatchKey@aaaaaaaaa
Kind=ENTRY_CREATE
register=C:\Temp\Folder A\subfolder wk=... $WindowsWatchKey@xxxxxxxx

Renaming "subfolder" to "LATEST" says:

Watch Key=... $WindowsWatchKey@aaaaaaaaa
Kind=ENTRY_DELETE
unregister=C:\Temp\Folder A\subfolder key=... $WindowsWatchKey@xxxxxxxx

Watch Key=... $WindowsWatchKey@aaaaaaaaa
Kind=ENTRY_CREATE
register=C:\Temp\Folder A\LATEST wk=... $WindowsWatchKey@yyyyyyyyy

Then editing a file under the new directory name will report with the current folder name, and most importantly, with the new watch key after rename not the @xxxxxxxx value it was printing before:

Watch Key=sun.nio.fs.WindowsWatchService$WindowsWatchKey@yyyyyyyyy
Event Path=file.txt
Absolute Path=C:\Temp\Folder A\LATEST\file.txt
Kind=ENTRY_MODIFY

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM