Статьи

Java 7: WatchService

Из всех новых функций Java 7 одной из наиболее интересных является WatchService, добавляющая возможность отслеживать каталог на предмет изменений. WatchService отображается непосредственно на собственный механизм уведомления о событиях файла, если таковой имеется.

Если собственный механизм уведомления о событиях недоступен, тогда реализация по умолчанию будет использовать опрос. В результате, отзывчивость, упорядоченность событий и доступные детали зависят от реализации. (ПРИМЕЧАНИЕ: есть сопутствующий пост об использовании Guava EventBus для обработки событий WatchService )

Просмотр каталога

Интерфейс Path реализует метод register, который принимает объект WatchService и varargs типа WatchEvent.Kind в качестве аргументов. Есть 4 события для просмотра:

  1. ENTRY_CREATE
  2. ENTRY_DELETE
  3. ENTRY_MODIFY
  4. OVERFLOW

Хотя первые 3 типа говорят сами за себя, OVERFLOW указывает, что события могут быть потеряны или отброшены. WatchService создается путем вызова FileSystem.newWatchService (). Просмотр каталога осуществляется путем регистрации объекта Path с помощью WatchService:

1
2
3
4
import static java.nio.file.StandardWatchEventKinds.*;
Path path = Paths.get("/home");
WatchService watchService = FileSystems.getDefault().newWatchService();
WatchKey watchKey = path.register(watchService,ENTRY_CREATE,ENTRY_DELETE,ENTRY_MODIFY);

Как видно из примера, метод register возвращает объект WatchKey. WatchKey — это токен, который представляет регистрацию Пути с помощью WatchService.

WatchKey

В результате процесса регистрации WatchKey находится в состоянии «готово» и считается действительным. WatchKey остается действительным, пока не произойдет одно из следующих действий:

  1. WatchKey.cancel () вызывается.
  2. Просматриваемый каталог больше не доступен.
  3. Объект WatchService закрыт.

Проверка на наличие изменений

Когда обнаруживается изменение, состояние WatchKey устанавливается на «сигнализированный» и помещается в очередь для обработки. Удаление WatchKeys из очереди включает вызов WatchService.poll () или WatchService.take (). Вот основной пример:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
private boolean notDone = true;
while(notDone){
    try{
         WatchKey watchKey = watchService.poll(60,TimeUnit.SECONDS);
         List<WatchEvent.Kind<?>> events = watchKey.pollEvents();
         for(WatchEvent event : events){
            ...process the events
         }
         if(!watchKey.reset()){
            ...handle situation no longer valid
         }
     }catch(InterruptedException e){
            Thread.currentThread().interrupt();
     }

В строке 5 мы вызываем метод pollEvents, чтобы получить все события для этого объекта WatchKey. В строке 9 вы заметите вызов метода сброса. Метод reset устанавливает состояние WatchKey обратно на «ready» и возвращает логическое значение, указывающее, является ли WatchKey по-прежнему действительным. Если есть какие-либо ожидающие события, то WatchKey будет немедленно помещен в очередь, в противном случае он останется в состоянии готовности, пока не будут обнаружены новые события. Вызов сброса для WatchKey, который был отменен или находится в состоянии готовности, не будет иметь никакого эффекта. Если WatchKey отменяется, пока он находится в очереди, он будет повторно обрабатываться в очереди до получения. Отмена также может произойти автоматически, если каталог был удален или больше не доступен.

Обработка событий

Теперь, когда мы обнаружили событие, как мы определяем:

  1. В каком каталоге произошло событие? (при условии, что зарегистрировано более одного каталога)
  2. Каким было настоящее событие? (при условии прослушивания более одного события)
  3. Какова была цель события, т.е. какой объект Path был создан, удален или обновлен?

Переходя к строке 6 в предыдущем примере, мы проанализируем необходимую информацию из WatchKey и WatchEvent:

1
2
3
4
5
6
//WatchKey watchable returns the calling Path object of Path.register
 Path watchedPath = (Path) watchKey.watchable();
 //returns the event type
 StandardWatchEventKinds eventKind = event.kind();
 //returns the context of the event
 Path target = (Path)event.context();

В строке 6 мы видим, как вызывается метод WatchEvent.context. Метод контекста вернет объект Path, если событие было созданием, удалением или обновлением, и будет относиться к наблюдаемому каталогу. Важно знать, что при получении события нет гарантии, что программа (ы), выполняющая операцию, завершилась, поэтому может потребоваться некоторый уровень координации.

Вывод

WatchService — это очень интересная особенность нового пакета java.nio.file в Java 7. При этом необходимо помнить о двух моментах, связанных с WatchService:

  1. WatchService не принимает события для подкаталогов просматриваемого каталога.
  2. Нам все еще нужно опросить WatchService на наличие событий, а не получать асинхронное уведомление.

Для решения вышеупомянутых проблем есть последующее сообщение, использующее Guava EventBus для обработки событий WatchService . Спасибо за ваше время и увидимся в следующем посте.


Ресурсы

  1. Пакет java.nio.file , который содержит объекты WatchService, WatchKey и WatchEvent, обсуждаемые здесь.
  2. Модульный тест, демонстрирующий WatchService

Ссылка: Что нового в Java 7: WatchService от нашего партнера JCG