Ein Handbuch zu WatchService in Java NIO2

Eine Anleitung zu WatchService in Java NIO2

1. Überblick

In diesem Artikel werden wir dieWatchService-Schnittstelle von Java NIO.2-Dateisystem-APIs untersuchen. Dies ist eine der weniger bekannten Funktionen der neueren E / A-APIs, die in Java 7 neben derFileVisitor-Schnittstelle eingeführt wurden.

Um dieWatchService-Schnittstelle in Ihren Anwendungen zu verwenden, müssen Sie die entsprechenden Klassen importieren:

import java.nio.file.*;

2. WarumWatchService verwenden?

Ein gängiges Beispiel für die Funktionsweise des Dienstes ist die IDE.

Möglicherweise haben Sie bemerkt, dass die IDEs immerdetects a change in source code filesaußerhalb von sich selbst auftreten. Einige IDEs informieren Sie über ein Dialogfeld, sodass Sie wählen können, ob Sie die Datei aus dem Dateisystem neu laden möchten oder nicht, andere aktualisieren die Datei einfach im Hintergrund.

In ähnlicher Weise führen neuere Frameworks wie Play standardmäßig auch das Hot-Reload des Anwendungscodes durch - immer dann, wenn Sie Änderungen in einem Editor vornehmen.

Diese Anwendungen verwenden eine Funktion namensfile change notification, die in allen Dateisystemen verfügbar ist.

Grundsätzlichwe can write code to poll the filesystem for changes on specific files and directories. Diese Lösung ist jedoch nicht skalierbar, insbesondere wenn die Dateien und Verzeichnisse Hunderte und Tausende erreichen.

In Java 7 NIO.2 bietet dieWatchService-API eine skalierbare Lösung zum Überwachen von Verzeichnissen auf Änderungen. Es verfügt über eine saubere API und ist so gut für die Leistung optimiert, dass wir keine eigene Lösung implementieren müssen.

3. Wie funktioniert der Watchservice?

Um die Funktionen vonWatchServicezu verwenden, müssen Sie zunächst eine Instanz vonWatchServicemit der Klassejava.nio.file.FileSystemserstellen:

WatchService watchService = FileSystems.getDefault().newWatchService();

Als Nächstes müssen wir den Pfad zu dem Verzeichnis erstellen, das wir überwachen möchten:

Path path = Paths.get("pathToDir");

Nach diesem Schritt müssen wir den Pfad beim Überwachungsdienst registrieren. In dieser Phase sind zwei wichtige Konzepte zu verstehen. Die KlasseStandardWatchEventKindsund die KlasseWatchKey. Werfen Sie einen Blick auf den folgenden Registrierungscode, um zu verstehen, wo jeder Fall liegt. Wir werden darauf mit Erklärungen derselben folgen:

WatchKey watchKey = path.register(
  watchService, StandardWatchEventKinds...);

Beachten Sie hier nur zwei wichtige Dinge: Erstens verwendet der Pfadregistrierungs-API-Aufruf die Überwachungsdienstinstanz als ersten Parameter, gefolgt von variablen Argumenten vonStandardWatchEventKinds. Zweitens ist der Rückgabetyp des Registrierungsprozesses eineWatchKey-Instanz.

3.1. The StandardWatchEventKinds

Dies ist eine Klasse, deren Instanzen dem Überwachungsdienst mitteilen, auf welche Arten von Ereignissen im registrierten Verzeichnis zugegriffen werden soll. Derzeit gibt es vier mögliche Ereignisse, auf die Sie achten müssen:

  • StandardWatchEventKinds.ENTRY_CREATE - wird ausgelöst, wenn ein neuer Eintrag im überwachten Verzeichnis vorgenommen wird. Dies kann daran liegen, dass eine neue Datei erstellt oder eine vorhandene Datei umbenannt wurde.

  • StandardWatchEventKinds.ENTRY_MODIFY - wird ausgelöst, wenn ein vorhandener Eintrag im überwachten Verzeichnis geändert wird. Alle Dateibearbeitungen lösen dieses Ereignis aus. Auf einigen Plattformen wird dies sogar durch Ändern der Dateiattribute ausgelöst.

  • StandardWatchEventKinds.ENTRY_DELETE - wird ausgelöst, wenn ein Eintrag im überwachten Verzeichnis gelöscht, verschoben oder umbenannt wird.

  • StandardWatchEventKinds.OVERFLOW - wird ausgelöst, um verlorene oder verworfene Ereignisse anzuzeigen. Wir werden uns nicht viel darauf konzentrieren

3.2. The WatchKey

Diese Klasse repräsentiert die Registrierung eines Verzeichnisses beim Überwachungsdienst. Ihre Instanz wird uns vom Überwachungsdienst zurückgegeben, wenn wir ein Verzeichnis registrieren und den Überwachungsdienst fragen, ob Ereignisse aufgetreten sind, für die wir uns registriert haben.

