WatchService звучал как захватывающая идея ... к сожалению, он кажется таким же низкоуровневым, как предупреждалось в учебнике/api плюс не очень вписывается в модель событий Swing (или я упускаю что-то очевидное, вероятность не нулевая
Взяв код из примера WatchDir в учебнике (просто переработанный для обработки только одного каталога), Я в основном закончил тем, что
обрабатываем куски, запуская propertyChangeEvents с удаленными/созданными файлами в качестве newValue
@SuppressWarnings("unchecked")
public class FileWorker extends SwingWorker> {
public static final String DELETED = "deletedFile";
public static final String CREATED = "createdFile";
private Path directory;
private WatchService watcher;
public FileWorker(File file) throws IOException {
directory = file.toPath();
watcher = FileSystems.getDefault().newWatchService();
directory.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
}
@Override
protected Void doInBackground() throws Exception {
for (;;) {
// ожидание сигнала о нажатии клавиши
WatchKey ключ;
try {
key = watcher.take();
} catch (InterruptedException x) {
return null;
}
for (WatchEvent> event : key.pollEvents()) {
WatchEvent.Kind> kind = event.kind();
// TBD - пример обработки события OVERFLOW
if (kind == OVERFLOW) {
продолжаем;
}
publish((WatchEvent) event);
}
// сброс ключа возвращается, если каталог больше не доступен
boolean valid = key.reset();
if (!valid) {
break;
}
}
return null;
}
@Override
protected void process(List> chunks) {
super.process(chunks);
for (WatchEvent event : chunks) {
WatchEvent.Kind> kind = event.kind();
Path name = event.context();
Path child = directory.resolve(name);
File file = child.toFile();
if (StandardWatchEventKinds.ENTRY_DELETE == kind) {
firePropertyChange(DELETED, null, file);
} else if (StandardWatchEventKinds.ENTRY_CREATE == kind) {
firePropertyChange(CREATED, null, file);
}
}
}
}
Основная идея заключается в том, чтобы сделать использующий код блаженно неосведомленным о тонких деталях: он слушает изменения свойств и f.i. обновляет произвольные модели по мере необходимости:
String testDir = "D:\\scans\\library";
File directory = new File(testDir);
final DefaultListModel model = new DefaultListModel();
for (File file : directory.listFiles()) {
model.addElement(file);
}
final FileWorker worker = new FileWorker(directory);
PropertyChangeListener l = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (FileWorker.DELETED == evt.getPropertyName()) {
model.removeElement(evt.getNewValue());
} else if (FileWorker.CREATED == evt.getPropertyName()) {
model.addElement((File) evt.getNewValue());
}
}
};
worker.addPropertyChangeListener(l);
JXList list = new JXList(model);
Вроде бы работает, но я чувствую себя неловко
[A] Отредактировано (вызвано комментарием @trashgods) - на самом деле мне нужно передавать не ключ вместе с событием, а директорию, в которой он сообщает об изменениях. Соответственно изменил вопрос
К вашему сведению, этот вопрос перепостнут на форум OTN swing
Дополнение
Читаем api doc WatchKey:
Если есть несколько потоков, извлекающих сигнальные ключи из службы WatchKey, то следует позаботиться о том, чтобы в них не было ошибок. тогда необходимо позаботиться о том, чтобы метод сброса был вызывался только после обработки событий для объекта.
похоже, подразумевается, что события должны
Не совсем уверен, но в сочетании с (будущим) требованием рекурсивного просмотра каталогов (более одного) решил последовать совету @Eels, вроде как - скоро выложу код, на котором остановился
EDIT только что принял свой собственный ответ - смиренно верну его, если у кого-то есть разумные возражения