Java Getriebe

Java und NetBeans

Dateiüberwachung in Java 7

Die meisten modernen Dateisysteme bieten die Möglichkeit Änderungen an Dateien oder das Erstellen bzw. Löschen von Dateien als „Event“ an die Anwendungsprogramme zu senden. Für die Anwendung bietet dies den Vorteil, dass sie nicht ständig selber auf der Platte nachsehen muss ob sich etwas geändert hat („polling“). Dies erzeugt eigentlich nur unnötige Zugriffe auf die Festplatte und kann je nach Anzahl der „überwachten“ Dateien bzw. Verzeichnissen auch schon mal das System blockieren (wenns ganz blöd läuft).

Diese hardwarenahe Funktion waren bis zur Version 6 von Java nicht direkt verfügbar. Es mussten schon mit irgendwelchen JNI/JNA Schnittstellen gearbeitet werden. Ab Java 7 gibt es ein neues Paket java.nio.file in dem Zugriffsklasse auf die lokalen Dateisysteme ermöglicht werden. Die ganzen Funktionen hier aufzuzählen würde den Artikel sprengen. Christian Ullenboom hat letzten Sommer schon einiges über das neue NIO.2 geschrieben. Insbesondere der Artikel Mit NIO.2 in Java 7 Verzeichnisse im Dateisystem überwachen beschriebt das worauf ich im folgenden eingehen möchte.[edit]Der Artikel ist mittlerweile leider im Blog nicht mehr zu finden. Im Java7 Erweiterungsbuch der „Insel“ findet sich das Thema im Kapitel 5.3.10 „Verzeichnisse im Dateisystem überwachen“.[/edit]

Das eingangs erwähnte Problem mit den Dateievents funktioniert eigentlich ganz gut, hat allerdings den Nachteil, dass die Implementierung nicht ganz so trivial ist. Das Erstellen eines [javadoc]java.nio.file.WatchService[/javadoc] und das registrieren eines Ordners zur Überwachung ist schnell gemacht:

1
2
3
File folder = new File(System.getProperty("java.io.tmpdir"));
WatchService service = FileSystems.getDefault().newWatchService();
folder.toPath().register(service, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE);

Problematisch wird es, wenn jetzt auf eine Änderung im $TEMP Ordner gewartet werden soll. Dafür ist die Methode take() des WatchService zuständig. Leider blockiert diese Methode die weitere Ausführung des Programms bis ein Event ausgelöst wird. Damit scheidet ein Aufruf im EventThread der GUI schon mal aus. Er muss also in einem eigenen Thread ausgeführt werden. Auch die weiteren Abfragen des zurückgegebenen [javadoc]java.nio.file.WatchKey[/javadoc] sind nicht gerade intuitiv zu lösen.

Die Frage ist vor allem, warum wurde von seitens Oracles nicht einfach ein in Java übliches Listener Konzept implementiert? Jeder Java Entwickler würde in kürzester Zeit damit zurecht kommen und diverse Fallstricke wie das Threading oder die korrekte Verarbeitung der WatchEvents würden einfach entfallen.

Ich stelle hier mal einen ersten Entwurf von mir vor für ein solches Listener Konzept. Man kann beim FileChangeListener für ein Verzeichnis oder auch eine einzelne Datei einen FileChangeListener anmelden. Ändert sich etwas wird die Methode fileChanged() aufgerufen und alle „wichtigen“ Details dazu sind in der übergebenen FileChangeEvent Instanz verfügbar. Wie schon gesagt ist es ein erster Entwurf. Ich habe nicht getestet, was passiert, wenn sehr viele Dateien und Verzeichnisse überwacht werden. Verzeichnisse können auch nicht automatisch rekursiv überwacht werden. Außerdem habe ich nicht vollständig implementiert, was passiert, wenn ein Listener nicht mehr benötigt wird und der WatchService „aufgeräuft“ werden sollte.

Download: filechangelistener-0.1.zip