当您使用Java的WatchService观察一个目录,然后尝试重命名其父目录时,重命名将失败,并出现AccessDeniedException。目录似乎被WatchService锁定了。
可以用复制
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.*;
public class WatcherTest {
@Test
public void moveWatchedDir(@TempDir Path tempDir) throws Exception {
Files.createDirectories(tempDir.resolve("dir1/dir2"));
var watchService = FileSystems.getDefault().newWatchService();
tempDir.resolve("dir1/dir2").register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
Files.move(tempDir.resolve("dir1"), tempDir.resolve("dir1_b"));
}
}
失败:
java.nio.file.AccessDeniedException: C:UsersMarkusAppDataLocalTempjunit14649009910061913524dir1 -> C:UsersMarkusAppDataLocalTempjunit14649009910061913524dir1_b
at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:89)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
at java.base/sun.nio.fs.WindowsFileCopy.move(WindowsFileCopy.java:395)
at java.base/sun.nio.fs.WindowsFileSystemProvider.move(WindowsFileSystemProvider.java:292)
at java.base/java.nio.file.Files.move(Files.java:1426)
at WatcherTest.moveWatchedDir(WatcherTest.java:13)
用OpenJDK11&Windows 10上的14。尝试在Windows资源管理器中重命名dir1也失败。在Linux上按预期工作。
还要注意,当观察程序没有在dir1/dir2上注册,而只在dir1上注册时,它就可以工作了。
这是OpenJDK
中的错误吗?查看一些旧问题(https://bugs.openjdk.java.net/browse/JDK-8153925)似乎不应该锁定目录。
Linux使用咨询锁定,这意味着它不会阻止重命名甚至删除目录。
@jurez答案正确,在Windows上,WatchService将锁定目录。这是一个已知的OpenJDK问题,显然无法修复。
但有一个很好的变通办法。在Windows上,可以监视整个目录结构,而不必像在Linux上那样为每个子目录手动注册观察程序。
这应该在许多情况下解决这个问题。有了这个解决方法,我只需要监视/锁定顶级目录,这不会给用户带来问题。