Un ghid pentru WatchService în Java NIO2

1. Prezentare generală

În acest articol, vom explora WatchService interfața de API - uri de sistem de fișiere Java NIO.2. Aceasta este una dintre caracteristicile mai puțin cunoscute ale noilor API IO care au fost introduse în Java 7 alături de interfața FileVisitor .

Pentru a utiliza interfața WatchService în aplicațiile dvs., trebuie să importați clasele corespunzătoare:

import java.nio.file.*;

2. De ce să folosiți WatchService

Un exemplu comun pentru a înțelege ce face serviciul este de fapt IDE.

Este posibil să fi observat că IDE-urile detectează întotdeauna o modificare a fișierelor de cod sursă care se întâmplă în afara ei. Unele IDE vă informează folosind o casetă de dialog, astfel încât să puteți alege să reîncărcați fișierul din sistemul de fișiere sau nu, altele pur și simplu actualizați fișierul în fundal.

În mod similar, cadrele mai noi, cum ar fi Play, reîncarcă în mod implicit codul aplicației în mod implicit - ori de câte ori efectuați editări de la orice editor.

Aceste aplicații utilizează o caracteristică numită notificare de modificare a fișierelor care este disponibilă în toate sistemele de fișiere.

Practic, putem scrie cod pentru a interoga sistemul de fișiere pentru modificări în anumite fișiere și directoare . Cu toate acestea, această soluție nu este scalabilă mai ales dacă fișierele și directoarele ajung la sute și mii.

În Java 7 NIO.2, API-ul WatchService oferă o soluție scalabilă pentru monitorizarea directoarelor pentru modificări. Are un API curat și este atât de bine optimizat pentru performanță încât nu este nevoie să implementăm propria noastră soluție.

3. Cum funcționează serviciul de supraveghere?

Pentru a utiliza caracteristicile WatchService , primul pas este să creați o instanță WatchService utilizând clasa java.nio.file.FileSystems :

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

Apoi, trebuie să creăm calea către directorul pe care dorim să îl monitorizăm:

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

După acest pas, trebuie să înregistrăm calea cu serviciul de ceas. Există două concepte importante de înțeles în această etapă. StandardWatchEventKinds clasa si WatchKey clasa. Aruncați o privire la următorul cod de înregistrare doar pentru a înțelege unde cad fiecare. Vom urmări acest lucru cu explicații despre același lucru:

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

Observați doar două lucruri importante aici: Mai întâi, apelul API de înregistrare a căii ia instanța serviciului de urmărire ca primul parametru urmat de argumentele variabile ale StandardWatchEventKinds . În al doilea rând, tipul de returnare a procesului de înregistrare este o instanță WatchKey .

3.1. cele StandardWatchEventKinds

Aceasta este o clasă ale cărei instanțe indică serviciului de vizionare tipurile de evenimente de urmărit în directorul înregistrat. În prezent, există patru evenimente posibile de urmărit:

  • StandardWatchEventKinds.ENTRY_CREATE - declanșat când se face o nouă intrare în directorul urmărit. S-ar putea datora creării unui fișier nou sau redenumirii unui fișier existent.
  • StandardWatchEventKinds.ENTRY_MODIFY - declanșat la modificarea unei intrări existente în directorul urmărit. Toate modificările de fișiere declanșează acest eveniment. Pe unele platforme, chiar și modificarea atributelor fișierului îl va declanșa.
  • StandardWatchEventKinds.ENTRY_DELETE - declanșat atunci când o intrare este ștearsă, mutată sau redenumită în directorul urmărit.
  • StandardWatchEventKinds.OVERFLOW - declanșat pentru a indica evenimentele pierdute sau eliminate. Nu ne vom concentra prea mult pe asta

3.2. The WatchKey

Această clasă reprezintă înregistrarea unui director la serviciul de ceas. Instanța sa ne este returnată de către serviciul de ceas atunci când înregistrăm un director și când întrebăm serviciul de ceas dacă au avut loc evenimente pentru care ne-am înregistrat.

Serviciul Watch nu ne oferă metode de apelare care sunt apelate ori de câte ori are loc un eveniment. Nu putem să o sondăm decât în ​​mai multe moduri de a găsi aceste informații.

Putem folosi API-ul sondajului :

WatchKey watchKey = watchService.poll();

Acest apel API revine imediat. Revine următoarea cheie de vizionare în coadă, oricare dintre ale cărei evenimente au avut loc sau nulă dacă nu au avut loc evenimente înregistrate.

De asemenea, putem folosi o versiune supraîncărcată care acceptă un argument de expirare :

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

Acest apel API este similar cu cel anterior în valoare de returnare. Cu toate acestea, blochează unitățile de timp de expirare pentru a oferi mai mult timp în care poate apărea un eveniment, în loc să revină imediat la nul.

În cele din urmă, putem folosi API - ul take :

WatchKey watchKey = watchService.take();

Această ultimă abordare se blochează pur și simplu până când apare un eveniment.

Trebuie să menționăm ceva foarte important aici: când instanța WatchKey este returnată de oricare dintre sondaje sau de a lua API - uri, nu va captura mai multe evenimente dacă API-ul resetat nu este invocat:

watchKey.reset();

Aceasta înseamnă că instanța cheii de ceas este eliminată din coada serviciului de ceas de fiecare dată când este returnată de o operație de sondaj. De resetare API apel îl pune înapoi în coada de așteptare să aștepte pentru mai multe evenimente.

Cea mai practică aplicație a serviciului de monitorizare ar necesita o buclă în cadrul căreia verificăm continuu modificările din directorul urmărit și procesăm în consecință. Putem folosi următorul idiom pentru a implementa acest lucru:

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

Creăm o cheie de ceas pentru a stoca valoarea returnată a operației de sondare. Bucla while se va bloca până când instrucțiunea condițională revine fie cu o cheie de ceas, fie cu nul.

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

În acest articol, am explorat unele dintre caracteristicile mai puțin frecvent utilizate disponibile în Java 7 NIO.2 - API-uri de sistem de fișiere, în special interfața WatchService .

De asemenea, am reușit să parcurgem etapele construirii unei aplicații de urmărire a directorului pentru a demonstra funcționalitatea.

Și, ca întotdeauna, codul sursă complet pentru exemplele utilizate în acest articol este disponibil în proiectul Github.