WatchKey всегда нулевой - PullRequest
       6

WatchKey всегда нулевой

0 голосов
/ 18 февраля 2019

Я пытаюсь посмотреть определенные файлы на предмет изменений.Но WatchKey, который я получаю от watch_object.watch_service.poll(16, TimeUnit.MILLISECONDS);, всегда null.На консоль не выводится ни одной ошибки, поэтому я немного растерялся.

public class FileWatcher implements Runnable {

    public FileWatcher() {
    }

    static public class Watch_Object {
        public File file;
        public WatchService watch_service;
    }

    static public HashMap<Object, Watch_Object> watched_files = new HashMap<>();

    static public boolean is_running = false;


    static public synchronized void watch(Object obj, String filename) {

        File file = new File(filename);

        if (file.exists()) {

            try {

                WatchService watcher = null;
                watcher = FileSystems.getDefault().newWatchService();

                Watch_Object watch_object = new Watch_Object();
                watch_object.file = file;
                watch_object.watch_service = watcher;

                watched_files.put(obj, watch_object); 

                Path path = file.toPath().getParent();
                path.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);

                if (!is_running) {
                    (new Thread(new FileWatcher())).start();
                    is_running = true;
                }


            } 
            catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                return;
            }

        }
        else {
            // Error
        }

    }



    @Override
    public void run() {

        try  {
            while (true) {
                synchronized(this) {

                    for (Watch_Object watch_object : watched_files.values()) {

                        WatchKey key = watch_object.watch_service.poll(16, TimeUnit.MILLISECONDS);

                        System.out.println("A");

                        if (key != null) {

                            System.out.println("B");

                        }

                    }

                }
                Thread.sleep(16);    
            }
        } 
        catch (Throwable e) {
            // Log or rethrow the error
            e.printStackTrace();
        }
    }

}

Для запуска:

public static void main(String[] args) {
    // the obj is WIP, just use null for now
    watch(null, "/Users/doekewartena/Desktop/test_image.png");
}

Ответы [ 2 ]

0 голосов
/ 27 февраля 2019

Это рабочий код, который я использую, он очень похож на ваш код.Надеюсь, это поможет.

 public void watchDirectory(Path dir) {
    logger.info("Watching directory {} for new Files", dir);

    WatchService watchService = FileSystems.getDefault().newWatchService();
    registerRecursive(dir, watchService);
    WatchKey key;
    while ((key = watchService.take()) != null) {
      key.pollEvents();
      executorService.submit(this::performAction);
      boolean reset = key.reset();
      if (!reset) {
        logger.error("Could not reset the WatchKey");
        throw new RunTimeException("Could not reset the WatchKey");
      }
    }

  }

  private void performAction() {
    // Your code after an event is registered
  }

  private void registerRecursive(Path root, WatchService watchService) throws IOException {
    Files.walkFileTree(root, new SimpleFileVisitor<Path>() {

      @Override
      public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        dir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
        return FileVisitResult.CONTINUE;
      }
    });
  }
0 голосов
/ 21 февраля 2019

Я бы хотел добавить к этому ответу префикс, сказав, что WatchService сильно зависит от реализации:

Зависимости платформы

Реализация, которая наблюдает за событиямииз файловой системы предназначен для непосредственного отображения в собственное средство уведомления о событиях в файлах, где оно доступно, или для использования примитивного механизма, такого как опрос, когда собственное средство недоступно.Следовательно, многие подробности о том, как обнаруживаются события, их своевременность и сохраняется ли их порядок, зависят от конкретной реализации.Например, когда файл в наблюдаемом каталоге изменяется, это может привести к одному событию ENTRY_MODIFY в некоторых реализациях, но к нескольким событиям в других реализациях.Краткосрочные файлы (то есть файлы, которые удаляются очень быстро после их создания) могут не обнаруживаться примитивными реализациями, которые периодически опрашивают файловую систему, чтобы обнаружить изменения.

Если просматриваемый файл не находится в локальной папкезапоминающее устройство, то это зависит от реализации, если изменения в файле могут быть обнаружены.В частности, не требуется обнаруживать изменения в файлах, выполненных в удаленных системах.


