Статьи

Введение в NIO.2 (JSR 203), часть 5: служба наблюдения и уведомления об изменениях

Долгое время разработчики Java использовали собственные разработанные решения для мониторинга файловой системы на предмет изменений. Некоторые разработали библиотеки общего назначения, чтобы облегчить задачу для других, кто сталкивается с тем же требованием. Коммерческие и бесплатные / открытые библиотеки, такие как http://jnotify.sourceforge.net/ , http://jpathwatch.wordpress.com/ и   http://www.teamdev.com/jxfilewatcher и другие. Java 7 поставляется с NIO.2 или JSR 203, которые предоставляют службу наблюдения за файловой системой.

Служба наблюдения, предоставляемая в Java 7, использует базовые функциональные возможности файловой системы для отслеживания изменений в файловой системе, поэтому, если мы работаем в Windows, MacOS или Linux… мы уверены, что служба наблюдения не накладывает накладных расходов на опрос в нашем приложении, потому что Базовая ОС и файловая система предоставляют необходимые функции, позволяющие Java регистрироваться для получения уведомлений об изменениях файловой системы. Если базовая файловая система не обеспечивает возможности наблюдения, что, как я сомневаюсь, для любой основной файловой системы, Java прибегнет к некоторому элементарному механизму опроса, чтобы поддерживать работу кода, но производительность снизится.

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


Вся история начинается с WatchService, в котором мы проявляем интерес к просмотру пути, используя его.
Сам WatchService представляет собой интерфейс с несколькими реализациями для разных файловых систем и операционных систем.

У нас есть четыре класса для работы, когда мы разрабатываем систему с возможностью наблюдения за файловой системой.
  1. Смотрибелен : а смотрибелен является объектом класса , реализующего интерфейс смотрибелен. В нашем случае это класс Path, который является одним из центральных классов в NIO.2
  2. Набор типов событий : Мы используем его , чтобы указать , какие типы событий , мы заинтересованы в том, например , хотим ли мы получить создание, удаление, … события.. В нашем случае мы будем использовать StandardWatchEventKind, который реализует WatchEvent.Kind <T>.
  3. Модификатор события : Модификатор события , которое квалифицируется как смотрибелен зарегистрировано с WatchService. В нашем случае мы пока не будем иметь дело с чем-то конкретным, поскольку в дистрибутив JDK не включена реализация этого интерфейса.
  4. The Wacher : Это наблюдатель, который смотрит некоторые смотрибельные. В нашем случае наблюдатель следит за изменениями в файловой системе. Абстрактный класс — java.nio.file.WatchService, но мы будем использовать объект FileSystem для создания наблюдателя для файловой системы.

Теперь, когда мы знаем основы, давайте посмотрим, как будет выглядеть полная выборка, а затем разберем образец на несколько частей и обсудим их одну за другой.

import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKind;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class WatchSer {
public static void main(String args[]) throws InterruptedException {
try {
FileSystem fs = FileSystems.getDefault();
WatchService ws = null;
try {
ws = fs.newWatchService();
} catch (IOException ex) {
Logger.getLogger(WatchSer.class.getName()).log(Level.SEVERE, null, ex);
}
Path path = fs.getPath("/home/masoud/Pictures");
path.register(ws, StandardWatchEventKind.ENTRY_CREATE, StandardWatchEventKind.ENTRY_MODIFY, StandardWatchEventKind.OVERFLOW, StandardWatchEventKind.ENTRY_DELETE);

WatchKey k = ws.take();

List> events = k.pollEvents();
for (WatchEvent object : events) {
if (object.kind() == StandardWatchEventKind.ENTRY_MODIFY) {
System.out.println("Modify: " + object.context().toString());
}
if (object.kind() == StandardWatchEventKind.ENTRY_DELETE) {
System.out.println("Delete: " + object.context().toString());
}
if (object.kind() == StandardWatchEventKind.ENTRY_CREATE) {
System.out.println("Created: " + object.context().toString());
}
}
} catch (IOException ex) {
Logger.getLogger(WatchSer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

В начале кода мы создаем объект FileSystem, а затем создаем WatchService для файловой системы под объектом FileSystem, который мы создали ранее.

            FileSystem fs = FileSystems.getDefault();
WatchService ws = null;
try {
ws = fs.newWatchService();
} catch (IOException ex) {
Logger.getLogger(WatchSer.class.getName()).log(Level.SEVERE, null, ex);
}

На следующем шаге мы создаем объект Path, который в основном является нашим наблюдаемым, что мы хотим наблюдать, и затем регистрируем его в объекте WatchService, который мы создали на предыдущем шаге.

  Path path = fs.getPath("/home/masoud/Pictures");
path.register(ws, StandardWatchEventKind.ENTRY_CREATE, StandardWatchEventKind.ENTRY_MODIFY, StandardWatchEventKind.OVERFLOW, StandardWatchEventKind.ENTRY_DELETE);

Далее мы получаем ключ, который представляет собой регистрацию часов с помощью службы часов.
Этот объект WatchKey представляет нашу дверь доступа к событиям, распространяемым объектом WatchService.

Получив ключ, мы можем опросить события, пришедшие на ключ. Событие файловой системы может быть представлено несколькими событиями WatchService. Например, переименование представлено событиями удаления и создания.

 WatchKey key = ws.take();
List> events = key.pollEvents();

Теперь, когда у нас есть список событий, мы можем перебрать список событий и посмотреть, заинтересованы ли мы в этом событии или нет.

for (WatchEvent object : events) {
if (object.kind() == StandardWatchEventKind.ENTRY_MODIFY) {
System.out.println("Modify: " + path.toRealPath(true)+"/"+ object.context().toString());

}
if (object.kind() == StandardWatchEventKind.ENTRY_DELETE) {
System.out.println("Delete: " + path.toRealPath(true)+"/"+ object.context().toString());
}
if (object.kind() == StandardWatchEventKind.ENTRY_CREATE) {
System.out.println("Created: " + path.toRealPath(true)+"/"+ object.context().toString());
}
}

В нашем примере кода мы запускаем службу наблюдения в потоке приложения, в то время как мы можем использовать несколько потоков для обработки событий. Одной из причин того, что WatchService не реализован с использованием Listener, является необходимость использования нескольких потоков для обработки событий в тех случаях, когда существуют тысячи событий, или обработка событий занимает больше времени, чем пороговое значение приложения.
Вы можете получить самую последнюю версию Java 7, известную как Dolphin, отсюда и на той же странице вы можете получить последнюю версию JavaDoc, сгенерированную из того же исходного кода, который используется для генерации двоичных битов. Другие записи этой серии находятся под  тегом NIO.2 моего блога.

С http://kalali.me/introduction-nio-2-jsr-203-part-5-watch-service-and-change-notification/