Как приостановить службу наблюдателя, чтобы я мог обновить файл, а затем перезапустить

#java

#java

Вопрос:

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

Возможно, есть лучшее предложение???

  package net.codejava.io;

import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.sql.Timestamp;
import java.util.Date;


public class DirectoryWatchDemo {

    static String logfile = "C:/Temp/log.txt";

    volatile static boolean suspended = false;

    public static void main(String[] args) {
        try {
            WatchService watcher = FileSystems.getDefault().newWatchService();
            Path dir = Paths.get("C:/Temp/");
            dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
            // System.out.println("Watch Service registered for dir: "  
            // dir.getFileName());

            while (true) {
                WatchKey key;
                try {
                    key = watcher.take();
                } catch (InterruptedException ex) {
                    return;
                }

                if (!suspended) {
                    resume();

                } else {
                    updateFile();
                    suspend();

                }

                if (key.isValid())

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

                        @SuppressWarnings("unchecked")
                        WatchEvent<Path> ev = (WatchEvent<Path>) event;
                        Path fileName = ev.context();
                        System.out.println(kind.name()   ": "   fileName);
                    }

                boolean valid = key.reset();
                if (!valid) {
                    System.out.println("Watch service invalid");
                    break;
                }
            }

        } catch (IOException ex) {
            System.err.println(ex);
        }
    }

    private static void suspend() {
        suspended = false;

    }

    private static void resume() {
        suspended = true;

    }

    public static void updateFile() {

        try {
            File outfile = new File(logfile);
            if (!outfile.exists()) {
                System.out.println("No file exists...writing a new file");
                outfile.createNewFile();
            }
            FileWriter fw = new FileWriter(outfile.getAbsoluteFile(), true);
            BufferedWriter bw = new BufferedWriter(fw);
            bw.write("TimeStamp: "   new Timestamp(new java.util.Date().getTime()).toString()   "rn");
            bw.flush();
            bw.close();
            System.out.println("done");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  

Ответ №1:

Вам нужно прервать поток, который использует службу наблюдения, потому что это единственный способ выйти из watcher.take() него. Пример с потоками старой школы:

 public void start() {
    thread = new Thread(directoryWatchDemo::watch);
    thread.start();
}

public void pause() throws InterruptedException {
    if (thread.isAlive()) {
        thread.interrupt();
        thread.join();
    }
}
  

Смотрите ExecutorService, например Executors.newSingleThreadExecutor() и Future.cancel() для большей гибкости.

В качестве альтернативы, вы можете take() все события и игнорировать их на основе volatile boolean .

Комментарии:

1. Можете ли вы подробнее рассказать о методе volatile Boolean? Это нужно было бы игнорировать непосредственно перед обновлением файла с метки времени, а затем снова включить. Извините, я новичок в этом…

2. @RLange Конечно. Вы объявляете boolean volatile suspended = false , и два метода suspend() , и resume() которые устанавливают переменную. Сразу после watcher.take() проверки значения и, если значение suspended равно true, continue ожидание следующего изменения. Другой (не наблюдающий) поток, который обновит файл и установит временные метки, может контролировать, какие изменения будут эффективно обрабатываться, вызывая два метода.

3. @RLange Если у вас всегда есть два изменения в паре, вы также можете подсчитать изменения и проверить, является ли текущее изменение нечетным или четным; или используйте Instant, а затем обрабатывайте изменения только тогда, когда произошло другое (недавнее) изменение.

4. Спасибо, Штеффен. Все еще работаю над первым предложением. Проблема заключается в том, что при изменении добавляется более одной временной метки. Я не совсем уверен, что вы говорите в своем втором предложении. «два изменения в паре»?

5. @RLange Ну, может быть, я неправильно понял вашу проблему. Я подумал, что вы изменяете содержимое файла (которое обновляет временную метку), а затем, через некоторое время, загружаете touch файл и снова обновляете mtime. Вы хотите начать обработку только после второго изменения после touch . Если я ошибаюсь, пожалуйста, уточните свой вопрос.