add WatchServer

This commit is contained in:
Looly 2019-12-05 17:28:58 +08:00
parent 41b578391b
commit 9029b10a52
4 changed files with 237 additions and 6 deletions

View File

@ -776,6 +776,7 @@ public class CollUtil {
} else if (collectionType.isAssignableFrom(LinkedHashSet.class)) {
list = new LinkedHashSet<>();
} else if (collectionType.isAssignableFrom(TreeSet.class)) {
//noinspection SortedCollectionWithNonComparableKeys
list = new TreeSet<>();
} else if (collectionType.isAssignableFrom(EnumSet.class)) {
list = (Collection<T>) EnumSet.noneOf((Class<Enum>) ClassUtil.getTypeArgument(collectionType));
@ -2082,7 +2083,7 @@ public class CollUtil {
list.addAll(coll);
}
if (null != comparator) {
Collections.sort(list, comparator);
list.sort(comparator);
}
return page(pageNo, pageSize, list);
@ -2130,7 +2131,7 @@ public class CollUtil {
*/
public static <T> List<T> sort(Collection<T> collection, Comparator<? super T> comparator) {
List<T> list = new ArrayList<>(collection);
Collections.sort(list, comparator);
list.sort(comparator);
return list;
}
@ -2144,7 +2145,7 @@ public class CollUtil {
* @see Collections#sort(List, Comparator)
*/
public static <T> List<T> sort(List<T> list, Comparator<? super T> c) {
Collections.sort(list, c);
list.sort(c);
return list;
}
@ -2224,7 +2225,7 @@ public class CollUtil {
*/
public static <K, V> LinkedHashMap<K, V> sortToMap(Collection<Map.Entry<K, V>> entryCollection, Comparator<Map.Entry<K, V>> comparator) {
List<Map.Entry<K, V>> list = new LinkedList<>(entryCollection);
Collections.sort(list, comparator);
list.sort(comparator);
LinkedHashMap<K, V> result = new LinkedHashMap<>();
for (Map.Entry<K, V> entry : list) {
@ -2258,7 +2259,7 @@ public class CollUtil {
@SuppressWarnings({"unchecked", "rawtypes"})
public static <K, V> List<Entry<K, V>> sortEntryToList(Collection<Entry<K, V>> collection) {
List<Entry<K, V>> list = new LinkedList<>(collection);
Collections.sort(list, (o1, o2) -> {
list.sort((o1, o2) -> {
V v1 = o1.getValue();
V v2 = o2.getValue();

View File

@ -0,0 +1,57 @@
package cn.hutool.core.io.watch;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
/**
* 监听事件类型枚举包括
*
* <pre>
* 1. 事件丢失 OVERFLOW -StandardWatchEventKinds.OVERFLOW
* 2. 修改事件 MODIFY -StandardWatchEventKinds.ENTRY_MODIFY
* 3. 创建事件 CREATE -StandardWatchEventKinds.ENTRY_CREATE
* 4. 删除事件 DELETE -StandardWatchEventKinds.ENTRY_DELETE
* </pre>
*
* @author loolly
* @since 5.1.0
*/
public enum WatchKind {
/**
* 事件丢失
*/
OVERFLOW(StandardWatchEventKinds.OVERFLOW),
/**
* 修改事件
*/
MODIFY(StandardWatchEventKinds.ENTRY_MODIFY),
/**
* 创建事件
*/
CREATE(StandardWatchEventKinds.ENTRY_CREATE),
/**
* 删除事件
*/
DELETE(StandardWatchEventKinds.ENTRY_DELETE);
private WatchEvent.Kind<?> value;
/**
* 构造
*
* @param value 事件类型
*/
WatchKind(WatchEvent.Kind<?> value) {
this.value = value;
}
/**
* 获取枚举对应的事件类型
*
* @return 事件类型值
*/
public WatchEvent.Kind<?> getValue() {
return this.value;
}
}

View File

@ -318,7 +318,7 @@ public class WatchMonitor extends Thread implements Closeable, Serializable {
* <pre>
* maxDepth &lt;= 1 表示只监听当前目录
* maxDepth = 2 表示监听当前目录以及下层目录
* maxDepth = 3 表示监听当前目录以及下
* maxDepth = 3 表示监听当前目录以及下
* </pre>
*
* @param path 字符串路径

View File

@ -0,0 +1,173 @@
package cn.hutool.core.io.watch;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Filter;
import cn.hutool.core.util.ArrayUtil;
import java.io.Closeable;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.AccessDeniedException;
import java.nio.file.ClosedWatchServiceException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
/**
* 文件监听服务此服务可以同时监听多个路径
*
* @author loolly
* @since 5.1.0
*/
public class WatchServer extends Thread implements Closeable, Serializable {
private static final long serialVersionUID = 1L;
/**
* 监听服务
*/
private WatchService watchService;
/**
* 监听事件列表
*/
private WatchEvent.Kind<?>[] events;
/**
* 监听选项例如监听频率等
*/
private WatchEvent.Modifier[] modifiers;
/**
* 监听是否已经关闭
*/
private boolean isClosed;
/**
* WatchKey Path的对应表
*/
private Map<WatchKey, Path> watchKeyPathMap = new HashMap<>();
/**
* 初始化<br>
* 初始化包括
* <pre>
* 1解析传入的路径判断其为目录还是文件
* 2创建{@link WatchService} 对象
* </pre>
*
* @throws WatchException 监听异常IO异常时抛出此异常
*/
public void init() throws WatchException {
//初始化监听
try {
watchService = FileSystems.getDefault().newWatchService();
} catch (IOException e) {
throw new WatchException(e);
}
isClosed = false;
}
/**
* 设置监听选项例如监听频率等可设置项包括
*
* <pre>
* 1com.sun.nio.file.StandardWatchEventKinds
* 2com.sun.nio.file.SensitivityWatchEventModifier
* </pre>
*
* @param modifiers 监听选项例如监听频率等
*/
public void setModifiers(WatchEvent.Modifier[] modifiers) {
this.modifiers = modifiers;
}
/**
* 将指定路径加入到监听中
*
* @param path 路径
* @param maxDepth 递归下层目录的最大深度
*/
public void registerPath(Path path, int maxDepth) {
try {
final WatchKey key;
if (ArrayUtil.isEmpty(this.modifiers)) {
key = path.register(this.watchService, this.events);
} else {
key = path.register(this.watchService, this.events, this.modifiers);
}
watchKeyPathMap.put(key, path);
// 递归注册下一层层级的目录
if (maxDepth > 1) {
//遍历所有子目录并加入监听
Files.walkFileTree(path, EnumSet.noneOf(FileVisitOption.class), maxDepth, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
registerPath(dir, 0);//继续添加目录
return super.postVisitDirectory(dir, exc);
}
});
}
} catch (IOException e) {
if (false == (e instanceof AccessDeniedException)) {
throw new WatchException(e);
}
//对于禁止访问的目录跳过监听
}
}
/**
* 执行事件获取并处理
*
* @param watcher {@link Watcher}
* @param watchFilter 监听过滤接口通过实现此接口过滤掉不需要监听的情况null表示不过滤
*/
public void watch(Watcher watcher, Filter<WatchEvent<?>> watchFilter) {
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 != watchFilter && false == watchFilter.accept(event)) {
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();
}
/**
* 关闭监听
*/
@Override
public void close() {
isClosed = true;
IoUtil.close(watchService);
}
}