WatchService запускает ENTRY_MODIFY иногда дважды, а иногда и один раз

Я использую этот пример WatchService из Oracle:

import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;
import static java.nio.file.LinkOption.*;
import java.nio.file.attribute.*;
import java.io.*;
import java.util.*;

public class WatchDir {

private final WatchService watcher;
private final Map<WatchKey,Path> keys;
private final boolean recursive;
private boolean trace = false;

@SuppressWarnings("unchecked")
static <T> WatchEvent<T> cast(WatchEvent<?> event) {
    return (WatchEvent<T>)event;
}

/**
 * Register the given directory with the WatchService
 */
private void register(Path dir) throws IOException {
    WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
    if (trace) {
        Path prev = keys.get(key);
        if (prev == null) {
            System.out.format("register: %s\n", dir);
        } else {
            if (!dir.equals(prev)) {
                System.out.format("update: %s -> %s\n", prev, dir);
            }
        }
    }
    keys.put(key, dir);
}

/**
 * Register the given directory, and all its sub-directories, with the
 * WatchService.
 */
private void registerAll(final Path start) throws IOException {
    // register directory and sub-directories
    Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                throws IOException
        {
            register(dir);
            return FileVisitResult.CONTINUE;
        }
    });
}

/**
 * Creates a WatchService and registers the given directory
 */
WatchDir(Path dir, boolean recursive) throws IOException {
    this.watcher = FileSystems.getDefault().newWatchService();
    this.keys = new HashMap<WatchKey,Path>();
    this.recursive = recursive;

    if (recursive) {
        System.out.format("Scanning %s ...\n", dir);
        registerAll(dir);
        System.out.println("Done.");
    } else {
        register(dir);
    }

    // enable trace after initial registration
    this.trace = true;
}

/**
 * Process all events for keys queued to the watcher
 */
void processEvents() {
    for (;;) {

        // wait for key to be signalled
        WatchKey key;
        try {
            key = watcher.take();
        } catch (InterruptedException x) {
            return;
        }

        Path dir = keys.get(key);
        if (dir == null) {
            System.err.println("WatchKey not recognized!!");
            continue;
        }

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

            // TBD - provide example of how OVERFLOW event is handled
            if (kind == OVERFLOW) {
                continue;
            }

            // Context for directory entry event is the file name of entry
            WatchEvent<Path> ev = cast(event);
            Path name = ev.context();
            Path child = dir.resolve(name);

            // print out event
            System.out.format("%s: %s\n", event.kind().name(), child);

            // if directory is created, and watching recursively, then
            // register it and its sub-directories
            if (recursive && (kind == ENTRY_CREATE)) {
                try {
                    if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
                        registerAll(child);
                    }
                } catch (IOException x) {
                    // ignore to keep sample readbale
                }
            }
        }

        // reset key and remove from set if directory no longer accessible
        boolean valid = key.reset();
        if (!valid) {
            keys.remove(key);

            // all directories are inaccessible
            if (keys.isEmpty()) {
                break;
            }
        }
    }
}

static void usage() {
    System.err.println("usage: java WatchDir [-r] dir");
    System.exit(-1);
}

public static void main(String[] args) throws IOException {
    // parse arguments
    if (args.length == 0 || args.length > 2)
        usage();
    boolean recursive = false;
    int dirArg = 0;
    if (args[0].equals("-r")) {
        if (args.length < 2)
            usage();
        recursive = true;
        dirArg++;
    }

    // register directory and process its events
    Path dir = Paths.get(args[dirArg]);
    new WatchDir(dir, recursive).processEvents();
}
}

Я разрабатываю приложение в Windows 7, а среда развертывания — rhel 7.2. Сначала в обеих ОС всякий раз, когда я копировал файл, он запускал один ENTRY_CREATED, а затем два ENTRY_MODIFY. Первая ENTRY_MODIFY была в начале копирования, а вторая ENTRY_MODIFY была в конце копирования. Так я понял, что процесс копирования окончен. Однако теперь он запускает только один ENTRY_MODIFY из rhel 7.2. Однако он по-прежнему запускает два события ENTRY_MODIFY в Windows 7.

Я нашел это в stackoverflow. Этот вопрос спрашивает, почему два ENTRY_MODIFY уволены. Это не совсем мой вопрос, но один из его ответов оспаривает то, что я спрашиваю. К сожалению, в этом споре нет решения моего вопроса.

Потому что в конце нет ENTRY_MODIFY, а только в начале копирования, я не могу понять, когда копирование закончено. Как вы думаете, что может быть причиной этого? Можно ли это исправить, как понять, что копирование закончено? Я не могу изменить rhel 7.2, но с радостью приму все, что угодно, кроме этого. Заранее спасибо.


person halil    schedule 25.08.2016    source источник
comment
Здравствуйте, посмотрите мой ответ здесь, чтобы узнать, когда был скопирован ваш файл. ХТН.   -  person TT.    schedule 26.08.2016
comment
Спасибо за ответ, много раз читал ваш комментарий. Однако я не понимаю, как получение блокировки файла поможет мне понять, когда процесс копирования завершится. Не могли бы вы объяснить более подробно?   -  person halil    schedule 26.08.2016
comment
Файл остается заблокированным для доступа RW другими процессами/потоками, пока процесс копирования занят. Фрагмент кода в моем ответе пытается заблокировать этот файл (после того, как вы узнаете о его существовании по событию ENTRY_CREATED). Блокировка будет предоставлена, как только файл перестанет быть заблокирован процессом копирования. На этом процесс копирования завершен.   -  person TT.    schedule 26.08.2016
comment
Здравствуйте еще раз, в методе processEvents я проверяю событие ENTRY_CREATE, а затем добавляю ваш блок кода в это условие. Однако он запускает java.io.FileNotFoundExcepiton: процесс не может получить доступ к файлу, поскольку он используется другим процессом. Может быть, я использую его неправильно. Не могли бы вы добавить свой блок кода в мой пример и посмотреть, работает ли он. Так что я могу принять ваш ответ.   -  person halil    schedule 26.08.2016


Ответы (2)


Я просто проверяю, равна ли длина файла нулю или нет. Вот пример

for (WatchEvent<?> event : key.pollEvents()) {
    Path fileName = (Path) event.context();
    if("tomcat-users.xml".equals(fileName.toString())) {
        Path tomcatUsersXml = tomcatConf.resolve(fileName);
        if(tomcatUsersXml.toFile().length() > 0) {
            load(tomcatUsersXml);
        }
    } 
}
person culmat    schedule 28.04.2017
comment
рад найти гения в stackoverflow - мне не удалось найти ничего, что мне понравилось в другом потоке, но это здорово if (path.toFile().length() > 0) - person ycomp; 28.01.2019

Вы можете использовать DelayQueue для дедупликации событий.

person softwarevamp    schedule 24.12.2019