Browse Source

add WatchServer

Looly 6 years ago
parent
commit
9029b10a52

+ 6 - 5
hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java

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

+ 57 - 0
hutool-core/src/main/java/cn/hutool/core/io/watch/WatchKind.java

@@ -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;
+	}
+}

+ 1 - 1
hutool-core/src/main/java/cn/hutool/core/io/watch/WatchMonitor.java

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

+ 173 - 0
hutool-core/src/main/java/cn/hutool/core/io/watch/WatchServer.java

@@ -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>
+	 * 1、com.sun.nio.file.StandardWatchEventKinds
+	 * 2、com.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);
+	}
+}