Guide sur WatchService dans Java NIO2

Guide de WatchService en Java NIO2

1. Vue d'ensemble

Dans cet article, nous allons explorer l'interfaceWatchService des API du système de fichiers Java NIO.2. C'est l'une des fonctionnalités les moins connues des nouvelles API IO qui ont été introduites dans Java 7 avec l'interface deFileVisitor.

Pour utiliser l'interfaceWatchService dans vos applications, vous devez importer les classes appropriées:

import java.nio.file.*;

2. Pourquoi utiliserWatchService

Un exemple courant pour comprendre ce que fait le service est en réalité l'EDI.

Vous avez peut-être remarqué que les IDE sont toujoursdetects a change in source code files qui se produisent en dehors de lui-même. Certains IDE vous informent à l'aide d'une boîte de dialogue afin que vous puissiez choisir de recharger le fichier à partir du système de fichiers ou non, d'autres mettent simplement à jour le fichier en arrière-plan.

De même, les nouveaux frameworks tels que Play effectuent également un rechargement à chaud du code de l'application par défaut, chaque fois que vous effectuez des modifications à partir de n'importe quel éditeur.

Ces applications utilisent une fonctionnalité appeléefile change notification qui est disponible dans tous les systèmes de fichiers.

Fondamentalement,we can write code to poll the filesystem for changes on specific files and directories. Cependant, cette solution n'est pas évolutive, en particulier si les fichiers et les répertoires atteignent des centaines et des milliers.

Dans Java 7 NIO.2, l'APIWatchService fournit une solution évolutive pour surveiller les répertoires des modifications. Il dispose d'une API propre et est si bien optimisé pour les performances que nous n'avons pas besoin de mettre en œuvre notre propre solution.

3. Comment fonctionne Watchservice?

Pour utiliser les fonctionnalitésWatchService, la première étape consiste à créer une instanceWatchService en utilisant la classejava.nio.file.FileSystems:

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

Ensuite, nous devons créer le chemin du répertoire que nous voulons surveiller:

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

Après cette étape, nous devons enregistrer le chemin avec le service de surveillance. Il y a deux concepts importants à comprendre à ce stade. La classeStandardWatchEventKinds et la classeWatchKey. Jetez un coup d’œil au code d’enregistrement suivant pour comprendre où chaque chute a lieu. Nous suivrons ceci avec des explications du même:

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

Notez seulement deux choses importantes ici: Premièrement, l'appel de l'API d'enregistrement de chemin prend l'instance de service de surveillance comme premier paramètre suivi des arguments variables deStandardWatchEventKinds. Deuxièmement, le type de retour du processus d'enregistrement est une instanceWatchKey.

3.1. The StandardWatchEventKinds

Il s'agit d'une classe dont les instances indiquent au service de surveillance les types d'événements à surveiller dans le répertoire enregistré. Il y a actuellement quatre événements possibles à surveiller:

  • StandardWatchEventKinds.ENTRY_CREATE - déclenché lorsqu'une nouvelle entrée est effectuée dans le répertoire surveillé. Cela peut être dû à la création d'un nouveau fichier ou au changement de nom d'un fichier existant.

  • StandardWatchEventKinds.ENTRY_MODIFY - déclenché lorsqu'une entrée existante dans le répertoire surveillé est modifiée. Toutes les modifications de fichiers déclenchent cet événement. Sur certaines plates-formes, même le changement d'attributs de fichier le déclenchera.

  • StandardWatchEventKinds.ENTRY_DELETE - déclenché lorsqu'une entrée est supprimée, déplacée ou renommée dans le répertoire surveillé.

  • StandardWatchEventKinds.OVERFLOW - déclenché pour indiquer des événements perdus ou ignorés. Nous ne nous y concentrerons pas beaucoup

3.2. The WatchKey

Cette classe représente l'enregistrement d'un répertoire avec le service de surveillance. Son instance nous est renvoyée par le service de surveillance lorsque nous enregistrons un répertoire et lorsque nous lui demandons si des événements pour lesquels nous nous sommes enregistrés se sont produits.