Watch Service bietet keine Callback-Methoden an, die bei Eintreten eines Ereignisses aufgerufen werden. Wir können es nur auf verschiedene Arten abfragen, um diese Informationen zu finden.

Wir können diepoll API verwenden:

WatchKey watchKey = watchService.poll();

Dieser API-Aufruf wird sofort zurückgegeben. Es gibt den nächsten Überwachungsschlüssel in der Warteschlange zurück, dessen Ereignisse aufgetreten sind, oder null, wenn keine registrierten Ereignisse aufgetreten sind.

Wir können auch eine überladene Version verwenden, die das Argumenttimeoutakzeptiert:

WatchKey watchKey = watchService.poll(long timeout, TimeUnit units);

Dieser API-Aufruf ähnelt dem vorherigen im Rückgabewert. Es blockiert jedochtimeout Zeiteinheiten, um mehr Zeit zu geben, innerhalb derer ein Ereignis auftreten kann, anstatt sofort null zurückzugeben.

Schließlich können wir dietake API verwenden:

WatchKey watchKey = watchService.take();

Dieser letzte Ansatz blockiert einfach, bis ein Ereignis eintritt.

Wir müssen hier etwas sehr Wichtiges beachten:when the WatchKey instance is returned by either of the poll or take APIs, it will not capture more events if it’s reset API is not invoked:

watchKey.reset();

Dies bedeutet, dass die Überwachungsschlüsselinstanz jedes Mal aus der Überwachungsdienstwarteschlange entfernt wird, wenn sie von einer Abfrageoperation zurückgegeben wird. Der API-Aufruf vonresettellt ihn wieder in die Warteschlange, um auf weitere Ereignisse zu warten.

Die praktischste Anwendung des Watcher-Dienstes würde eine Schleife erfordern, in der wir kontinuierlich nach Änderungen im überwachten Verzeichnis suchen und entsprechend verarbeiten. Wir können die folgende Redewendung verwenden, um dies zu implementieren:

WatchKey key;
while ((key = watchService.take()) != null) {
    for (WatchEvent event : key.pollEvents()) {
        //process
    }
    key.reset();
}

Wir erstellen einen Überwachungsschlüssel, um den Rückgabewert der Abfrageoperation zu speichern. Die while-Schleife wird blockiert, bis die bedingte Anweisung entweder mit einem Überwachungsschlüssel oder mit Null zurückgegeben wird.

Wenn wir einen Überwachungsschlüssel erhalten, führt die while-Schleife den darin enthaltenen Code aus. Wir verwenden dieWatchKey.pollEvents-API, um eine Liste der aufgetretenen Ereignisse zurückzugeben. Wir verwenden dann einefor each-Schleife, um sie einzeln zu verarbeiten.

Nachdem alle Ereignisse verarbeitet wurden, müssen wir die API vonresetaufrufen, um den Überwachungsschlüssel erneut in die Warteschlange zu stellen.

4. Beispiel für die Verzeichnisüberwachung

Da wir dieWatchService-API im vorherigen Unterabschnitt behandelt haben und wie sie intern funktioniert und wie wir sie verwenden können, können wir uns jetzt ein vollständiges und praktisches Beispiel ansehen.

Aus Gründen der Portabilität werden wir im Benutzerverzeichnis nach Aktivitäten Ausschau halten, die auf allen modernen Betriebssystemen verfügbar sein sollten.

Der Code enthält nur wenige Codezeilen, daher behalten wir ihn in der Hauptmethode bei:

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();
        }
    }
}

Und das ist alles, was wir wirklich tun müssen. Jetzt können Sie die Klasse ausführen, um ein Verzeichnis zu überwachen.

Wenn Sie zum Home-Verzeichnis des Benutzers navigieren und Dateibearbeitungsaktivitäten wie das Erstellen einer Datei oder eines Verzeichnisses, das Ändern des Inhalts einer Datei oder sogar das Löschen einer Datei ausführen, wird alles an der Konsole protokolliert.

Angenommen, Sie gehen zum Benutzer nach Hause, klicken Sie mit der rechten Maustaste in das Leerzeichen, wählen Sienew – > file`aus, um eine neue Datei zu erstellen, und nennen Sie sie danntestFile. Dann fügen Sie etwas Inhalt hinzu und speichern. Die Ausgabe an der Konsole sieht folgendermaßen aus:

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.

Sie können den Pfad so bearbeiten, dass er auf ein beliebiges Verzeichnis verweist, das Sie überwachen möchten.

5. Fazit

In diesem Artikel haben wir einige der weniger häufig verwendeten Funktionen untersucht, die in den Java 7 NIO.2 - Dateisystem-APIs verfügbar sind, insbesondere dieWatchService-Schnittstelle.

Wir haben es auch geschafft, eine Verzeichnisüberwachungsanwendung zu erstellen, um die Funktionalität zu demonstrieren.

Und wie immer ist der vollständige Quellcode für die in diesem Artikel verwendeten Beispiele inGithub project verfügbar.