Swing/WatchingDirectoryTable のバックアップ(No.3)
- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- Swing/WatchingDirectoryTable へ行く。
- 1 (2019-01-07 (月) 15:07:57)
- 2 (2019-11-12 (火) 16:43:41)
- 3 (2021-05-28 (金) 08:09:00)
- category: swing folder: WatchingDirectoryTable title: JTableに指定したディレクトリへのファイル追加、削除などを表示する tags: [JTable, File, WatchService, SecondaryLoop] author: aterai pubdate: 2019-01-07T15:06:47+09:00 description: WatchServiceを使用してディレクトリの変更を監視し、ファイルの追加削除をJTableに表示します。 image: https://drive.google.com/uc?id=1zVO0YYMG8diggVHo4BHTANeWATFiL90StA
概要
WatchService
を使用してディレクトリの変更を監視し、ファイルの追加削除をJTable
に表示します。
Screenshot
Advertisement
サンプルコード
Path dir = Paths.get(System.getProperty("java.io.tmpdir"));
SecondaryLoop loop = Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop();
Thread worker = new Thread(() -> {
try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
dir.register(watcher,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE);
append("register: " + dir);
processEvents(dir, watcher);
loop.exit();
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
});
worker.start();
if (!loop.enter()) {
append("Error");
}
// Watching a Directory for Changes (The Java™ Tutorials > Essential Classes > Basic I/O)
// https://docs.oracle.com/javase/tutorial/essential/io/notification.html
// Process all events for keys queued to the watcher
public void processEvents(Path dir, WatchService watcher) {
for (;;) {
// wait for key to be signaled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException ex) {
EventQueue.invokeLater(() -> append("Interrupted"));
return;
}
for (WatchEvent<?> event: key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
// This key is registered only for ENTRY_CREATE events,
// but an OVERFLOW event can occur regardless if events
// are lost or discarded.
if (kind == StandardWatchEventKinds.OVERFLOW) {
continue;
}
// The filename is the context of the event.
@SuppressWarnings("unchecked")
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path filename = ev.context();
Path child = dir.resolve(filename);
EventQueue.invokeLater(() -> {
append(String.format("%s: %s", kind, child));
updateTable(kind, child);
});
}
// Reset the key -- this step is critical if you want to
// receive further watch events. If the key is no longer valid,
// the directory is inaccessible so exit the loop.
boolean valid = key.reset();
if (!valid) {
break;
}
}
}
public void updateTable(WatchEvent.Kind<?> kind, Path child) {
if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
model.addPath(child);
} else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
for (int i = 0; i < model.getRowCount(); i++) {
Object value = model.getValueAt(i, 2);
String path = Objects.toString(value, "");
if (path.equals(child.toString())) {
deleteRowSet.add(i);
// model.removeRow(i);
break;
}
}
sorter.setRowFilter(new RowFilter<TableModel, Integer>() {
@Override
public boolean include(Entry<? extends TableModel, ? extends Integer> entry) {
return !deleteRowSet.contains(entry.getIdentifier());
}
});
}
}
View in GitHub: Java, Kotlin解説
上記のサンプルでは、SecondaryLoop
で作成したEDT
とは別のスレッドでWatchService
を起動し、System.getProperty("java.io.tmpdir")
で取得した一時ディレクトリの変更を監視しています。
createTempFile
ボタンで一時ファイルを作成した場合:WatchService
で更新を取得し、StandardWatchEventKinds.ENTRY_CREATE
の場合はJTable
にPath
を行として追加
JTable
に表示されている一時ファイルを表す行がJPopupMenu
から削除された場合:Files.delete(...)
メソッドで一時ディレクトリから削除- この段階では
JTable
から行の削除は実行しない
- この段階では
WatchService
でStandardWatchEventKinds.ENTRY_DELETE
を検出したらRowFilter
で削除された一時ファイルを表す行を非表示に設定DefaultTableModel#removeRow(...)
で削除すると例外が発生する場合がある?(再現できない?)
- 別アプリケーションで一時ファイルが削除された場合:
WatchService
でStandardWatchEventKinds.ENTRY_DELETE
を検出したらRowFilter
で削除された一時ファイルを表す行を非表示に設定
- このサンプルアプリケーションを終了した場合:
HierarchyListener
でパネルの破棄を検出したら、WatchService
を使用しているスレッドにinterrupt()
メソッドで割り込みInterruptedException
を発生InterruptedException
をキャッチしたらWatchService
の監視ループを抜けて、SecondaryLoop#exit()
メソッドを実行してセカンダリループも抜ける
参考リンク
Watching a Directory for Changes (The Java™ Tutorials > Essential Classes > Basic I/O)