Le service de surveillance ne nous propose aucune méthode de rappel qui est appelée chaque fois qu'un événement se produit. Nous ne pouvons l'interroger que de plusieurs manières pour trouver cette information.

Nous pouvons utiliser l'APIpoll:

WatchKey watchKey = watchService.poll();

Cet appel d'API revient immédiatement. Il renvoie la clé de surveillance suivante en file d'attente dont l'un des événements s'est produit ou la valeur null si aucun événement enregistré ne s'est produit.

Nous pouvons également utiliser une version surchargée qui prend un argumenttimeout:

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

Cet appel d'API est similaire au précédent en valeur de retour. Cependant, il bloque pourtimeout unités de temps pour donner plus de temps pendant lequel un événement peut se produire au lieu de renvoyer NULL immédiatement.

Enfin, nous pouvons utiliser l'APItake:

WatchKey watchKey = watchService.take();

Cette dernière approche bloque simplement jusqu'à ce qu'un événement se produise.

Il faut noter quelque chose de très important ici: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();

Cela signifie que l'instance de clé de surveillance est supprimée de la file d'attente du service de surveillance chaque fois qu'elle est renvoyée par une opération d'interrogation. L'appel API dereset le remet dans la file d'attente pour attendre d'autres événements.

L'application la plus pratique du service d'observateur nécessiterait une boucle dans laquelle nous vérifions en permanence les modifications apportées au répertoire surveillé et procédions en conséquence. Nous pouvons utiliser l'idiome suivant pour implémenter ceci:

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

Nous créons une clé de surveillance pour stocker la valeur de retour de l'opération d'interrogation. La boucle while restera bloquée jusqu'au retour de l'instruction conditionnelle avec une clé de contrôle ou une valeur null.

Lorsque nous obtenons une clé de surveillance, la boucle while exécute le code qu'il contient. Nous utilisons l'APIWatchKey.pollEvents pour renvoyer une liste d'événements qui se sont produits. Nous utilisons ensuite une bouclefor each pour les traiter un par un.

Une fois tous les événements traités, nous devons appeler l'APIreset pour mettre à nouveau la clé de surveillance en file d'attente.

4. Exemple de surveillance d'annuaire

Puisque nous avons couvert l'APIWatchService dans la sous-section précédente et comment cela fonctionne en interne et comment nous pouvons l'utiliser, nous pouvons maintenant aller de l'avant et regarder un exemple complet et pratique.

Pour des raisons de portabilité, nous allons surveiller l'activité dans le répertoire de base de l'utilisateur, qui devrait être disponible sur tous les systèmes d'exploitation modernes.

Le code contient seulement quelques lignes de code, nous allons donc le garder dans la méthode principale:

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

Et c'est tout ce que nous devons vraiment faire. Vous pouvez maintenant lancer la classe pour commencer à regarder un répertoire.

Lorsque vous accédez au répertoire de base de l'utilisateur et effectuez toute opération de manipulation de fichier, telle que la création d'un fichier ou d'un répertoire, la modification du contenu d'un fichier ou même la suppression d'un fichier, tout sera consigné sur la console.

Par exemple, en supposant que vous alliez à la page d'accueil de l'utilisateur, faites un clic droit dans l'espace, sélectionnez `new – > file` pour créer un nouveau fichier, puis nommez-letestFile. Ensuite, vous ajoutez du contenu et enregistrez. La sortie sur la console ressemblera à ceci:

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.

N'hésitez pas à modifier le chemin pour qu'il pointe vers le répertoire que vous souhaitez regarder.

5. Conclusion

Dans cet article, nous avons exploré certaines des fonctionnalités les moins utilisées disponibles dans les API du système de fichiers Java 7 NIO.2, en particulier l'interfaceWatchService.

Nous avons également réussi à créer une application de surveillance de répertoires pour en démontrer la fonctionnalité.

Et, comme toujours, le code source complet des exemples utilisés dans cet article est disponible dans leGithub project.