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