Из всех новых функций Java 7 одной из наиболее интересных является WatchService, добавляющая возможность отслеживать каталог на предмет изменений. WatchService отображается непосредственно на собственный механизм уведомления о событиях файла, если таковой имеется. Если собственный механизм уведомления о событиях недоступен, тогда реализация по умолчанию будет использовать опрос. В результате, отзывчивость, упорядоченность событий и доступные детали зависят от реализации. (ПРИМЕЧАНИЕ: есть сопутствующий пост об использовании Guava EventBus для обработки событий WatchService )
Просмотр каталога
Интерфейс Path реализует метод register, который принимает объект WatchService и varargs типа WatchEvent.Kind в качестве аргументов. Есть 4 события для просмотра:
- ENTRY_CREATE
- ENTRY_DELETE
- ENTRY_MODIFY
- OVERFLOW
Хотя первые 3 типа говорят сами за себя, OVERFLOW указывает, что события могут быть потеряны или отброшены. WatchService создается путем вызова FileSystem.newWatchService (). Просмотр каталога осуществляется путем регистрации объекта Path с помощью WatchService:
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 остается действительным, пока не произойдет одно из следующих действий:
- WatchKey.cancel () вызывается.
- Просматриваемый каталог больше не доступен.
- Объект WatchService закрыт.
Проверка на наличие изменений
Когда обнаруживается изменение, состояние WatchKey устанавливается на «сигнализированный» и помещается в очередь для обработки. Удаление WatchKeys из очереди включает вызов WatchService.poll () или WatchService.take (). Вот основной пример:
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 отменяется, пока он находится в очереди, он будет повторно обрабатываться в очереди до получения. Отмена также может произойти автоматически, если каталог был удален или больше не доступен.
Обработка событий
Теперь, когда мы обнаружили событие, как мы определяем:
- В каком каталоге произошло событие? (при условии, что зарегистрировано более одного каталога)
- Каким было настоящее событие? (при условии прослушивания более одного события)
- Какова была цель события, т.е. какой объект Path был создан, удален или обновлен?
Переходя к строке 6 в предыдущем примере, мы проанализируем необходимую информацию из WatchKey и WatchEvent:
//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:
- WatchService не принимает события для подкаталогов просматриваемого каталога.
- Нам все еще нужно опросить WatchService на наличие событий, а не получать асинхронное уведомление.
Для решения вышеупомянутых проблем есть последующее сообщение, использующее Guava EventBus для обработки событий WatchService . Спасибо за ваше время и увидимся в следующем посте.
Ресурсы
- Пакет java.nio.file , который содержит объекты WatchService, WatchKey и WatchEvent, обсуждаемые здесь.
- Модульный тест демонстрирует WatchService