Вы упоминаете, что WatchService.poll всегда возвращает null.Это не совсем удивительно, так как poll() и poll(long,TimeUnit) вернут null, если нет событий для обработки - стандартное поведение, подобное очереди.Но вы говорите, что всегда получаете null, даже если вы изменили просматриваемый файл *.К сожалению, я не могу воспроизвести проблему, используя OpenJDK 11.0.2 (или JDK 1.8.0_202), Windows 10 и локальное устройство хранения.

* Это было сказано в комментариях к вопросу перед тем, как онибыл очищен.

При попытке вашего кода я наблюдал, как B выводится на консоль.Конечно, это было нелегко увидеть, поскольку вывод A каждые 16 миллисекунд был ошеломляющим, но это было там.Однако была одна проблема: после первого события модификации больше не сообщалось.Это приводит меня к нескольким замечаниям по поводу вашего кода.

  1. Вы не звоните WatchKey.reset().

Важно позвонитьметод по окончании обработки WatchKey.Метод помечает WatchKey как готовый к обнаружению новых событий.Без этого звонка вы не будете наблюдать за последующими событиями.

Вы не опрашиваете события из WatchKey.

Пытаясь решить проблему невидения последующих событий, я наивно добавил вызовдо reset(), ничего не делая.Это привело к тому, что подавляющее количество B s печаталось на консоли.Я был сбит с толку, потому что я только один раз изменил файл, но затем я прочитал документацию WatchKey.reset ( выделение мое):

Сброс этого ключа часов.

Если этот ключ отслеживания был отменен или этот ключ отслеживания уже находится в состоянии готовности, то вызов этого метода не имеет никакого эффекта. В противном случае, если для объекта есть ожидающие события, тогда эта клавиша наблюдения немедленно помещается в очередь для службы наблюдения. Если нет ожидающих событий, тогда клавиша наблюдения переводится в состояние готовности и остается в этом состоянии.состояние, пока не будет обнаружено событие или пока кнопка отмены не будет отменена.

То, что я видел, было просто одним и тем же событием снова и снова, потому что я никогда не обрабатывал его.После добавления звонка к WatchEvent.pollEvents() я больше не спамился с B s.

Вы создаете новый WatchService для каждого файла, который хотите просмотреть.

Похоже, вам нужен класс, который может просматривать произвольное количество файлов (и только эти файлы).Это не требует WatchService на файл, так как вы можете зарегистрировать несколько каталогов с одним WatchService.В случае, если вам потребуется , требуется , чтобы использовать несколько WatchService с, если файлы поступят из разных FileSystem с.Однако в вашем коде последовательно используется файловая система по умолчанию .

* 1.092 * Использование того же WatchService также устраняет необходимость использования poll.Я предполагаю, что причина, по которой вы используете poll, заключается в том, что вам нужно проверять каждый WatchService.Поскольку теперь есть только один, вы можете использовать вместо этого метод блокировки WatchService.take().

Вот небольшой пример, который, я считаю, вы хотите.Я не могу обещать, что это идеально, поскольку это не было полностью проверено.Также я не могу обещать, что он будет работать на вашем компьютере.

import java.io.Closeable;
import java.io.IOException;
import java.nio.file.ClosedWatchServiceException;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;

/**
 * Watches files for modification events, but not for creation, 
 * deletion, or overflow events.
 */
public class FileWatcher implements Closeable, Runnable {

    private final List<BiConsumer<? super FileWatcher, ? super Path>> handlers 
            = new CopyOnWriteArrayList<>();

    private final Object lock = new Object();
    private final Map<Path, Registry> registeredDirs = new HashMap<>();
    private final Set<Path> watchedFiles = new HashSet<>();

    private final AtomicBoolean running = new AtomicBoolean();
    private final FileSystem fileSystem;
    private final WatchService service;

    public FileWatcher(FileSystem fs) throws IOException {
        service = fs.newWatchService();
        fileSystem = fs;
    }

    public FileSystem getFileSystem() {
        return fileSystem;
    }

