Java WatchService意外停止工作
我有一个递归监视服务,在应用程序运行时使用它监视目录。由于未知原因,watchservice在大约一天后停止工作。在这一点上,我可以将一个新文件添加到一个受监控的目录中,并且不会得到任何日志语句,也不会通知我的观察者 我认为Spring可能正在销毁bean,所以我在类的@pre-destroy部分添加了一个log语句,但在watchservice停止工作后,该log语句不会出现,因此bean似乎仍然存在,只是没有按预期运行。课程安排如下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
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语句(并用日志替换)后,我没有任何问题。到底发生了什么?你怎么知道的