-1
我有一个递归监视服务,用于在应用程序运行时监视目录。由于不明原因,大约一天后,watchservice显示停止工作。此时,我可以将新文件添加到受监视的目录,并且不会收到日志语句,我的观察者也不会收到通知。Java WatchService意外停止工作
我以为Spring可能会销毁bean,所以我在类的@ pre-destroy部分添加了一条日志语句,但是watchservice停止工作后该日志语句不会显示出来,所以看起来bean仍然存在,它只是没有按预期运作。类是如下
import com.sun.nio.file.SensitivityWatchEventModifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
@Service
public class DirectoryMonitor {
private static final Logger logger = LoggerFactory.getLogger(DirectoryMonitor.class);
private WatchService watcher;
private ExecutorService executor;
private List<DirectoryMonitorObserver> observerList = new ArrayList<>();
private final Map<WatchKey, Path> keys = new HashMap<>();
public void addObserver(DirectoryMonitorObserver observer){
observerList.add(observer);
}
private void notifyObservers(){
observerList.forEach(DirectoryMonitorObserver::directoryModified);
}
@PostConstruct
public void init() throws IOException {
watcher = FileSystems.getDefault().newWatchService();
executor = Executors.newSingleThreadExecutor();
}
@PreDestroy
public void cleanup() {
try {
logger.info("Stopping directory monitor");
watcher.close();
} catch (IOException e) {
logger.error("Error closing watcher service", e);
}
executor.shutdown();
}
@SuppressWarnings("unchecked")
public void startRecursiveWatcher(String pathToMonitor) {
logger.info("Starting Recursive Watcher");
Consumer<Path> register = p -> {
if (!p.toFile().exists() || !p.toFile().isDirectory())
throw new RuntimeException("folder " + p + " does not exist or is not a directory");
try {
Files.walkFileTree(p, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
logger.info("registering " + dir + " in watcher service");
WatchKey watchKey = dir.register(watcher, new WatchEvent.Kind[]{ENTRY_CREATE, ENTRY_DELETE}, SensitivityWatchEventModifier.HIGH);
keys.put(watchKey, dir);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
throw new RuntimeException("Error registering path " + p);
}
};
register.accept(Paths.get(pathToMonitor));
executor.submit(() -> {
while (true) {
final WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException ex) {
logger.error(ex.toString());
continue;
}
final Path dir = keys.get(key);
key.pollEvents().stream()
.map(e -> ((WatchEvent<Path>) e).context())
.forEach(p -> {
final Path absPath = dir.resolve(p);
if (absPath.toFile().isDirectory()) {
register.accept(absPath);
} else {
final File f = absPath.toFile();
logger.info("Detected new file " + f.getAbsolutePath());
}
});
notifyObservers();
key.reset();
}
});
}
}
这是我创建的监视器豆..
@Component
public class MovieInfoFacade {
@Value("${media.path}")
private String mediaPath;
private MovieInfoControl movieInfoControl;
private DirectoryMonitor directoryMonitor;
private FileListProvider fileListProvider;
@Autowired
public MovieInfoFacade(MovieInfoControl movieInfoControl, DirectoryMonitor directoryMonitor, FileListProvider fileListProvider){
this.movieInfoControl = movieInfoControl;
this.directoryMonitor = directoryMonitor;
this.fileListProvider = fileListProvider;
}
@PostConstruct
public void startDirectoryMonitor(){
if(!mediaPath.equalsIgnoreCase("none")) {
directoryMonitor.addObserver(fileListProvider);
directoryMonitor.startRecursiveWatcher(mediaPath);
}
}
public int loadMovieListLength(String directoryPath){
return fileListProvider.listFiles(directoryPath).length;
}
public List<MovieInfo> loadMovieList(MovieSearchCriteria searchCriteria) {
List<File> files = Arrays.asList(fileListProvider.listFiles(searchCriteria.getPath()));
return files.parallelStream()
.sorted()
.skip(searchCriteria.getPage() * searchCriteria.getItemsPerPage())
.limit(searchCriteria.getItemsPerPage())
.map(file -> movieInfoControl.loadMovieInfoFromCache(file.getAbsolutePath()))
.collect(Collectors.toList());
}
public MovieInfo loadSingleMovie(String filePath) {
return movieInfoControl.loadMovieInfoFromCache(filePath);
}
}
究竟发生了什么?你怎么知道它停止?你是否在OS的事件处理中看到任何事件? – bestprogrammerintheworld
我在这个问题中增加了更多细节。基本上我没有得到日志输出,我的观察者也没有像往常那样得到通知。 – NRahm
你怎么知道程序实际停止了呢? :-) – bestprogrammerintheworld