44 votes

Java 7 WatchService - Ignorer les occurrences multiples du même événement

La javadoc pour StandardWatchEventKinds.ENTRY_MODIFY dit :

Modification de l'entrée du répertoire. Lorsqu'un répertoire est enregistré pour cet événement, le WatchKey est mis en file d'attente lorsqu'il est constaté qu'une entrée dans le l'annuaire a été modifiée. Le nombre d'événements pour cet événement est de 1 ou plus.

Lorsque vous modifiez le contenu d'un fichier par le biais d'un éditeur, celui-ci modifie à la fois la date (ou d'autres métadonnées) et le contenu. Vous obtenez donc deux ENTRY_MODIFY mais chacun aura un count de 1 (du moins, c'est ce que je vois).

J'essaie de surveiller un fichier de configuration ( servers.cfg préalablement enregistré auprès de l WatchService ) qui est mis à jour manuellement (c'est-à-dire par la ligne de commande vi ) avec le code suivant :

while(true) {
    watchKey = watchService.take(); // blocks

    for (WatchEvent<?> event : watchKey.pollEvents()) {
        WatchEvent<Path> watchEvent = (WatchEvent<Path>) event;
        WatchEvent.Kind<Path> kind = watchEvent.kind();

        System.out.println(watchEvent.context() + ", count: "+ watchEvent.count() + ", event: "+ watchEvent.kind());
        // prints (loop on the while twice)
        // servers.cfg, count: 1, event: ENTRY_MODIFY
        // servers.cfg, count: 1, event: ENTRY_MODIFY

        switch(kind.name()) {
            case "ENTRY_MODIFY":
                handleModify(watchEvent.context()); // reload configuration class
                break;
            case "ENTRY_DELETE":
                handleDelete(watchEvent.context()); // do something else
                break;              
        }
    }   

    watchKey.reset();       
}

Puisque vous avez deux ENTRY_MODIFY les événements ci-dessus rechargeraient deux fois la configuration alors qu'une seule fois est nécessaire. Existe-t-il un moyen d'ignorer tous ces événements sauf un, en supposant qu'il puisse y en avoir plusieurs ?

Si le WatchService Si l'API dispose d'une telle utilité, c'est tant mieux. (Je n'ai pas vraiment envie de vérifier les temps entre chaque événement. Toutes les méthodes du gestionnaire dans mon code sont synchrones.

La même chose se produit si vous créez (copier/coller) un fichier d'un répertoire vers le répertoire surveillé. Comment pouvez-vous combiner ces deux événements en un seul ?

0voto

codingkapoor Points 83

Si vous essayez de faire la même chose en Scala en utilisant better-files-akka J'ai trouvé une solution de contournement basée sur la solution proposée dans la réponse acceptée.

https://github.com/pathikrit/better-files/issues/313

trait ConfWatcher {

  implicit def actorSystem: ActorSystem

  private val confPath = "/home/codingkapoor/application.conf"
  private val appConfFile = File(confPath)
  private var appConfLastModified = appConfFile.lastModifiedTime

  val watcher: ActorRef = appConfFile.newWatcher(recursive = false)

  watcher ! on(EventType.ENTRY_MODIFY) { file =>
    if (appConfLastModified.compareTo(file.lastModifiedTime) < 0) {
      // TODO
      appConfLastModified = file.lastModifiedTime
    }
  }

}

0voto

M.Hoang Points 31

L'ensemble des événements multiples dépend de l'outil utilisé pour créer/modifier les fichiers.

1. créer un nouveau fichier avec Vim

Les événements MODIFIED et CREATED sont déclenchés.

2.modifier le fichier avec Vim

Les événements DELETED, MODIFIED, CREATED sont déclenchés.

Utilisez la commande Linux "mv" pour déplacer un fichier d'un autre dossier vers le dossier surveillé.

L'événement MODIFIED est déclenché

4. utiliser la commande Linux 'cp' pour copier un fichier d'un autre dossier vers le dossier surveillé.

MODIFIED, CREATED sont déclenchés si aucun fichier de même nom n'existe. CREATED est déclenché si un fichier portant le même nom existe.

