Часовой сервис получает дескриптор файла
Я использую службу наблюдения Java в своем приложении, чтобы получать уведомления о любых изменениях файлов.
Но проблема в том, что служба часов хранит дескриптор файла родительского каталога. Например, если моя иерархия
F
-F1
-F2
Когда я регистрируюсь, смотрю сервис на F, F1 и F2. Затем, если я пытаюсь переименовать или удалить родительскую папку F, появляется сообщение об открытии файла другой программой, которая является службой наблюдения.
Я нашел эту проблему упомянутой здесь http://bugs.java.com/bugdatabase/view_bug.do;jsessionid=76a42b61021a94ffffffffa049f7587fd4149?bug_id=6972833
Также попытался модификатор File_Tree, чтобы обойти эту проблему, но это не помогло. Возможно, я не использовал модификатор File_Tree должным образом.
Ниже приведен пример кода, на котором я тестирую. Пожалуйста, проверьте правильность использования модификатора FILE_TREE в моем коде.
/**
* Example to watch a directory (or tree) for changes to files.
* This code is direct copy from https://docs.oracle.com/javase/tutorial/essential/io/examples/WatchDir.java
*/
public class FileWatcher {
private final WatchService watcher;
private final Map<WatchKey,Path> keys;
private final boolean recursive;
private boolean trace = false;
@SuppressWarnings("unchecked")
static <T> WatchEvent<T> cast(WatchEvent<?> event) {
return (WatchEvent<T>)event;
}
/**
* Register the given directory with the WatchService
*/
private void register(Path dir) throws IOException {
WatchKey key = dir.register(watcher, new WatchEvent.Kind[] { ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY, OVERFLOW },com.sun.nio.file.ExtendedWatchEventModifier.FILE_TREE);
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
{
register(dir);
return FileVisitResult.CONTINUE;
}
});
}
/**
* Creates a WatchService and registers the given directory
*/
FileWatcher(Path dir, boolean recursive) throws IOException {
this.watcher = FileSystems.getDefault().newWatchService();
this.keys = new HashMap<WatchKey,Path>();
this.recursive = recursive;
if (recursive) {
System.out.format("Scanning %s ...\n", dir);
registerAll(dir);
System.out.println("Done.");
} else {
register(dir);
}
// enable trace after initial registration
this.trace = true;
}
/**
* Process all events for keys queued to the watcher
*/
void processEvents() {
for (;;) {
// wait for key to be signalled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
return;
}
Path dir = keys.get(key);
if (dir == null) {
System.err.println("WatchKey not recognized!!");
continue;
}
for (WatchEvent<?> event: key.pollEvents()) {
WatchEvent.Kind kind = event.kind();
// TBD - provide example of how OVERFLOW event is handled
if (kind == OVERFLOW) {
continue;
}
// Context for directory entry event is the file name of entry
WatchEvent<Path> ev = cast(event);
Path name = ev.context();
Path child = dir.resolve(name);
// print out event
System.out.format("%s: %s\n", event.kind().name(), child);
// if directory is created, and watching recursively, then
// register it and its sub-directories
if (recursive && kind == ENTRY_CREATE) {
try {
if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
registerAll(child);
}
} catch (IOException x) {
// ignore to keep sample readbale
}
}
}
// reset key and remove from set if directory no longer accessible
boolean valid = key.reset();
if (!valid) {
keys.remove(key);
// all directories are inaccessible
if (keys.isEmpty()) {
break;
}
}
}
}
static void usage() {
System.err.println("usage: java WatchDir [-r] dir");
System.exit(-1);
}
public static void main(String[] args) throws IOException {
// parse arguments
if (args.length == 0 || args.length > 2) {
usage();
}
boolean recursive = false;
int dirArg = 0;
if (args[0].equals("-r")) {
if (args.length < 2) {
usage();
}
recursive = false;
dirArg++;
}
// register directory and process its events
Path dir = Paths.get(args[dirArg]);
new FileWatcher(dir, recursive).processEvents();
}
}
1 ответ
Покопайся немного в документах и найди решение для себя.
С модификатором File_Tree одна папка назначается всем папкам / директориям. Поэтому нам нужно работать с одним ключом. Поэтому я пытаюсь перерегистрировать с тем же корневым путем и затем повторно использовать ключ при каждом новом создании элемента в наблюдаемом каталоге.
основные забрать являются
- Рекурсивный способ просмотра каждой папки в каталоге больше не требуется.
- Перерегистрация по тому же пути необходима при создании новой папки под корневым путем, потому что каждый раз требуется только ключ
- Я не уверен в производительности, так как каждый раз регистрирую весь путь. вероятно, я сделаю некоторый анализ производительности.
Ниже приведена модифицированная версия моего кода. Дайте мне знать, если код не понятен
package com.sap.mcm;
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.Map;
import com.sun.nio.file.ExtendedWatchEventModifier;
/**
* Example to watch a directory (or tree) for changes to files.
* This code is direct copy from https://docs.oracle.com/javase/tutorial/essential/io/examples/WatchDir.java
*/
public class FileWatcher {
private final WatchService watcher;
private final Map<WatchKey,Path> keys;
private final boolean recursive;
private boolean trace = true;
private Path parentDir;
private WatchKey key;
@SuppressWarnings("unchecked")
static <T> WatchEvent<T> cast(WatchEvent<?> event) {
return (WatchEvent<T>)event;
}
/**
* Register the given directory with the WatchService
*/
private void register(Path dir) throws IOException {
try {
parentDir = dir;
key = parentDir.register(watcher, new WatchEvent.Kind[] { ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY, OVERFLOW }, ExtendedWatchEventModifier.FILE_TREE);
} catch (UnsupportedOperationException usoe) {
usoe.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public void reregister() throws IOException {
// WatchKey key = keys.get(path);
key.cancel();
register(parentDir);
}
/**
* 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
{
register(dir);
return FileVisitResult.CONTINUE;
}
});
}
/**
* Creates a WatchService and registers the given directory
*/
FileWatcher(Path dir, boolean recursive) throws IOException {
this.watcher = FileSystems.getDefault().newWatchService();
this.keys = new HashMap<WatchKey,Path>();
this.recursive = recursive;
if (recursive) {
System.out.format("Scanning %s ...\n", dir);
registerAll(dir);
System.out.println("Done.");
} else {
register(dir);
}
// enable trace after initial registration
this.trace = true;
}
/**
* Process all events for keys queued to the watcher
*/
void processEvents() {
for (;;) {
// wait for key to be signalled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
return;
}
// Path dir = keys.get(key);
// if (dir == null) {
// System.err.println("WatchKey not recognized!!");
// continue;
// }
if (this.key != key) {
return;
}
for (WatchEvent<?> event: key.pollEvents()) {
WatchEvent.Kind kind = event.kind();
// TBD - provide example of how OVERFLOW event is handled
if (kind == OVERFLOW) {
continue;
}
// Context for directory entry event is the file name of entry
WatchEvent<Path> ev = cast(event);
ev.context();
// print out event
System.out.format("%s: %s \n", event.kind().name(), event.context());
// if directory is created, and watching recursively, then
// register it and its sub-directories
if (kind == ENTRY_CREATE) {
try {
// if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
reregister();
// }
} catch (IOException x) {
// ignore to keep sample readbale
}
}
}
// reset key and remove from set if directory no longer accessible
boolean valid = this.key.reset();
if (!valid) {
keys.remove(key);
// all directories are inaccessible
if (keys.isEmpty()) {
break;
}
}
}
}
static void usage() {
System.err.println("usage: java WatchDir [-r] dir");
System.exit(-1);
}
public static void main(String[] args) throws IOException {
// parse arguments
if (args.length == 0 || args.length > 2) {
usage();
}
boolean recursive = false;
int dirArg = 0;
if (args[0].equals("-r")) {
if (args.length < 2) {
usage();
}
recursive = false;
dirArg++;
}
// register directory and process its events
Path dir = Paths.get(args[dirArg]);
new FileWatcher(dir, recursive).processEvents();
}
}