Java WatchService意外停止工作

Java WatchService意外停止工作,java,spring,java-8,watchservice,Java,Spring,Java 8,Watchservice,我有一个递归监视服务,在应用程序运行时使用它监视目录。由于未知原因,watchservice在大约一天后停止工作。在这一点上,我可以将一个新文件添加到一个受监控的目录中,并且不会得到任何日志语句,也不会通知我的观察者 我认为Spring可能正在销毁bean,所以我在类的@pre-destroy部分添加了一个log语句,但在watchservice停止工作后,该log语句不会出现,因此bean似乎仍然存在,只是没有按预期运行。课程安排如下 import com.sun.nio.file.Sensi

我有一个递归监视服务,在应用程序运行时使用它监视目录。由于未知原因,watchservice在大约一天后停止工作。在这一点上,我可以将一个新文件添加到一个受监控的目录中,并且不会得到任何日志语句,也不会通知我的观察者

我认为Spring可能正在销毁bean,所以我在类的@pre-destroy部分添加了一个log语句,但在watchservice停止工作后,该log语句不会出现,因此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();
        }
    });
}
导入com.sun.nio.file.SensitivityWatchEventModifier;
导入org.slf4j.Logger;
导入org.slf4j.LoggerFactory;
导入org.springframework.stereotype.Service;
导入java.io.File;
导入java.io.IOException;
导入java.nio.file.*;
导入java.nio.file.attribute.BasicFileAttributes;
导入java.util.ArrayList;
导入java.util.HashMap;
导入java.util.List;
导入java.util.Map;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.Executors;
导入java.util.function.Consumer;
导入javax.annotation.PostConstruct;
导入javax.annotation.PreDestroy;
导入静态java.nio.file.StandardWatchEventTypes.ENTRY\u创建;
导入静态java.nio.file.StandardWatchEventTypes.ENTRY\u DELETE;
@服务
公共类目录监视器{
私有静态最终记录器Logger=LoggerFactory.getLogger(DirectoryMonitor.class);
私人观察者;
私人遗嘱执行人;
private List observerList=new ArrayList();
私有最终映射键=新HashMap();
public void addObserver(DirectoryMonitorObserver){
添加(观察者);
}
私人观察者(){
forEach(DirectoryMonitorObserver::directoryModified);
}
@施工后
public void init()引发IOException{
watcher=FileSystems.getDefault().newWatchService();
executor=Executors.newSingleThreadExecutor();
}
@发情前期
公共空间清理(){
试一试{
info(“停止目录监视器”);
watcher.close();
}捕获(IOE异常){
记录器错误(“关闭观察者服务错误”,e);
}
executor.shutdown();
}
@抑制警告(“未选中”)
public void startRecursiveWatcher(字符串路径监视器){
logger.info(“启动递归观察程序”);
消费者注册=p->{
如果(!p.toFile().exists()| |!p.toFile().isDirectory())
抛出新的RuntimeException(“文件夹“+p+”不存在或不是目录”);
试一试{
walkFileTree(p,新的SimpleFileVisitor(){
@凌驾
公共文件VisitResult preVisitDirectory(路径目录,基本文件属性属性属性)引发IOException{
logger.info(“在watcher服务中注册“+dir+”);
WatchKey WatchKey=dir.register(watcher,newwatchevent.Kind[]{ENTRY\u CREATE,ENTRY\u DELETE},SensitivityWatchEventModifier.HIGH);
按键。put(watchKey,dir);
返回FileVisitResult.CONTINUE;
}
});
}捕获(IOE异常){
抛出新的运行时异常(“错误注册路径”+p);
}
};
register.accept(path.get(pathToMonitor));
执行人提交(()->{
while(true){
最终观察键;
试一试{
key=watcher.take();
}捕获(中断异常例外){
logger.error(例如toString());
继续;
}
最终路径dir=keys.get(key);
key.pollEvents().stream()
.map(e->((WatchEvent)e).context()
.forEach(p->{
最终路径absPath=dir.resolve(p);
if(absPath.toFile().isDirectory()){
register.accept(absPath);
}否则{
最终文件f=absPath.toFile();
logger.info(“检测到的新文件”+f.getAbsolutePath());
}
});
通知观察员();
键。重置();
}
});
}
}

这就是我创建监视器bean的地方

@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);
}
@组件
公共级电影信息门面{
@值(${media.path}”)
私有字符串中介路径;
私人电影信息控制电影信息控制;
私有目录监视器;
私有FileListProvider FileListProvider;
@自动连线
公共MovieInfoFacade(MovieInfoControl MovieInfoControl、DirectoryMonitor DirectoryMonitor、FileListProvider FileListProvider){
this.movieInfoControl=movieInfoControl;
this.directoryMonitor=directoryMonitor;
this.fileListProvider=fileListProvider;
}
@施工后
public void startDirectory监视器(){
如果(!mediaPath.equalsIgnoreCase(“无”)){
addObserver(fileListProvider);
directoryMonitor.startRecursiveWatcher(mediaPath);
}
}
public int loadMovieListLength(字符串目录路径){
返回fileListProvider.listFiles(directoryPath).length;
}
公共列表加载MovieList(MovieSearchCriteria搜索条件){
List files=Arrays.asList(fileListProvider.listFiles(searchCriteria.getPath());
返回文件.parallelStream()
.已排序()
.skip(searchCriteria.getPage()*searchCriteria.getItemsPerPage())
.limit(searchCriteria.getItemsPerPage())
.map(文件->movieInfoControl.loadMovieInfoFromCache(文件.getAbsolutePath())
.collect(Collectors.toList());
}
public MovieInfo loadSingleMovie(字符串文件路径){
返回movieInfoControl.loadMovieInfoFromCache(filePath);
}

}

似乎错误在我的异常处理中。删除throw语句(并用日志替换)后,我没有任何问题。

到底发生了什么?你怎么知道的