Javadoc для `StandardWatchEventKinds.ENTRY_MODIFY говорит:
запись в директории изменен. Если каталог зарегистрирован для этого событие тогда WatchKey в очередь, когда он заметил, что запись в каталог был изменен. Граф событий для этого события составляет 1 или больше.
Когда вы редактируете содержимое файла через редактор, это'МР изменить дату (или другие метаданные) и содержание. Таким образом, вы получаете два ENTRY_MODIFY событий, но каждый будет иметь Граф
на 1 (по крайней мере,'s то, что я'м видя).
Я'м пытаясь контролировать конфигурационном файле (серверов.cfg
и ранее зарегистрированный в WatchService
) это ручное обновление (т. е. через командную строку ви
) со следующим кодом:
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();
}
Поскольку вы получаете два ENTRY_MODIFY событий, тем выше будет дважды перезагрузить конфигурацию, когда только один раз необходима. Есть ли способ, чтобы игнорировать все, кроме одного из них, предполагая, что там может быть больше чем одно такое событие?
Если WatchService
API имеет такие программы тем лучше. (Я не'т хотите, чтобы проверить время между событиями. Все методы обработчика в коде являются синхронными.
То же самое происходит, если создать (копировать/вставить) файл из одного каталога в наблюдаемой директории. Как можно совместить оба в одно событие?
WatcherServices отчеты два события, поскольку основной файл обновляется дважды. Как только за содержание и один раз по времени модификации файла. Эти события происходят в течение короткого промежутка времени. Чтобы решить эту проблему, сон между опрос()
или принять()
звонки и ключ.pollEvents()` вызов. Например:
@Override
@SuppressWarnings( "SleepWhileInLoop" )
public void run() {
setListening( true );
while( isListening() ) {
try {
final WatchKey key = getWatchService().take();
final Path path = get( key );
// Prevent receiving two separate ENTRY_MODIFY events: file modified
// and timestamp updated. Instead, receive one ENTRY_MODIFY event
// with two counts.
Thread.sleep( 50 );
for( final WatchEvent<?> event : key.pollEvents() ) {
final Path changed = path.resolve( (Path)event.context() );
if( event.kind() == ENTRY_MODIFY && isListening( changed ) ) {
System.out.println( "Changed: " + changed );
}
}
if( !key.reset() ) {
ignore( path );
}
} catch( IOException | InterruptedException ex ) {
// Stop eavesdropping.
setListening( false );
}
}
}
Называя сон () помогает устранить двойной вызов. Задержка может быть до трех секунд.
У меня была похожая проблема - я с помощью API WatchService, чтобы держать в синхронизации каталогов, но заметил, что во многих случаях, обновления осуществляются дважды. Я, кажется, уже решен вопрос, проверяя метки на файлах - это, видимо, на экраны вышел второй операции копирования. (По крайней мере в Windows 7 - что я могу'т быть уверен, что он будет корректно работать в других операционных системах)
Может быть, вы могли бы использовать что-то подобное? Хранить метку времени из файла и загрузить только тогда, когда метка времени обновляется?
Один из моих Гото решения такой проблемы является просто поставить в очередь уникальным событием ресурсов и задержка обработки за приемлемое количество времени. В этом случае я утверждаю, установленное в<строка> в том, что содержит все файлы, полученные из каждого события, которое прибывает. С помощью набора<>` гарантирует, что дубликаты не'т вам и, следовательно, будет только после обработки (в период задержки).
Каждый раз интересное мероприятие приезжает я добавить имя файла в комплект<> и перезагрузить таймер задержки. Когда все успокоится и период задержки истекает, я приступаю к обработке файлов.
В addFileToProcess() и processFiles() методы 'синхронизировать' чтобы убедиться, что нет ConcurrentModificationExceptions садят.
Этот упрощенный/автономного примером является производным от Oracle'ы WatchDir.java:
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 static java.nio.file.StandardWatchEventKinds.OVERFLOW;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
public class DirectoryWatcherService implements Runnable {
@SuppressWarnings("unchecked")
static <T> WatchEvent<T> cast(WatchEvent<?> event) {
return (WatchEvent<T>)event;
}
/*
* Wait this long after an event before processing the files.
*/
private final int DELAY = 500;
/*
* Use a SET to prevent duplicates from being added when multiple events on the
* same file arrive in quick succession.
*/
HashSet<String> filesToReload = new HashSet<String>();
/*
* Keep a map that will be used to resolve WatchKeys to the parent directory
* so that we can resolve the full path to an event file.
*/
private final Map<WatchKey,path> keys;
Timer processDelayTimer = null;
private volatile Thread server;
private boolean trace = false;
private WatchService watcher = null;
public DirectoryWatcherService(Path dir, boolean recursive)
throws IOException {
this.watcher = FileSystems.getDefault().newWatchService();
this.keys = new HashMap<WatchKey,path>();
if (recursive) {
registerAll(dir);
} else {
register(dir);
}
// enable trace after initial registration
this.trace = true;
}
private synchronized void addFileToProcess(String filename) {
boolean alreadyAdded = filesToReload.add(filename) == false;
System.out.println("Queuing file for processing: "
+ filename + (alreadyAdded?"(already queued)":""));
if (processDelayTimer != null) {
processDelayTimer.cancel();
}
processDelayTimer = new Timer();
processDelayTimer.schedule(new TimerTask() {
@Override
public void run() {
processFiles();
}
}, DELAY);
}
private synchronized void processFiles() {
/*
* Iterate over the set of file to be processed
*/
for (Iterator<String> it = filesToReload.iterator(); it.hasNext();) {
String filename = it.next();
/*
* Sometimes you just have to do what you have to do...
*/
System.out.println("Processing file: " + filename);
/*
* Remove this file from the set.
*/
it.remove();
}
}
/**
* 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
{
if (dir.getFileName().toString().startsWith(".")) {
return FileVisitResult.SKIP_SUBTREE;
}
register(dir);
return FileVisitResult.CONTINUE;
}
});
}
@SuppressWarnings("unchecked")
@Override
public void run() {
Thread thisThread = Thread.currentThread();
while (server == thisThread) {
try {
// wait for key to be signaled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
return;
}
Path dir = keys.get(key);
if (dir == null) {
continue;
}
for (WatchEvent<?> event: key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind == OVERFLOW) {
continue;
}
if (kind == ENTRY_MODIFY) {
WatchEvent<path> ev = (WatchEvent<path>)event;
Path name = ev.context();
Path child = dir.resolve(name);
String filename = child.toAbsolutePath().toString();
addFileToProcess(filename);
}
}
key.reset();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void start() {
server = new Thread(this);
server.setName("Directory Watcher Service");
server.start();
}
public void stop() {
Thread moribund = server;
server = null;
if (moribund != null) {
moribund.interrupt();
}
}
public static void main(String[] args) {
if (args==null || args.length == 0) {
System.err.println("You need to provide a path to watch!");
System.exit(-1);
}
Path p = Paths.get(args[0]);
if (!Files.isDirectory(p)) {
System.err.println(p + " is not a directory!");
System.exit(-1);
}
DirectoryWatcherService watcherService;
try {
watcherService = new DirectoryWatcherService(p, true);
watcherService.start();
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
}
Вот это полная реализация с помощью метки
, чтобы избежать стрельбы несколько событий:
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import static java.nio.file.StandardWatchEventKinds.*;
public abstract class DirectoryWatcher
{
private WatchService watcher;
private Map<WatchKey, path> keys;
private Map<path, Long> fileTimeStamps;
private boolean recursive;
private boolean trace = true;
@SuppressWarnings("unchecked")
private static <T> WatchEvent<T> cast(WatchEvent<?> event)
{
return (WatchEvent<T>) event;
}
/**
* Register the given directory with the WatchService
*/
private void register(Path directory) throws IOException
{
WatchKey watchKey = directory.register(watcher, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE);
addFileTimeStamps(directory);
if (trace)
{
Path existingFilePath = keys.get(watchKey);
if (existingFilePath == null)
{
System.out.format("register: %s\n", directory);
} else
{
if (!directory.equals(existingFilePath))
{
System.out.format("update: %s -> %s\n", existingFilePath, directory);
}
}
}
keys.put(watchKey, directory);
}
private void addFileTimeStamps(Path directory)
{
File[] files = directory.toFile().listFiles();
if (files != null)
{
for (File file : files)
{
if (file.isFile())
{
fileTimeStamps.put(file.toPath(), file.lastModified());
}
}
}
}
/**
* Register the given directory, and all its sub-directories, with the
* WatchService.
*/
private void registerAll(Path directory) throws IOException
{
Files.walkFileTree(directory, new SimpleFileVisitor<path>()
{
@Override
public FileVisitResult preVisitDirectory(Path currentDirectory, BasicFileAttributes attrs)
throws IOException
{
register(currentDirectory);
return FileVisitResult.CONTINUE;
}
});
}
/**
* Creates a WatchService and registers the given directory
*/
DirectoryWatcher(Path directory, boolean recursive) throws IOException
{
this.watcher = FileSystems.getDefault().newWatchService();
this.keys = new HashMap<>();
fileTimeStamps = new HashMap<>();
this.recursive = recursive;
if (recursive)
{
System.out.format("Scanning %s ...\n", directory);
registerAll(directory);
System.out.println("Done.");
} else
{
register(directory);
}
// enable trace after initial registration
this.trace = true;
}
/**
* Process all events for keys queued to the watcher
*/
void processEvents() throws InterruptedException, IOException
{
while (true)
{
WatchKey key = watcher.take();
Path dir = keys.get(key);
if (dir == null)
{
System.err.println("WatchKey not recognized!!");
continue;
}
for (WatchEvent<?> event : key.pollEvents())
{
WatchEvent.Kind watchEventKind = event.kind();
// TBD - provide example of how OVERFLOW event is handled
if (watchEventKind == OVERFLOW)
{
continue;
}
// Context for directory entry event is the file name of entry
WatchEvent<path> watchEvent = cast(event);
Path fileName = watchEvent.context();
Path filePath = dir.resolve(fileName);
long oldFileModifiedTimeStamp = fileTimeStamps.get(filePath);
long newFileModifiedTimeStamp = filePath.toFile().lastModified();
if (newFileModifiedTimeStamp > oldFileModifiedTimeStamp)
{
fileTimeStamps.remove(filePath);
onEventOccurred();
fileTimeStamps.put(filePath, filePath.toFile().lastModified());
}
if (recursive && watchEventKind == ENTRY_CREATE)
{
if (Files.isDirectory(filePath, NOFOLLOW_LINKS))
{
registerAll(filePath);
}
}
break;
}
boolean valid = key.reset();
if (!valid)
{
keys.remove(key);
if (keys.isEmpty())
{
break;
}
}
}
}
public abstract void onEventOccurred();
}
Расширить класс и реализовать метод onEventOccurred ()
.
Я изменил WatchDir.java получать только антропогенных изменений. Сравнение `.lastModified () из файла.
long lastModi=0; //above for loop
if(kind==ENTRY_CREATE){
System.out.format("%s: %s\n", event.kind().name(), child);
}else if(kind==ENTRY_MODIFY){
if(child.toFile().lastModified() - lastModi > 1000){
System.out.format("%s: %s\n", event.kind().name(), child);
}
}else if(kind==ENTRY_DELETE){
System.out.format("%s: %s\n", event.kind().name(), child);
}
lastModi=child.toFile().lastModified();
Если вы используете RxJava можно использовать throttleLast оператора. В приведенном ниже примере только последнее событие в 1000 миллисекунд выделяется для каждого файла в наблюдаемой директории.
public class FileUtils {
private static final long EVENT_DELAY = 1000L;
public static Observable<FileWatchEvent> watch(Path directory, String glob) {
return Observable.<FileWatchEvent>create(subscriber -> {
final PathMatcher matcher = directory.getFileSystem().getPathMatcher("glob:" + glob);
WatchService watcher = FileSystems.getDefault().newWatchService();
subscriber.setCancellable(watcher::close);
try {
directory.register(watcher,
ENTRY_CREATE,
ENTRY_DELETE,
ENTRY_MODIFY);
} catch (IOException e) {
subscriber.onError(e);
return;
}
while (!subscriber.isDisposed()) {
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException e) {
if (subscriber.isDisposed())
subscriber.onComplete();
else
subscriber.onError(e);
return;
}
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind != OVERFLOW) {
WatchEvent<path> ev = (WatchEvent<path>) event;
Path child = directory.resolve(ev.context());
if (matcher.matches(child.getFileName()))
subscriber.onNext(new FileWatchEvent(kindToType(kind), child));
}
}
if (!key.reset()) {
subscriber.onError(new IOException("Invalid key"));
return;
}
}
}).groupBy(FileWatchEvent::getPath).flatMap(o -> o.throttleLast(EVENT_DELAY, TimeUnit.MILLISECONDS));
}
private static FileWatchEvent.Type kindToType(WatchEvent.Kind kind) {
if (StandardWatchEventKinds.ENTRY_CREATE.equals(kind))
return FileWatchEvent.Type.ADDED;
else if (StandardWatchEventKinds.ENTRY_MODIFY.equals(kind))
return FileWatchEvent.Type.MODIFIED;
else if (StandardWatchEventKinds.ENTRY_DELETE.equals(kind))
return FileWatchEvent.Type.DELETED;
throw new RuntimeException("Invalid kind: " + kind);
}
public static class FileWatchEvent {
public enum Type {
ADDED, DELETED, MODIFIED
}
private Type type;
private Path path;
public FileWatchEvent(Type type, Path path) {
this.type = type;
this.path = path;
}
public Type getType() {
return type;
}
public Path getPath() {
return path;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FileWatchEvent that = (FileWatchEvent) o;
if (type != that.type) return false;
return path != null ? path.equals(that.path) : that.path == null;
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (path != null ? path.hashCode() : 0);
return result;
}
}
}
Вы уверены, что есть проблема с jdk7? Это дает правильный результат для меня (jdk7u15, окна)
Код
import java.io.IOException;
import java.nio.file.*;
public class WatchTest {
public void watchMyFiles() throws IOException, InterruptedException {
Path path = Paths.get("c:/temp");
WatchService watchService = path.getFileSystem().newWatchService();
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
while (true) {
WatchKey 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;
default:
System.out.println("Event not expected " + event.kind().name());
}
}
watchKey.reset();
}
}
private void handleDelete(Path context) {
System.out.println("handleDelete " + context.getFileName());
}
private void handleModify(Path context) {
System.out.println("handleModify " + context.getFileName());
}
public static void main(String[] args) throws IOException, InterruptedException {
new WatchTest().watchMyFiles();
}
}
Выход, как ниже, когда файл копируется или редактируется с помощью блокнота.
config.xml, count: 1, event: ENTRY_MODIFY
handleModify config.xml
Ви использует множество дополнительных файлов, и, кажется, обновить атрибут файл несколько раз. Блокнот++ делает ровно в два раза.
Я решил эту проблему путем определения глобальной логической переменной по кличке "modifySolver" и что быть false по умолчанию. Вы можете справиться с этой проблемой, как я показываю ниже:
else if (eventKind.equals (ENTRY_MODIFY))
{
if (event.count() == 2)
{
getListener(getDirPath(key)).onChange (FileChangeType.MODIFY, file.toString ());
}
/*capture first modify event*/
else if ((event.count() == 1) && (!modifySolver))
{
getListener(getDirPath(key)).onChange (FileChangeType.MODIFY, file.toString ());
modifySolver = true;
}
/*discard the second modify event*/
else if ((event.count() == 1) && (modifySolver))
{
modifySolver = false;
}
}
Я собрал Оракула'ы WatchDir.java и @nilesh'ы предложение в заметный
класс, который будет уведомлять его'ы наблюдателей, когда изменения файла.
Я пытался сделать его максимально читаемым, и как можно короче, но все равно приземлился с более чем 100 строк. Улучшения приветствуются, конечно.
Использование:
FileChangeNotifier fileReloader = new FileChangeNotifier(File file);
fileReloader.addObserver((Observable obj, Object arg) -> {
System.out.println("File changed for the " + arg + " time.");
});
Если вы ищете то же в Scala с использованием получше-файлы-акка библиотеки, у меня comeup с этим обойти на основе решения, предложенного в принято отвечать.
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
}
}
}
Я попытался это и это'ы прекрасно работают :
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static java.nio.file.StandardWatchEventKinds.*;
public class FileWatcher implements Runnable, AutoCloseable {
private final WatchService service;
private final Map<path, WatchTarget> watchTargets = new HashMap<>();
private final List<FileListener> fileListeners = new CopyOnWriteArrayList<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock r = lock.readLock();
private final Lock w = lock.writeLock();
private final AtomicBoolean running = new AtomicBoolean(false);
public FileWatcher() throws IOException {
service = FileSystems.getDefault().newWatchService();
}
@Override
public void run() {
if (running.compareAndSet(false, true)) {
while (running.get()) {
WatchKey key;
try {
key = service.take();
} catch (Throwable e) {
break;
}
if (key.isValid()) {
r.lock();
try {
key.pollEvents().stream()
.filter(e -> e.kind() != OVERFLOW)
.forEach(e -> watchTargets.values().stream()
.filter(t -> t.isInterested(e))
.forEach(t -> fireOnEvent(t.path, e.kind())));
} finally {
r.unlock();
}
if (!key.reset()) {
break;
}
}
}
running.set(false);
}
}
public boolean registerPath(Path path, boolean updateIfExists, WatchEvent.Kind... eventKinds) {
w.lock();
try {
WatchTarget target = watchTargets.get(path);
if (!updateIfExists && target != null) {
return false;
}
Path parent = path.getParent();
if (parent != null) {
if (target == null) {
watchTargets.put(path, new WatchTarget(path, eventKinds));
parent.register(service, eventKinds);
} else {
target.setEventKinds(eventKinds);
}
return true;
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
w.unlock();
}
return false;
}
public void addFileListener(FileListener fileListener) {
fileListeners.add(fileListener);
}
public void removeFileListener(FileListener fileListener) {
fileListeners.remove(fileListener);
}
private void fireOnEvent(Path path, WatchEvent.Kind eventKind) {
for (FileListener fileListener : fileListeners) {
fileListener.onEvent(path, eventKind);
}
}
public boolean isRunning() {
return running.get();
}
@Override
public void close() throws IOException {
running.set(false);
w.lock();
try {
service.close();
} finally {
w.unlock();
}
}
private final class WatchTarget {
private final Path path;
private final Path fileName;
private final Set<String> eventNames = new HashSet<>();
private final Event lastEvent = new Event();
private WatchTarget(Path path, WatchEvent.Kind[] eventKinds) {
this.path = path;
this.fileName = path.getFileName();
setEventKinds(eventKinds);
}
private void setEventKinds(WatchEvent.Kind[] eventKinds) {
eventNames.clear();
for (WatchEvent.Kind k : eventKinds) {
eventNames.add(k.name());
}
}
private boolean isInterested(WatchEvent e) {
long now = System.currentTimeMillis();
String name = e.kind().name();
if (e.context().equals(fileName) && eventNames.contains(name)) {
if (lastEvent.name == null || !lastEvent.name.equals(name) || now - lastEvent.when > 100) {
lastEvent.name = name;
lastEvent.when = now;
return true;
}
}
return false;
}
@Override
public int hashCode() {
return path.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj == this || obj != null && obj instanceof WatchTarget && Objects.equals(path, ((WatchTarget) obj).path);
}
}
private final class Event {
private String name;
private long when;
}
public static void main(String[] args) throws IOException, InterruptedException {
FileWatcher watcher = new FileWatcher();
if (watcher.registerPath(Paths.get("filename"), false, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE)) {
watcher.addFileListener((path, eventKind) -> System.out.println(path + " -> " + eventKind.name()));
new Thread(watcher).start();
System.in.read();
}
watcher.close();
System.exit(0);
}
}
FileListener :
import java.nio.file.Path;
import java.nio.file.WatchEvent;
public interface FileListener {
void onEvent(Path path, WatchEvent.Kind eventKind);
}
У меня были похожие проблемы. Я знаю, что это поздно, но это может помочь кому-то.
Мне просто нужно, чтобы исключить повторяющиеся ENTRY_MODIFY
.
Всякий раз, когда ENTRY_MODIFY срабатывает, граф (возвращает)2 или 1. Если это 1, то там будет еще одно мероприятие с
графа()` 1.
Так просто поставить глобальный счетчик, который ведет подсчет возвращаемых значений и проводить операции только тогда, когда счетчик становится 2. Что-то вроде этого можно сделать:
WatchEvent event;
int count = 0;
if(event.count() == 2)
count = 2;
if(event.count() == 1)
count++;
if(count == 2){
//your operations here
count = 0;
}
/**
*
*
* 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
*
*
*
* 在windows环境下创建一个文件会产生1个创建事件+多个修改事件, 这个方法用于合并重复事件
* 合并优先级为 删除 > 更新 > 创建
*
*
* @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());
}
Непроверенные, но, возможно, это будет работать:
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();
}