J'ai utilisé 3 cartes pour collecter les entrées CREATED/MODIFIED/DELETED dans la boucle for itérant sur WatchEvent. Ensuite, j'ai exécuté 3 boucles for sur ces 3 cartes pour déterminer l'événement correct que nous devons notifier (ex : si un nom de fichier apparaît dans les trois cartes, alors nous pourrions dire que c'est un événement MODIFIED).

            File f = new File(sourceDir);
        if (!f.exists() || !f.isDirectory()) {
            LOGGER.warn("File " + sourceDir + " does not exist OR is not a directory");
            return;
        }
        WatchService watchService = FileSystems.getDefault().newWatchService();
        Path watchedDir = f.toPath();
        watchedDir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);

        while (true) {
            try {
                WatchKey watchKey = watchService.take();

                Thread.sleep(checkInterval);

                //Events fired by Java NIO file watcher depends on the tool used
                //to create/update/delete file. We need those 3 maps to collect
                //all entries fired by NIO watcher. Then, make 3 loops at the end
                //to determine the correct unique event to notify
                final Map<String, Boolean> createdEntries = new HashMap<>();
                final Map<String, Boolean> modifiedEntries = new HashMap<>();
                final Map<String, Boolean> deletedEntries = new HashMap<>();

                List<WatchEvent<?>> events = watchKey.pollEvents();
                for (WatchEvent<?> event : events) {
                    if (event.kind() == OVERFLOW) {
                        continue;
                    }

                    WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
                    WatchEvent.Kind<Path> kind = pathEvent.kind();

                    Path path = pathEvent.context();
                    String fileName = path.toString();
                    if (accept(fileName)) {
                        if (kind == ENTRY_CREATE) {
                            createdEntries.put(fileName, true);
                        } else if (kind == ENTRY_MODIFY) {
                            modifiedEntries.put(fileName, true);
                        } else if (kind == ENTRY_DELETE) {
                            deletedEntries.put(fileName, true);
                        }
                    }
                }

                long timeStamp = System.currentTimeMillis();
                final Map<String, Boolean> handledEntries = new HashMap<>();

                //3 for loops to determine correct event to notify
                for (String key : createdEntries.keySet()) {
                    if (handledEntries.get(key) == null) {
                        Boolean modified = modifiedEntries.get(key);
                        Boolean deleted = deletedEntries.get(key);
                        if (modified != null && deleted != null) {
                            //A triplet of DELETED/MODIFIED/CREATED means a MODIFIED event
                            LOGGER.debug("File " + key + " was modified");
                            notifyFileModified(key, timeStamp);
                        } else if (modified != null) {
                            LOGGER.debug("New file " + key + " was created");
                            notifyFileCreated(key, timeStamp);
                        } else {
                            LOGGER.debug("New file " + key + " was created");
                            notifyFileCreated(key, timeStamp);
                        }
                        handledEntries.put(key, true);
                    }
                }

                for (String key : modifiedEntries.keySet()) {
                    if (handledEntries.get(key) == null) {
                        //Current entry survives from loop on CREATED entries. It is certain
                        //that we have MODIFIED event
                        LOGGER.debug("File " + key + " was modified");
                        notifyFileModified(key, timeStamp);
                        handledEntries.put(key, true);
                    }

                }

                for (String key : deletedEntries.keySet()) {
                    if (handledEntries.get(key) == null) {
                        //Current entry survives from two loops on CREATED/MODIFIED entries. It is certain
                        //that we have DELETE event
                        LOGGER.debug("File " + key + " was deleted");
                        notifyFileDeleted(key, timeStamp);
                    }
                }

                boolean valid = watchKey.reset();
                if (!valid) {
                    break;
                }
            } catch (Exception ex) {
                LOGGER.warn("Error while handling file events under: " + sourceDir, ex);
            }

            Thread.sleep(checkInterval);
        }

0voto

user2729516 Points 23

J'ai eu un problème similaire. Je sais que c'est un peu tard mais cela pourrait aider quelqu'un. J'avais juste besoin d'éliminer les doublons ENTRY_MODIFY . Chaque fois que ENTRY_MODIFY est déclenché, count() renvoie soit 2 soit 1. Si c'est 1, alors il y aura un autre événement avec count() 1. Il suffit donc de mettre un compteur global qui garde le compte des valeurs de retour et d'effectuer les opérations seulement lorsque le compteur devient 2. Quelque chose comme ceci peut faire l'affaire :

WatchEvent event; 
int count = 0;

if(event.count() == 2)
     count = 2;

if(event.count() == 1)
     count++;

if(count == 2){
     //your operations here
     count = 0;
}

0voto

宏杰李 Points 8709
    /**
 * 
 * 
 * in windows os, multiple event will be fired for a file create action
 * this method will combine the event on same file
 * 
 * for example:
 * 
 * pathA -> createEvent -> createEvent
 * pathA -> createEvent + modifyEvent, .... -> modifyEvent
 * pathA -> createEvent + modifyEvent, ...., deleteEvent -> deleteEvent
 * 
 * 
 * 
 * windows1+, 
 *   >  > 
 * 
 *
 * @param events
 * @return
 */
private List<WatchEvent<?>> filterEvent(List<WatchEvent<?>> events) {

    // sorted by event create > modify > delete
    Comparator<WatchEvent<?>> eventComparator = (eventA, eventB) -> {
        HashMap<WatchEvent.Kind, Integer> map = new HashMap<>();
        map.put(StandardWatchEventKinds.ENTRY_CREATE, 0);
        map.put(StandardWatchEventKinds.ENTRY_MODIFY, 1);
        map.put(StandardWatchEventKinds.ENTRY_DELETE, 2);
        return map.get(eventA.kind()) - map.get(eventB.kind());

    };
    events.sort(eventComparator);

    HashMap<String, WatchEvent<?>> hashMap = new HashMap<>();
    for (WatchEvent<?> event : events) {
        // if this is multiple event on same path
        // the create event will added first
        // then override by modify event
        // then override by delete event
        hashMap.put(event.context().toString(), event);
    }

    return new ArrayList<>(hashMap.values());

}

-1voto

Robert H Points 4145

Non testé, mais peut-être que cela fonctionnera :

AtomicBoolean modifyEventFired = new AtomicBoolean();
modifyEventFired.set(false);

while(true) {
    watchKey = watchService.take(); // blocks

    for (WatchEvent<?> event : watchKey.pollEvents()) {
        WatchEvent<Path> watchEvent = (WatchEvent<Path>) event;
        WatchEvent.Kind<Path> kind = watchEvent.kind();

        System.out.println(watchEvent.context() + ", count: "+ watchEvent.count() + ", event: "+ watchEvent.kind());
        // prints (loop on the while twice)
        // servers.cfg, count: 1, event: ENTRY_MODIFY
        // servers.cfg, count: 1, event: ENTRY_MODIFY

        switch(kind.name()) {
            case "ENTRY_MODIFY":
                if(!modifyEventFired.get()){
                   handleModify(watchEvent.context()); // reload configuration class
                   modifyEventFired.set(true);                           
                }
                break;
            case "ENTRY_DELETE":
                handleDelete(watchEvent.context()); // do something else
                break;              
        }
    }   
    modifyEventFired.set(false);
    watchKey.reset();       
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X