Статьи

Google Guava EventBus и Java 7 WatchService для программирования событий

Этот пост будет посвящен использованию EventBus Guava для публикации изменений в каталоге или подкаталогах, обнаруженных Java 7 WatchService. Guava EventBus — отличный способ добавить сообщение публикации / подписки в приложение. WatchService, новый в пакете Java 7 java.nio.file, используется для отслеживания изменений в каталоге. Поскольку EventBus и WatchService были рассмотрены в предыдущих статьях, мы не будем подробно останавливаться на этих темах. Для получения дополнительной информации читателю предлагается просмотреть сообщения EventBus и WatchService . [ПРИМЕЧАНИЕ: сообщение обновлено 28.02.2012 для ясности.]

Зачем использовать EventBus

Существует две основные причины использования EventBus с WatchService.

  1. Мы не хотим опросить события, а хотели бы получать асинхронное уведомление.
  2. После обработки событий необходимо вызвать метод WatchKey.reset, чтобы включить любые новые изменения в очередь. Хотя объект WatchKey является потокобезопасным, важно, чтобы метод сброса вызывался только после того, как все потоки закончили обрабатывать события, что приводит к некоторой сложности координации. Использование одного потока для обработки событий, вызов метода сброса, а затем публикация изменений через EventBus устраняет эту проблему.

Наш план для достижения этой цели прост и будет включать следующие шаги:

  1. Создание экземпляра WatchService.
  2. Зарегистрируйте каждый каталог рекурсивно, начиная с заданного объекта Path.
  3. Удалите события из очереди WatchService, затем обработайте и опубликуйте эти события.
  4. Запустите отдельный поток для удаления событий из очереди и публикации.

Следующие примеры кода являются наиболее важными моментами из класса DirectoryEventWatcherImpl, который будет выполнять всю эту работу.

Регистрация каталогов с помощью WatchService

Хотя добавление или удаление подкаталога будет генерировать событие, любые изменения внутри подкаталога просматриваемого каталога не будут. Мы собираемся компенсировать это путем рекурсивного прохождения всех подкаталогов (с помощью метода Files.walkFileTree) и регистрации каждого с помощью объекта WatchService (ранее определенного в примере здесь):

01
02
03
04
05
06
07
08
09
10
11
private void registerDirectories() throws IOException {
        Files.walkFileTree(startPath, new WatchServiceRegisteringVisitor());
}
 
private class WatchServiceRegisteringVisitor extends SimpleFileVisitor<Path>{
    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
         dir.register(watchService,ENTRY_CREATE,ENTRY_DELETE,ENTRY_MODIFY);
         return FileVisitResult.CONTINUE;
    }
}

В строке 2 метод Files.walkFileTree использует класс WatchServiceRegisteringVisitor, определенный в строке 5, для регистрации каждого каталога в WatchService. Зарегистрированные события — это создание файлов / каталогов, удаление файлов / каталогов или обновление файла.

Издательские события

Следующим шагом является создание FutureTask, который будет выполнять работу по проверке очереди и публикации событий.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void createWatchTask() {
    watchTask = new FutureTask<>(new Callable<Integer>() {
       private int totalEventCount;
       @Override
       public Integer call() throws Exception {
           while (keepWatching) {
               WatchKey watchKey = watchService.poll(10, TimeUnit.SECONDS);
               if (watchKey != null) {
                  List<WatchEvent<?>> events = watchKey.pollEvents();
                  Path watched = (Path) watchKey.watchable();
                  PathEvents pathEvents = new PathEvents(watchKey.isValid(), watched);
                  for (WatchEvent event : events) {
                        pathEvents.add(new PathEvent((Path) event.context(), event.kind()));
                        totalEventCount++;
                  }
                  watchKey.reset();
                  eventBus.post(pathEvents);
                }
          }
           return totalEventCount;
        }
      });
    }
 
private void startWatching() {
  new Thread(watchTask).start();
}

В строке 7 мы проверяем WatchService каждые 10 секунд на наличие событий в очереди. Когда возвращается верный WatchKey, первым шагом является получение событий (строка 9), а затем получение каталога, в котором произошли события (строка 10). В строке 11 создается объект PathEvents, принимающий логические значения и наблюдаемый каталог в качестве аргументов конструктора. Строки 12-15 циклически повторяют события, полученные в строке 9, используя целевой путь и тип события в качестве аргументов для создания объекта PathEvent. Метод WatchKey.reset вызывается в строке 16, возвращая состояние WatchKey в состояние готовности, позволяя ему получать новые события и помещаться обратно в очередь. Наконец, в строке 17 EventBus публикует объект PathEvents для всех подписчиков. Здесь важно отметить, что классы PathEvents и PathEvent являются неизменяемыми. TotalEventCount, который возвращается из Callable, никогда не отображается в API, но используется для целей тестирования. Метод startWatching в строке 25 запускает поток для запуска задачи наблюдения / публикации, определенной выше.

Вывод

Соединяя WatchService с Guava EventBus, мы можем управлять WatchKey и обрабатывать события в одном потоке и асинхронно уведомлять любое количество подписчиков о событиях. Надеемся, что читатель нашел этот пример полезным. Как всегда комментарии и предложения приветствуются.

Ресурсы

Ссылка: Пример программирования событий: Google Guava EventBus и Java 7 WatchService от нашего партнера по JCG Билла Бекака в блоге « Случайные мысли о кодировании» .