Advanced   Java   Services WatchService Back Next Up Home

WatchService

Eine der interessantesten Neuerungen in der Version 7 ist der WatchService. Mit ihm kann man die Vorgänge in einem Verzeichnis dokumentieren. Der WatchService übernimmt direkt die Eventaufrufe des Betriebssystems und ist damit vergleichbar mit einem LowLevelEvent. Aus diesem Grund erfordert das Einrichten eines WatchService einen gewissen Aufwand. Im folgenden wird das Vorgehen beschrieben.


Das Vorgehen

In welchem Filesystem liegt das zu beobachtende Verzeichnis?

Ein WatchService dient im Normalfall dazu, die Vorgänge in einem Verzeichnis zu überwachen. Das Verzeichnis liegt in einem bnestimmten FileSystem. Man muß sich also zuerst klarmachen in welchem Dateisystem das zu beobachtende Directory liegt. In den meisten Fällen ist es wohl das lokale FileSystem bzw. das DefaultFileSystem. Das Vorgehen ist aber für jedes FileSystem das gleiche.

FileSystem fileSystem = FileSystems.getDefault();  // liefert c:\, d:\, usw. auf Windowssystemen

Welche Rootverzeichnisse zu einem FileSystem gehören, liefert die Methode getRootDirectories().

Iterable<Path> iter = fileSystem.getRootDirectories();

Mit Hilfe des Filesystem holt man sich einen WatchService
WatchService watchService = fileSystem.newWatchService();

Ein Pathobjekt anlegen für das zu beobachtende Verzeichnis

Als nächstes braucht man ein ein Pathobjekt in diesem FileSystem das auf das zu überwachende Verzeichnis zeigt.

Path dirToWatch = Paths.get("c://tmp");

Die register-Methoden aus dem Interface Path

Das Interface Path stellt zwei register-Methoden bereit.

Modifier and TypeMethod and Description
WatchKeyregister(WatchService watcher, WatchEvent.Kind<?>... events)
Registers the file located by this path with a watch service.
WatchKeyregister(WatchService watcher, WatchEvent.Kind<?>[] events, WatchEvent.Modifier... modifiers)
Registers the file located by this path with a watch service.

Welche Ereignisse können überwacht werden

Die zu überwachenden Ereignisse sind als Felder in der KLasse StandardWatchEventKinds angelegt. Die vier statischen Konstanten sind vom Typ WatchEvent.Kind<T>. WatchEvent.Kind<T> selbst ist ein statisches inneres Interface von WatchEvent<T>.

OVERFLOW ist ein spezieller Event, der signalisiert, daß Events verlorengegangen oder verworfen worden sind. Die restlichen Eventnamen sind selbsterklärend. Events der Form ENTRY_CREATE und ENTRY_DELETE treten immer genau einmal auf, die restlichen Events können für einen Vorgang auch öfter als einmal auftreten.


Registrieren des WatchService
WatchKey watchKey = dirToWatch.register(watchService,
                                        StandardWatchEventKinds.ENTRY_CREATE,
                                        StandardWatchEventKinds.ENTRY_DELETE,
                                        StandardWatchEventKinds.ENTRY_MODIFY);

WatchKey verwaltet die anfallenden Events. Beim Registrieren wird er meist noch nicht gebraucht.


Der Ereignishandler

Da der WatchService mit Events arbeitet, braucht man einen Eventhandler. Das Einrichten dieses Handler erfordert etwas mehr Aufwand. Die Events werden von einem Watchkey verwaltet, den der WatchService zur Verfügung stellt. Der Eventhandler muß also ein Watchserviceobjekt erhalten. Am besten läuft er als eigener Thread. Der Aufruf kann etwa folgendermaßen aus:

WatchHandler wh = new WatchHandler(watchService, dirToWatch);
wh.start();

Die folgende Klasse WatchHandler ist eine Beispielimplementierung.

package hms;

import java.util.List;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

public class WatchHandler extends Thread
{
   WatchService watchService;
   Path dirToWatch;

   public WatchHandler(WatchService watchService, Path dirToWatch)
   {
      this.watchService = watchService;
      this.dirToWatch = dirToWatch;
   }

   /**
    */
   @Override
   public void run()
   {
      System.out.println("run");
      for(;;)
      {
         try
         {
            WatchKey key = watchService.take(); // blockiert
            List<WatchEvent<?>> eventList = key.pollEvents();
            System.out.println("size = " + eventList.size());
            for(WatchEvent<?> e : eventList)
            {
               System.out.print(e.kind() + " -> ");
               Path name = (Path)e.context();
               //System.out.print(name.getParent());
               // context liefert nur den Dateinamen, parent ist null !
               Path path = dirToWatch.resolve(name);
               System.out.print(path);
               if (Files.isDirectory(path))
                  System.out.println(" <dir>");
               else
                  System.out.println(" <file<");
            }
            boolean valid = key.reset();
            if (!valid)
            {
               break;
            }
         }
         catch(InterruptedException ex)
         {
            // TODO Auto-generated catch block
            ex.printStackTrace();
         }
      } // end for

   } // end run

} // end class

Erläuterung

Ausgangspunkt ist das Statement watchService.take(). take blockiert bis Events anliegen und liefert dann ein WatchKey-Objekt. Dieses Objekt enthält eine Liste von Events vom Typ WatchEvent, die wir mit der Methode pollEvents() bekommen. Diese Liste wird dann in einer for-Schleife untersucht. Es kann sein, daß man einen ungültigen WatchKey erhält. In diesem Fall wird die Endlosschleife beendet.


main()

Hier noch die vollständige main()-Klasse, die den WatchService einrichtet und den Thread startet.

public class WatchServiceDemo
{
   public static void main(String[] args)
   {
      FileSystem fs = FileSystems.getDefault(); // sun.nio.fs.WindowsFileSystem

      try
      {
         Path dirToWatch = Paths.get("c://tmp");
         WatchService watchService = fs.newWatchService(); // sun.nio.fs.WindowsWatchService
         WatchKey watchKey = dirToWatch.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
               StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);

         WatchHandler wh = new WatchHandler(watchService, dirToWatch);
         wh.start();
      }
      catch(IOException ex)
      {
         ex.printStackTrace();
      }
   }

} // end class WatchServiceDemo
Valid XHTML 1.0 Strict top Back Next Up Home