Eine Anleitung zu WatchService in Java NIO2

1. Übersicht

In diesem Artikel werden wir die WatchService- 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 der FileVisitor- Oberfläche eingeführt wurden.

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

import java.nio.file.*;

2. Warum WatchService verwenden ?

Ein häufiges Beispiel, um zu verstehen, was der Dienst tut, ist die IDE.

Möglicherweise haben Sie bemerkt, dass die IDEs immer eine Änderung der Quellcodedateien erkennen , die außerhalb von sich selbst erfolgt. 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 namens Dateiänderungsbenachrichtigung , die in allen Dateisystemen verfügbar ist.

Grundsätzlich können wir Code schreiben, um das Dateisystem auf Änderungen an bestimmten Dateien und Verzeichnissen abzufragen . Diese Lösung ist jedoch nicht skalierbar, insbesondere wenn die Dateien und Verzeichnisse Hunderte und Tausende erreichen.

In Java 7 NIO.2 bietet die WatchService- 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 WatchService- Funktionen zu verwenden, müssen Sie zunächst eine WatchService- Instanz mit der Klasse java.nio.file.FileSystems erstellen :

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 StandardWatchEventKinds- Klasse und die WatchKey- Klasse. Schauen Sie sich den folgenden Registrierungscode an, um zu verstehen, wo jeder Herbst liegt. Wir werden dies 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 von StandardWatchEventKinds . Zweitens ist der Rückgabetyp des Registrierungsprozesses eine WatchKey- Instanz.

3.1. Die StandardWatchEventKinds

Dies ist eine Klasse, deren Instanzen dem Überwachungsdienst mitteilen, auf welche Arten von Ereignissen im registrierten Verzeichnis geachtet werden soll. Derzeit sind vier mögliche Ereignisse zu beachten:

  • StandardWatchEventKinds.ENTRY_CREATE - wird ausgelöst, wenn ein neuer Eintrag im überwachten Verzeichnis vorgenommen wird. Dies kann an der Erstellung einer neuen Datei oder der Umbenennung einer vorhandenen Datei liegen.
  • 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. Der WatchKey

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

Der Überwachungsdienst bietet uns keine Rückrufmethoden an, die aufgerufen werden, wenn ein Ereignis eintritt. Wir können es nur auf verschiedene Arten abfragen, um diese Informationen zu finden.

Wir können die Umfrage- API verwenden:

WatchKey watchKey = watchService.poll();

Dieser API-Aufruf kehrt sofort zurück. 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 ein Timeout- Argument benötigt:

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

Dieser API-Aufruf ähnelt dem vorherigen Rückgabewert. Es werden jedoch Zeitüberschreitungseinheiten blockiert , um mehr Zeit für das Auftreten eines Ereignisses anzugeben, anstatt sofort null zurückzugeben.

Schließlich können wir die take- API verwenden:

WatchKey watchKey = watchService.take();

Dieser letzte Ansatz blockiert einfach, bis ein Ereignis eintritt.

Wir müssen etwas beachten Sie sehr wichtig: wenn die WatchKey Beispiel durch eine der zurückgegeben wird , Umfrage oder nehmen APIs, wird es nicht mehr Ereignisse erfassen , wenn er zurückgesetzt API ist nicht aufgerufen wird:

watchKey.reset();

Dies bedeutet, dass die Überwachungsschlüsselinstanz jedes Mal aus der Überwachungsdienstwarteschlange entfernt wird, wenn sie von einem Abfragevorgang zurückgegeben wird. Der API-Aufruf zum Zurücksetzen stellt 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 Watch-Schlüssel oder mit null zurückgegeben wird.

When we get a watch key, then the while loop executes the code inside it. We use the WatchKey.pollEvents API to return a list of events that have occurred. We then use a for each loop to process them one by one.

After all the events are processed, we must call the reset API to enqueue the watch key again.

4. Directory Watching Example

Since we have covered the WatchService API in the previous subsection and how it works internally and also how we can use it, we can now go ahead and look at a complete and practical example.

For portability reasons, we are going to watch for activity in the user home directory, which should be available on all modern operating systems.

The code contains only a few lines of code so we will just keep it in the main method:

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

And that is all we really have to do. Now you can run the class to start watching a directory.

When you navigate to the user home directory and perform any file manipulation activity like creating a file or directory, changing contents of a file or even deleting a file, it will all be logged at the console.

For instance, assuming you go to user home, right click in space, select `new – > file` to create a new file and then name it testFile. Then you add some content and save. The output at the console will look like this:

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.

Feel free to edit the path to point to any directory you want to watch.

5. Conclusion

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 die WatchService- Oberfläche.

Wir haben es auch geschafft, die Schritte zum Erstellen einer Verzeichnisüberwachungsanwendung zu durchlaufen, um die Funktionalität zu demonstrieren.

Und wie immer ist der vollständige Quellcode für die in diesem Artikel verwendeten Beispiele im Github-Projekt verfügbar.