@@ -34,245 +34,293 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil ;
/**
* 路径监听器<br>
* 路径监听器
*
* <p>
* 监听器可监听目录或文件<br>
* 如果监听的Path不存在, 则递归创建空目录然后监听此空目录<br>
* 递归监听目录时,并不会监听新创建的目录
*
* @author Looly
*
* @author Looly
*/
public class WatchMonitor extends Thread implements Closeable , Serializable {
public class WatchMonitor extends Thread implements Closeable , Serializable {
private static final long serialVersionUID = 1L ;
/** 事件丢失 */
/**
* 事件丢失
*/
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 = { //
OVERFLOW , //事件丢失
OVERFLOW , //事件丢失
ENTRY_MODIFY , //修改
ENTRY_CREATE , //创建
ENTRY_DELETE //删除
} ;
/** 监听路径,必须为目录 */
/**
* 监听路径,必须为目录
*/
private Path path ;
/** 递归目录的最大深度, 当小于1时不递归下层目录 */
/**
* 递归目录的最大深度, 当小于1时不递归下层目录
*/
private int maxDepth ;
/** 监听的文件,对于单文件监听不为空 */
/**
* 监听的文件,对于单文件监听不为空
*/
private Path filePath ;
/** 监听服务 */
/**
* 监听服务
*/
private WatchService watchService ;
/** 监听器 */
/**
* 监听器
*/
private Watcher watcher ;
/** 监听事件列表 */
/**
* 监听事件列表
*/
private WatchEvent . Kind < ? > [ ] events ;
/** 监听是否已经关闭 */
/**
* 监听是否已经关闭
*/
private boolean isClosed ;
/** WatchKey 和 Path的对应表 */
/**
* WatchKey 和 Path的对应表
*/
private Map < WatchKey , Path > watchKeyPathMap = new HashMap < > ( ) ;
//------------------------------------------------------ Static method start
/**
* 创建并初始化监听
* @param url URL
*
* @param url URL
* @param events 监听的事件列表
* @return 监听对象
*/
public static WatchMonitor create ( URL url , WatchEvent . Kind < ? > . . . events ) {
public static WatchMonitor create ( URL url , WatchEvent . Kind < ? > . . . events ) {
return create ( url , 0 , events ) ;
}
/**
* 创建并初始化监听
* @param url URL
* @param events 监听的事件列表
*
* @param url URL
* @param events 监听的事件列表
* @param maxDepth 当监听目录时, 监听目录的最大深度, 当设置值为1( 或小于1) 时, 表示不递归监听子目录
* @return 监听对象
*/
public static WatchMonitor create ( URL url , int maxDepth , WatchEvent . Kind < ? > . . . events ) {
public static WatchMonitor create ( URL url , int maxDepth , WatchEvent . Kind < ? > . . . events ) {
return create ( URLUtil . toURI ( url ) , maxDepth , events ) ;
}
/**
* 创建并初始化监听
* @param uri URI
*
* @param uri URI
* @param events 监听的事件列表
* @return 监听对象
*/
public static WatchMonitor create ( URI uri , WatchEvent . Kind < ? > . . . events ) {
public static WatchMonitor create ( URI uri , WatchEvent . Kind < ? > . . . events ) {
return create ( uri , 0 , events ) ;
}
/**
* 创建并初始化监听
* @param uri URI
* @param events 监听的事件列表
*
* @param uri URI
* @param events 监听的事件列表
* @param maxDepth 当监听目录时, 监听目录的最大深度, 当设置值为1( 或小于1) 时, 表示不递归监听子目录
* @return 监听对象
*/
public static WatchMonitor create ( URI uri , int maxDepth , WatchEvent . Kind < ? > . . . events ) {
public static WatchMonitor create ( URI uri , int maxDepth , WatchEvent . Kind < ? > . . . events ) {
return create ( Paths . get ( uri ) , maxDepth , events ) ;
}
/**
* 创建并初始化监听
* @param file 文件
*
* @param file 文件
* @param events 监听的事件列表
* @return 监听对象
*/
public static WatchMonitor create ( File file , WatchEvent . Kind < ? > . . . events ) {
public static WatchMonitor create ( File file , WatchEvent . Kind < ? > . . . events ) {
return create ( file , 0 , events ) ;
}
/**
* 创建并初始化监听
* @param file 文件
* @param events 监听的事件列表
*
* @param file 文件
* @param events 监听的事件列表
* @param maxDepth 当监听目录时, 监听目录的最大深度, 当设置值为1( 或小于1) 时, 表示不递归监听子目录
* @return 监听对象
*/
public static WatchMonitor create ( File file , int maxDepth , WatchEvent . Kind < ? > . . . events ) {
public static WatchMonitor create ( File file , int maxDepth , WatchEvent . Kind < ? > . . . events ) {
return create ( file . toPath ( ) , maxDepth , events ) ;
}
/**
* 创建并初始化监听
* @param path 路径
*
* @param path 路径
* @param events 监听的事件列表
* @return 监听对象
*/
public static WatchMonitor create ( String path , WatchEvent . Kind < ? > . . . events ) {
public static WatchMonitor create ( String path , WatchEvent . Kind < ? > . . . events ) {
return create ( path , 0 , events ) ;
}
/**
* 创建并初始化监听
* @param path 路径
* @param events 监听的事件列表
*
* @param path 路径
* @param events 监听的事件列表
* @param maxDepth 当监听目录时, 监听目录的最大深度, 当设置值为1( 或小于1) 时, 表示不递归监听子目录
* @return 监听对象
*/
public static WatchMonitor create ( String path , int maxDepth , WatchEvent . Kind < ? > . . . events ) {
public static WatchMonitor create ( String path , int maxDepth , WatchEvent . Kind < ? > . . . events ) {
return create ( Paths . get ( path ) , maxDepth , events ) ;
}
/**
* 创建并初始化监听
* @param path 路径
*
* @param path 路径
* @param events 监听事件列表
* @return 监听对象
*/
public static WatchMonitor create ( Path path , WatchEvent . Kind < ? > . . . events ) {
public static WatchMonitor create ( Path path , WatchEvent . Kind < ? > . . . events ) {
return create ( path , 0 , events ) ;
}
/**
* 创建并初始化监听
* @param path 路径
* @param events 监听事件列表
*
* @param path 路径
* @param events 监听事件列表
* @param maxDepth 当监听目录时, 监听目录的最大深度, 当设置值为1( 或小于1) 时, 表示不递归监听子目录
* @return 监听对象
*/
public static WatchMonitor create ( Path path , int maxDepth , WatchEvent . Kind < ? > . . . events ) {
public static WatchMonitor create ( Path path , int maxDepth , WatchEvent . Kind < ? > . . . events ) {
return new WatchMonitor ( path , maxDepth , events ) ;
}
//--------- createAll
/**
* 创建并初始化监听,监听所有事件
* @param uri URI
*
* @param uri URI
* @param watcher {@link Watcher}
* @return {@link WatchMonitor}
*/
public static WatchMonitor createAll ( URI uri , Watcher watcher ) {
public static WatchMonitor createAll ( URI uri , Watcher watcher ) {
return createAll ( Paths . get ( uri ) , watcher ) ;
}
/**
* 创建并初始化监听,监听所有事件
* @param url URL
*
* @param url URL
* @param watcher {@link Watcher}
* @return {@link WatchMonitor}
*/
public static WatchMonitor createAll ( URL url , Watcher watcher ) {
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 file 被监听文件
* @param watcher {@link Watcher}
* @return {@link WatchMonitor}
*/
public static WatchMonitor createAll ( File file , Watcher watcher ) {
public static WatchMonitor createAll ( File file , Watcher watcher ) {
return createAll ( file . toPath ( ) , watcher ) ;
}
/**
* 创建并初始化监听,监听所有事件
* @param path 路径
*
* @param path 路径
* @param watcher {@link Watcher}
* @return {@link WatchMonitor}
*/
public static WatchMonitor createAll ( String path , Watcher watcher ) {
public static WatchMonitor createAll ( String path , Watcher watcher ) {
return createAll ( Paths . get ( path ) , watcher ) ;
}
/**
* 创建并初始化监听,监听所有事件
* @param path 路径
*
* @param path 路径
* @param watcher {@link Watcher}
* @return {@link WatchMonitor}
*/
public static WatchMonitor createAll ( Path path , Watcher watcher ) {
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 file 文件
* @param events 监听的事件列表
*/
public WatchMonitor ( File file , WatchEvent . Kind < ? > . . . events ) {
this ( file . toPath ( ) , events ) ;
}
/**
* 构造
* @param path 字符串路径
*
* @param path 字符串路径
* @param events 监听的事件列表
*/
public WatchMonitor ( String path , WatchEvent . Kind < ? > . . . events ) {
this ( Paths . get ( path ) , events ) ;
}
/**
* 构造
* @param path 字符串路径
*
* @param path 字符串路径
* @param events 监听事件列表
*/
public WatchMonitor ( Path path , WatchEvent . Kind < ? > . . . events ) {
this ( path , 0 , events ) ;
}
/**
* 构造<br>
* 例如设置:
@@ -281,10 +329,10 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
* maxDepth = 2 表示监听当前目录以及下层目录
* maxDepth = 3 表示监听当前目录以及下层
* </pre>
*
* @param path 字符串路径
*
* @param path 字符串路径
* @param maxDepth 递归目录的最大深度, 当小于2时不递归下层目录
* @param events 监听事件列表
* @param events 监听事件列表
*/
public WatchMonitor ( Path path , int maxDepth , WatchEvent . Kind < ? > . . . events ) {
this . path = path ;
@@ -293,7 +341,7 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
this . init ( ) ;
}
//------------------------------------------------------ Constructor method end
/**
* 初始化<br>
* 初始化包括:
@@ -301,111 +349,89 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
* 1、解析传入的路径, 判断其为目录还是文件
* 2、创建{@link WatchService} 对象
* </pre>
*
*
* @throws WatchException 监听异常, IO异常时抛出此异常
*/
public void init ( ) throws WatchException {
public void init ( ) throws WatchException {
//获取目录或文件路径
if ( false = = Files . exists ( this . path , LinkOption . NOFOLLOW_LINKS ) ) {
if ( false = = Files . exists ( this . path , LinkOption . NOFOLLOW_LINKS ) ) {
// 不存在的路径
final Path lastPathEle = FileUtil . getLastPathEle ( this . path ) ;
if ( null ! = lastPathEle ) {
if ( null ! = lastPathEle ) {
final String lastPathEleStr = lastPathEle . toString ( ) ;
//带有点表示有扩展名, 按照未创建的文件对待。Linux下.d的为目录, 排除之
if ( StrUtil . contains ( lastPathEleStr , StrUtil . C_DOT ) & & false = = StrUtil . endWithIgnoreCase ( lastPathEleStr , " .d " ) ) {
if ( StrUtil . contains ( lastPathEleStr , StrUtil . C_DOT ) & & false = = StrUtil . endWithIgnoreCase ( lastPathEleStr , " .d " ) ) {
this . filePath = this . path ;
this . path = this . filePath . getParent ( ) ;
}
}
//创建不存在的目录或父目录
try {
Files . createDirectories ( this . path ) ;
} catch ( IOException e ) {
throw new IORuntimeException ( e ) ;
}
} else if ( Files . isRegularFile ( this . path , LinkOption . NOFOLLOW_LINKS ) ) {
} else if ( Files . isRegularFile ( this . path , LinkOption . NOFOLLOW_LINKS ) ) {
// 文件路径
this . filePath = this . path ;
this . path = this . filePath . getParent ( ) ;
}
//初始化监听
try {
watchService = FileSystems . getDefault ( ) . newWatchService ( ) ;
} catch ( IOException e ) {
throw new WatchException ( e ) ;
}
isClosed = false ;
}
/**
* 设置监听<br>
* 多个监听请使用{@link WatcherChain}
*
*
* @param watcher 监听
* @return {@link WatchMonitor}
*/
public WatchMonitor setWatcher ( Watcher watcher ) {
public WatchMonitor setWatcher ( Watcher watcher ) {
this . watcher = watcher ;
return this ;
}
@Override
public void run ( ) {
watch ( ) ;
}
/**
* 开始监听事件,阻塞当前进程
*/
public void watch ( ) {
public void watch ( ) {
watch ( this . watcher ) ;
}
/**
* 开始监听事件,阻塞当前进程
*
* @param watcher 监听
* @throws WatchException 监听异常,如果监听关闭抛出此异常
*/
public void watch ( Watcher watcher ) throws WatchException {
if ( isClosed ) {
public void watch ( Watcher watcher ) throws WatchException {
if ( isClosed ) {
throw new WatchException ( " Watch Monitor is closed ! " ) ;
}
// 按照层级注册路径及其子路径
registerPath ( ) ;
// log.debug("Start watching path: [{}]", this.path);
while ( false = = isClosed ) {
WatchKey wk ;
try {
wk = watchService . take ( ) ;
} catch ( InterruptedException | ClosedWatchServiceException e ) {
// log.warn(e);
return ;
}
final Path currentPath = watchKeyPathMap . get ( wk ) ;
WatchEvent . Kind < ? > kind ;
for ( WatchEvent < ? > event : wk . pollEvents ( ) ) {
kind = event . kind ( ) ;
if ( null ! = this . 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 , currentPath ) ;
} else if ( kind = = StandardWatchEventKinds . ENTRY_MODIFY ) {
watcher . onModify ( event , currentPath ) ;
} else if ( kind = = StandardWatchEventKinds . ENTRY_DELETE ) {
watcher . onDelete ( event , currentPath ) ;
} else if ( kind = = StandardWatchEventKinds . OVERFLOW ) {
watcher . onOverflow ( event , currentPath ) ;
}
}
wk . reset ( ) ;
doTakeAndWatch ( watcher ) ;
}
}
/**
* 当监听目录时,监听目录的最大深度<br>
* 当设置值为1( 或小于1) 时, 表示不递归监听子目录<br>
@@ -415,7 +441,7 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
* maxDepth = 2 表示监听当前目录以及下层目录
* maxDepth = 3 表示监听当前目录以及下层
* </pre>
*
*
* @param maxDepth 最大深度, 当设置值为1( 或小于1) 时, 表示不递归监听子目录, 监听所有子目录请传{@link Integer#MAX_VALUE}
* @return this
*/
@@ -423,37 +449,76 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
this . maxDepth = maxDepth ;
return this ;
}
/**
* 关闭监听
*/
@Override
public void close ( ) {
public void close ( ) {
isClosed = true ;
IoUtil . close ( watchService ) ;
}
//------------------------------------------------------ private method start
/**
* 执行事件获取并处理
*
* @param watcher {@link Watcher}
*/
private void doTakeAndWatch ( Watcher watcher ) {
WatchKey wk ;
try {
wk = watchService . take ( ) ;
} catch ( InterruptedException | ClosedWatchServiceException e ) {
// 用户中断
return ;
}
final Path currentPath = watchKeyPathMap . get ( wk ) ;
WatchEvent . Kind < ? > kind ;
for ( WatchEvent < ? > event : wk . pollEvents ( ) ) {
kind = event . kind ( ) ;
// 如果监听文件,检查当前事件是否与所监听文件关联
if ( null ! = this . 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 , currentPath ) ;
} else if ( kind = = StandardWatchEventKinds . ENTRY_MODIFY ) {
watcher . onModify ( event , currentPath ) ;
} else if ( kind = = StandardWatchEventKinds . ENTRY_DELETE ) {
watcher . onDelete ( event , currentPath ) ;
} else if ( kind = = StandardWatchEventKinds . OVERFLOW ) {
watcher . onOverflow ( event , currentPath ) ;
}
}
wk . reset ( ) ;
}
/**
* 注册监听路径
*/
private void registerPath ( ) {
registerPath ( this . path , ( null ! = this . filePath ) ? 0 : this . maxDepth ) ;
}
/**
* 将指定路径加入到监听中
* @param path 路径
*
* @param path 路径
* @param maxDepth 递归下层目录的最大深度
* @return {@link WatchKey}
*/
private void registerPath ( Path path , int maxDepth ) {
try {
final WatchKey key = path . register ( watchService, ArrayUtil . is Empty( this . events ) ? EVENTS_ALL : this . events ) ;
final WatchKey key = path . register ( this . watchService, ArrayUtil . defaultIf Empty( this . events , EVENTS_ALL ) ) ;
watchKeyPathMap . put ( key , path ) ;
if ( maxDepth > 1 ) {
if ( maxDepth > 1 ) {
//遍历所有子目录并加入监听
Files . walkFileTree ( path , EnumSet . noneOf ( FileVisitOption . class ) , maxDepth , new SimpleFileVisitor < Path > ( ) {
Files . walkFileTree ( path , EnumSet . noneOf ( FileVisitOption . class ) , maxDepth , new SimpleFileVisitor < Path > ( ) {
@Override
public FileVisitResult postVisitDirectory ( Path dir , IOException exc ) throws IOException {
registerPath ( dir , 0 ) ; //继续添加目录
@@ -462,7 +527,7 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
} ) ;
}
} catch ( IOException e ) {
if ( e instanceof AccessDeniedException ) {
if ( e instanceof AccessDeniedException ) {
//对于禁止访问的目录,跳过监听
return ;
}