package com.xiaoleilu.hutool.io.watch;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import com.xiaoleilu.hutool.io.IoUtil;
import com.xiaoleilu.hutool.util.ArrayUtil;
/**
* 路径监听器<br>
* 监听器可监听目录或文件
*
* @author Looly
*
*/
public class WatchMonitor extends Thread implements Closeable{
// private static final Log log = LogFactory.get();
/** 事件丢失 */
public static final WatchEvent.Kind<?> OVERFLOW = StandardWatchEventKinds.OVERFLOW;
/** 修改事件 */
public static final WatchEvent.Kind<?> ENTRY_MODIFY = StandardWatchEventKinds.ENTRY_MODIFY;
/** 创建事件 */
public static final WatchEvent.Kind<?> ENTRY_CREATE = StandardWatchEventKinds.ENTRY_CREATE;
/** 删除事件 */
public static final WatchEvent.Kind<?> ENTRY_DELETE = StandardWatchEventKinds.ENTRY_DELETE;
/** 全部事件 */
public static final WatchEvent.Kind<?>[] EVENTS_ALL = {//
StandardWatchEventKinds.OVERFLOW,//事件丢失
StandardWatchEventKinds.ENTRY_MODIFY, //修改
StandardWatchEventKinds.ENTRY_CREATE, //创建
StandardWatchEventKinds.ENTRY_DELETE};//删除
/** 监听路径,必须为目录 */
private Path path;
/** 监听的文件,对于单文件监听不为空 */
private Path filePath;
/** 监听服务 */
private WatchService watchService;
/** 监听器 */
private Watcher watcher;
/** 监听事件列表 */
private WatchEvent.Kind<?>[] events;
/** 监听是否已经关闭 */
private boolean isClosed;
//------------------------------------------------------ Static method start
/**
* 创建并初始化监听
* @param uri URI
* @param events 监听的事件列表
* @return 监听对象
*/
public static WatchMonitor create(URI uri, WatchEvent.Kind<?>... events){
return create(Paths.get(uri), events);
}
/**
* 创建并初始化监听
* @param url URL
* @param events 监听的事件列表
* @return 监听对象
*/
public static WatchMonitor create(URL url, WatchEvent.Kind<?>... events){
try {
return create(Paths.get(url.toURI()), events);
} catch (URISyntaxException e) {
throw new WatchException(e);
}
}
/**
* 创建并初始化监听
* @param file 文件
* @param events 监听的事件列表
* @return 监听对象
*/
public static WatchMonitor create(File file, WatchEvent.Kind<?>... events){
return new WatchMonitor(file, events);
}
/**
* 创建并初始化监听
* @param path 路径
* @param events 监听的事件列表
* @return 监听对象
*/
public static WatchMonitor create(String path, WatchEvent.Kind<?>... events){
return new WatchMonitor(path, events);
}
/**
* 创建并初始化监听
* @param path 路径
* @param events 监听事件列表
* @return 监听对象
*/
public static WatchMonitor create(Path path, WatchEvent.Kind<?>... events){
return new WatchMonitor(path, events);
}
//--------- createAll
/**
* 创建并初始化监听,监听所有事件
* @param uri URI
* @param watcher {@link Watcher}
* @return {@link WatchMonitor}
*/
public static WatchMonitor createAll(URI uri, Watcher watcher){
return createAll(Paths.get(uri), watcher);
}
/**
* 创建并初始化监听,监听所有事件
* @param url URL
* @param watcher {@link Watcher}
* @return {@link WatchMonitor}
*/
public static WatchMonitor createAll(URL url, Watcher watcher){
try {
return createAll(Paths.get(url.toURI()), watcher);
} catch (URISyntaxException e) {
throw new WatchException(e);
}
}
/**
* 创建并初始化监听,监听所有事件
* @param file 被监听文件
* @param watcher {@link Watcher}
* @return {@link WatchMonitor}
*/
public static WatchMonitor createAll(File file, Watcher watcher){
return createAll(file.toPath(), watcher);
}
/**
* 创建并初始化监听,监听所有事件
* @param path 路径
* @param watcher {@link Watcher}
* @return {@link WatchMonitor}
*/
public static WatchMonitor createAll(String path, Watcher watcher){
return createAll(Paths.get(path), watcher);
}
/**
* 创建并初始化监听,监听所有事件
* @param path 路径
* @param watcher {@link Watcher}
* @return {@link WatchMonitor}
*/
public static WatchMonitor createAll(Path path, Watcher watcher){
final WatchMonitor watchMonitor = create(path, EVENTS_ALL);
watchMonitor.setWatcher(watcher);
return watchMonitor;
}
//------------------------------------------------------ Static method end
//------------------------------------------------------ Constructor method start
/**
* 构造
* @param file 文件
* @param events 监听的事件列表
*/
public WatchMonitor(File file, WatchEvent.Kind<?>... events) {
this(file.toPath(), events);
}
/**
* 构造
* @param path 字符串路径
* @param events 监听的事件列表
*/
public WatchMonitor(String path, WatchEvent.Kind<?>... events) {
this(Paths.get(path), events);
}
/**
* 构造
* @param path 字符串路径
* @param events 监听事件列表
*/
public WatchMonitor(Path path, WatchEvent.Kind<?>... events) {
this.path = path;
this.events = events;
this.init();
}
//------------------------------------------------------ Constructor method end
/**
* 初始化
* @throws IOException
*/
public void init(){
if(path.toFile().isFile()){
this.filePath = this.path;
this.path = this.filePath.getParent();
}
try {
watchService = FileSystems.getDefault().newWatchService();
path.register(watchService, ArrayUtil.isEmpty(this.events) ? EVENTS_ALL : this.events);
} catch (Exception e) {
throw new WatchException(e);
}
isClosed = false;
}
/**
* 设置监听
* @param watcher 监听
* @return {@link WatchMonitor}
*/
public WatchMonitor setWatcher(Watcher watcher){
this.watcher = watcher;
return this;
}
@Override
public void run() {
watch();
}
/**
* 开始监听事件,阻塞当前进程
* @throws InterruptedException
*/
public void watch(){
watch(this.watcher);
}
/**
* 开始监听事件,阻塞当前进程
* @param watcher 监听
* @throws InterruptedException
*/
public void watch(Watcher watcher){
if(isClosed){
throw new WatchException("Watch Monitor is closed !");
}
// log.debug("Start watching path: [{}]", this.path);
while (false == isClosed) {
WatchKey wk;
try {
wk = watchService.take();
} catch (InterruptedException e) {
// log.warn(e);
return;
}
WatchEvent.Kind<?> kind;
for (WatchEvent<?> event : wk.pollEvents()) {
kind = event.kind();
if(null != filePath && false == this.filePath.endsWith(event.context().toString())){
// log.debug("[{}] is not fit for [{}], pass it.", event.context(), this.filePath.getFileName());
continue;
}
if(kind == StandardWatchEventKinds.ENTRY_CREATE){
watcher.onCreate(event);
}else if(kind == StandardWatchEventKinds.ENTRY_MODIFY){
watcher.onModify(event);
}else if(kind == StandardWatchEventKinds.ENTRY_DELETE){
watcher.onDelete(event);
}else if(kind == StandardWatchEventKinds.OVERFLOW){
watcher.onOverflow(event);
}
}
wk.reset();
}
}
/**
* 关闭监听
*/
@Override
public void close(){
isClosed = true;
IoUtil.close(watchService);
}
}