    public boolean startWatching(Path file) throws IOException {
        Objects.requireNonNull(file);
        synchronized (lock) {
            if (watchedFiles.add(file)) {
                Path directory = file.getParent();
                if (registeredDirs.containsKey(directory)) {
                    registeredDirs.get(directory).incrementCount();
                } else {
                    try {
                        WatchKey key = directory.register(service, ENTRY_MODIFY);
                        registeredDirs.put(directory, new Registry(key));
                    } catch (ClosedWatchServiceException | IllegalArgumentException
                            | IOException | SecurityException ex) {
                        watchedFiles.remove(file);
                        throw ex;
                    }
                }
                return true;
            }
            return false;
        }
    }

    public boolean stopWatching(Path file) {
        Objects.requireNonNull(file);
        synchronized (lock) {
            if (watchedFiles.remove(file)) {
                Path directory = file.getParent();
                Registry registry = registeredDirs.get(directory);
                if (registry.decrementCount()) {
                    registeredDirs.remove(directory);
                    registry.cancelKey();
                }
                return true;
            }
            return false;
        }
    }

    public void addHandler(BiConsumer<? super FileWatcher, ? super Path> handler) {
        handlers.add(Objects.requireNonNull(handler));
    }

    public void removeHandler(BiConsumer<? super FileWatcher, ? super Path> handler) {
        handlers.remove(Objects.requireNonNull(handler));
    }

    private void fireModifyEvent(Path source) {
        for (BiConsumer<? super FileWatcher, ? super Path> handler : handlers) {
            try {
                handler.accept(this, source);
            } catch (RuntimeException ex) {
                Thread.currentThread().getUncaughtExceptionHandler()
                        .uncaughtException(Thread.currentThread(), ex);
            }
        }
    }

    @Override
    public void close() throws IOException {
        service.close();
        synchronized (lock) {
            registeredDirs.clear();
            watchedFiles.clear();
        }
    }

    @Override
    public void run() {
        if (running.compareAndSet(false, true)) {
            try {
                while (!Thread.interrupted()) {
                    WatchKey key = service.take();
                    for (WatchEvent<?> event : key.pollEvents()) {
                        Path source = ((Path) key.watchable())
                                .resolve((Path) event.context());
                        boolean isWatched;
                        synchronized (lock) {
                            isWatched = watchedFiles.contains(source);
                        }
                        if (isWatched) {
                            fireModifyEvent(source);
                        }
                    }
                    key.reset();
                }
            } catch (InterruptedException ignore) {
            } finally {
                running.set(false);
            }
        } else {
            throw new IllegalStateException("already running");
        }
    }

    private static class Registry {

        private final WatchKey key;
        private int count;

        private Registry(WatchKey key) {
            this.key = key;
            incrementCount();
        }

        private void incrementCount() {
            count++;
        }

        private boolean decrementCount() {
            return --count <= 0;
        }

        private void cancelKey() {
            key.cancel();
        }

    }

}

И небольшое приложение, использующее выше FileWatcher:

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main {

    public static void main(String[] args) throws IOException {
        Path file = chooseFile();
        if (file == null) {
            return;
        }
        System.out.println("Entered \"" + file + "\"");

        ExecutorService executor = Executors.newSingleThreadExecutor();
        try (FileWatcher watcher = new FileWatcher(FileSystems.getDefault())) {
            Future<?> task = executor.submit(watcher);
            executor.shutdown();

            watcher.addHandler((fw, path) -> System.out.println("File modified: " + path));

            watcher.startWatching(file);

            waitForExit();
            task.cancel(true);
        } finally {
            executor.shutdownNow();
        }
    }

    private static Path chooseFile() {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("Enter file (or 'exit' to exit application): ");
            String line = scanner.nextLine();
            if ("exit".equalsIgnoreCase(line.trim())) {
                return null;
            }
            Path file = Paths.get(line).toAbsolutePath().normalize();
            if (Files.isRegularFile(file, LinkOption.NOFOLLOW_LINKS)) {
                return file;
            }
            System.out.println("File must exist and be a regular file. Try again.");
        }
    }

    private static void waitForExit() {
        System.out.println("\nType 'exit' to exit the application.");
        Scanner scanner = new Scanner(System.in);
        while (true) {
            String line = scanner.nextLine();
            if ("exit".equalsIgnoreCase(line.trim())) {
                return;
            }
        }
    }

}

И GIF-файл в действии:

enter image description here

...