Java WatchService блокирует каталог в Windows

Кажется, когда вы просматриваете каталог с помощью WatchService Java, а затем пытаетесь переименовать его родительский каталог, переименование завершится с ошибкой 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:\Users\Markus\AppData\Local\Temp\junit14649009910061913524\dir1 -> C:\Users\Markus\AppData\Local\Temp\junit14649009910061913524\dir1_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)

Протестировано с OpenJDK 11 и 14 в Windows 10. Попытка переименовать dir1 в проводнике Windows также не удалась. Работает, как и ожидалось, в Linux.

Также обратите внимание, что когда наблюдатель зарегистрирован не в каталоге dir1/dir2, а только в каталоге dir1, он работает.

Это ошибка в OpenJDK? Рассмотрение некоторых старых проблем (https://bugs.openjdk.java.net/browse/JDK-8153925) кажется, что блокировки каталогов происходить не должно.


person Markus Kramer    schedule 11.09.2020    source источник


Ответы (2)


Нет, это не ошибка. Это конструктивная особенность Windows, которая называется принудительная блокировка. Невозможно отключить его.

Linux использует рекомендательную блокировку, что означает, что он не будет блокировать переименование или даже удаление каталога.

person jurez    schedule 11.09.2020
comment
Если в Windows, как правило, невозможно просматривать структуру каталогов без блокировки (и, следовательно, блокировки переименований), то как приложения для синхронизации файлов, такие как Dropbox и т. д., могут обнаруживать изменения, не вызывая таких эффектов? - person Markus Kramer; 12.09.2020
comment
Хороший вопрос, возможно, они не используют постоянный дескриптор, как WatchService, а вместо этого периодически сканируют каталог. Или, возможно, они регистрируют WatcherService в каталоге верхнего уровня, который не меняется. Возможно, есть и другой способ - если кто-то знает, пожалуйста, оставьте комментарий. - person jurez; 12.09.2020
comment
Я нашел обходной путь, см. мой ответ. Спасибо за ваш вклад! - person Markus Kramer; 23.09.2020

Ответ @jurez правильный, в Windows WatchService блокирует каталоги. Это известная проблема OpenJDK, которую, по-видимому, нельзя исправить.

Но есть отличный обходной путь. В Windows можно наблюдать за всей структурой каталогов без необходимости вручную регистрировать наблюдатели для каждого подкаталога, как в Linux. См. пример кода< /а>.

Это должно решить эту проблему во многих ситуациях. В моем случае я разрабатываю инструмент синхронизации файлов (datalisk), и с помощью этого обходного пути мне нужно только смотреть/блокировать каталог верхнего уровня, что не должно вызывать проблем у пользователей.

person Markus Kramer    schedule 23.09.2020