1. Обзор
В этой статье мы собираемся изучить интерфейс WatchService
API- интерфейсов файловой системы Java NIO.2. Это одна из менее известных функций новых API-интерфейсов ввода-вывода, представленных в Java 7 вместе с интерфейсом FileVisitor
.
Чтобы использовать интерфейс WatchService
в своих приложениях, вам необходимо импортировать соответствующие классы:
import java.nio.file.*;
2. Зачем использовать WatchService
Распространенным примером для понимания того, что делает служба, является среда IDE.
Вы могли заметить, что IDE всегда обнаруживает изменения в файлах исходного кода , происходящие вне ее. Некоторые IDE информируют вас с помощью диалогового окна, чтобы вы могли выбрать, перезагружать файл из файловой системы или нет, другие просто обновляют файл в фоновом режиме.
Точно так же более новые фреймворки, такие как Play, также по умолчанию выполняют горячую перезагрузку кода приложения — всякий раз, когда вы выполняете редактирование из любого редактора.
Эти приложения используют функцию, называемую уведомлением об изменении файла
, которая доступна во всех файловых системах.
По сути, мы можем написать код для опроса файловой системы на наличие изменений в определенных файлах и каталогах . Однако это решение не масштабируется, особенно если количество файлов и каталогов достигает сотен и тысяч.
В Java 7 NIO.2 API WatchService
предоставляет масштабируемое решение для мониторинга каталогов на наличие изменений. Он имеет чистый API и настолько хорошо оптимизирован для производительности, что нам не нужно реализовывать собственное решение.
3. Как работает Watchservice?
Чтобы использовать функции WatchService
, первым шагом является создание экземпляра WatchService
с использованием класса java.nio.file.FileSystems :
WatchService watchService = FileSystems.getDefault().newWatchService();
Далее нам нужно создать путь к каталогу, который мы хотим отслеживать:
Path path = Paths.get("pathToDir");
После этого шага мы должны зарегистрировать путь в сервисе часов. На этом этапе необходимо понять две важные концепции. Класс StandardWatchEventKinds
и класс WatchKey
. Взгляните на следующий регистрационный код, чтобы понять, где каждое падение. Мы будем следовать этому с объяснениями того же самого:
WatchKey watchKey = path.register(
watchService, StandardWatchEventKinds...);
Обратите внимание только на две важные вещи: во-первых, вызов API регистрации пути принимает экземпляр службы наблюдения в качестве первого параметра, за которым следуют переменные аргументы StandardWatchEventKinds
. Во-вторых, возвращаемый тип процесса регистрации — экземпляр WatchKey
.
3.1. Виды StandardWatchEvent
Это класс, экземпляры которого сообщают службе наблюдения, за какими событиями следует следить в зарегистрированном каталоге. В настоящее время есть четыре возможных события, за которыми стоит следить:
StandardWatchEventKinds.ENTRY_CREATE
— срабатывает при создании новой записи в отслеживаемом каталоге. Это может быть связано с созданием нового файла или переименованием существующего файла.StandardWatchEventKinds.ENTRY_MODIFY
— срабатывает при изменении существующей записи в отслеживаемом каталоге. Все изменения файлов вызывают это событие. На некоторых платформах это вызывает даже изменение атрибутов файла.StandardWatchEventKinds.ENTRY_DELETE
— срабатывает, когда запись удаляется, перемещается или переименовывается в отслеживаемом каталоге.StandardWatchEventKinds.OVERFLOW
— срабатывает для индикации потерянных или отброшенных событий. Мы не будем уделять этому много внимания
3.2. WatchKey
_ ``
Этот класс представляет регистрацию каталога в службе наблюдения. Его экземпляр возвращается нам службой наблюдения, когда мы регистрируем каталог и когда мы спрашиваем службу наблюдения, произошли ли какие-либо события, для которых мы зарегистрировались.
Сервис Watch не предлагает нам методов обратного вызова, которые вызываются всякий раз, когда происходит событие. Мы можем только опросить его несколькими способами, чтобы найти эту информацию.
Мы можем использовать API опроса :
WatchKey watchKey = watchService.poll();
Этот вызов API сразу возвращается. Он возвращает следующий ключ наблюдения в очереди, любое из событий которого произошло, или null, если зарегистрированных событий не произошло.
Мы также можем использовать перегруженную версию, которая принимает аргумент тайм -аута:
WatchKey watchKey = watchService.poll(long timeout, TimeUnit units);
Этот вызов API похож на предыдущий по возвращаемому значению. Однако он блокирует единицы времени тайм
-аута , чтобы дать больше времени, в течение которого может произойти событие, вместо того, чтобы сразу возвращать значение null.
Наконец, мы можем использовать API взятия :
WatchKey watchKey = watchService.take();
Этот последний подход просто блокируется до тех пор, пока не произойдет событие.
Мы должны отметить здесь нечто очень важное: когда экземпляр WatchKey
возвращается API опроса
или взятия
, он не будет захватывать больше событий, если его API сброса не вызывается:
watchKey.reset();
Это означает, что экземпляр ключа наблюдения удаляется из очереди службы наблюдения каждый раз, когда он возвращается операцией опроса. Вызов API сброса
возвращает его в очередь для ожидания дополнительных событий.
Для наиболее практического применения службы наблюдателя потребуется цикл, в котором мы постоянно проверяем изменения в отслеживаемом каталоге и обрабатываем их соответствующим образом. Для реализации этого мы можем использовать следующую идиому:
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
//process
}
key.reset();
}
Мы создаем ключ наблюдения для хранения возвращаемого значения операции опроса. Цикл while будет блокироваться до тех пор, пока условный оператор не вернется либо с ключом наблюдения, либо с нулевым значением.
Когда мы получаем ключ watch, цикл while выполняет код внутри него. Мы используем API WatchKey.pollEvents
для возврата списка произошедших событий. Затем мы используем цикл for each
для обработки их один за другим.
После обработки всех событий мы должны вызвать API сброса
, чтобы снова поставить ключ наблюдения в очередь.
4. Пример просмотра каталога
Поскольку в предыдущем подразделе мы рассмотрели API WatchService
и то, как он работает внутри, а также то, как мы можем его использовать, теперь мы можем перейти к рассмотрению полного практического примера.
Из соображений переносимости мы будем следить за активностью в домашнем каталоге пользователя, который должен быть доступен во всех современных операционных системах.
Код содержит всего несколько строк кода, поэтому мы просто оставим его в основном методе:
public class DirectoryWatcherExample {
public static void main(String[] args) {
WatchService watchService
= FileSystems.getDefault().newWatchService();
Path path = Paths.get(System.getProperty("user.home"));
path.register(
watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
System.out.println(
"Event kind:" + event.kind()
+ ". File affected: " + event.context() + ".");
}
key.reset();
}
}
}
И это все, что нам действительно нужно сделать. Теперь вы можете запустить класс, чтобы начать просмотр каталога.
Когда вы переходите в домашний каталог пользователя и выполняете какие-либо действия с файлами, такие как создание файла или каталога, изменение содержимого файла или даже удаление файла, все это будет регистрироваться на консоли.
Например, предположим, что вы переходите на домашнюю страницу пользователя, щелкните правой кнопкой мыши в пространстве, выберите « новый -> файл»
, чтобы создать новый файл, а затем назовите его testFile
. Затем вы добавляете контент и сохраняете. Вывод в консоли будет выглядеть так:
Event kind:ENTRY_CREATE. File affected: New Text Document.txt.
Event kind:ENTRY_DELETE. File affected: New Text Document.txt.
Event kind:ENTRY_CREATE. File affected: testFile.txt.
Event kind:ENTRY_MODIFY. File affected: testFile.txt.
Event kind:ENTRY_MODIFY. File affected: testFile.txt.
Не стесняйтесь редактировать путь, чтобы он указывал на любой каталог, который вы хотите посмотреть.
5. Вывод
В этой статье мы рассмотрели некоторые из менее часто используемых функций, доступных в Java 7 NIO.2 — API-интерфейсы файловой системы, в частности интерфейс WatchService
.
Нам также удалось пройти этапы создания приложения для просмотра каталогов, чтобы продемонстрировать функциональность.
И, как всегда, полный исходный код примеров, использованных в этой статье, доступен в проекте Github .