2017-04-12 123 views
-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); 
} 

}

+0

究竟發生了什麼?你怎麼知道它停止?你是否在OS的事件處理中看到任何事件? – bestprogrammerintheworld

+0

我在這個問題中增加了更多細節。基本上我沒有得到日誌輸出,我的觀察者也沒有像往常那樣得到通知。 – NRahm

+1

你怎麼知道程序實際停止了呢? :-) – bestprogrammerintheworld

回答

0

看來,錯誤是我的例外處理。刪除throw語句(並用日誌替換它們)後,我沒有任何問題。