diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/array/ArrayUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/array/ArrayUtil.java
index b75e05fc1..01d6c8d54 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/array/ArrayUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/array/ArrayUtil.java
@@ -30,6 +30,7 @@ import org.dromara.hutool.core.util.RandomUtil;
import java.lang.reflect.Array;
import java.util.*;
import java.util.function.Function;
+import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
@@ -467,6 +468,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
// endregion
// region ----- newArray
+
/**
* 新建一个空数组
*
@@ -493,6 +495,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
// endregion
// region ----- type
+
/**
* 获取数组对象的元素类型,方法调用参数与返回结果举例:
*
@@ -1048,6 +1051,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
// endregion
// region ----- zip
+
/**
* 映射键值(参考Python的zip()函数)
* 例如:
@@ -1422,6 +1426,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
}
// region ----- join
+
/**
* 以 conjunction 为分隔符将数组转换为字符串
*
@@ -1840,6 +1845,21 @@ public class ArrayUtil extends PrimitiveArrayUtil {
public static Set mapToSet(final T[] array, final Function super T, ? extends R> func) {
return Arrays.stream(array).map(func).collect(Collectors.toSet());
}
+
+ /**
+ * 按照指定规则,将一种类型的数组元素转换为另一种类型,并保存为 {@link Set}
+ *
+ * @param array 被转换的数组
+ * @param func 转换规则函数
+ * @param generator 数组生成器,如返回String[],则传入String[]::new
+ * @param 原数组类型
+ * @param 目标数组类型
+ * @return 集合
+ */
+ public static R[] mapToArray(final T[] array, final Function super T, ? extends R> func,
+ final IntFunction generator) {
+ return Arrays.stream(array).map(func).toArray(generator);
+ }
// endregion
/**
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CastUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CastUtil.java
index 39cbebc21..309e3017f 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CastUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CastUtil.java
@@ -27,6 +27,18 @@ import java.util.Set;
*/
public class CastUtil {
+ /**
+ * 将指定对象强制转换为指定类型
+ *
+ * @param 目标类型
+ * @param value 被转换的对象
+ * @return 转换后的对象
+ */
+ @SuppressWarnings("unchecked")
+ public static T cast(final Object value) {
+ return (T)value;
+ }
+
/**
* 将指定对象强制转换为指定类型
*
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/LineReadWatcher.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/LineReadWatcher.java
index a280922a8..441ad8517 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/LineReadWatcher.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/LineReadWatcher.java
@@ -13,7 +13,7 @@
package org.dromara.hutool.core.io.file;
import org.dromara.hutool.core.io.IORuntimeException;
-import org.dromara.hutool.core.io.watch.SimpleWatcher;
+import org.dromara.hutool.core.io.watch.watchers.SimpleWatcher;
import org.dromara.hutool.core.func.SerConsumer;
import java.io.IOException;
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java
index 298efc744..dcd5a15ad 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java
@@ -12,7 +12,6 @@
package org.dromara.hutool.core.io.file;
-import org.dromara.hutool.core.collection.set.SetUtil;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.util.CharsetUtil;
@@ -115,6 +114,7 @@ public class PathUtil {
return fileList;
}
+ // region ----- walkFiles
/**
* 遍历指定path下的文件并做处理
*
@@ -162,6 +162,7 @@ public class PathUtil {
throw new IORuntimeException(e);
}
}
+ // endregion
/**
* 删除文件或者文件夹,不追踪软链
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/Tailer.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/Tailer.java
index e686d397b..b5f406703 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/Tailer.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/Tailer.java
@@ -16,9 +16,10 @@ import org.dromara.hutool.core.date.DateUnit;
import org.dromara.hutool.core.exception.HutoolException;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.io.IoUtil;
-import org.dromara.hutool.core.io.watch.SimpleWatcher;
+import org.dromara.hutool.core.io.watch.watchers.SimpleWatcher;
import org.dromara.hutool.core.io.watch.WatchKind;
import org.dromara.hutool.core.io.watch.WatchMonitor;
+import org.dromara.hutool.core.io.watch.WatchUtil;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.func.SerConsumer;
import org.dromara.hutool.core.text.CharUtil;
@@ -158,7 +159,7 @@ public class Tailer implements Serializable {
// 监听删除
if(stopOnDelete){
- fileDeleteWatchMonitor = WatchMonitor.of(this.filePath, WatchKind.DELETE.getValue());
+ fileDeleteWatchMonitor = WatchUtil.of(this.filePath, WatchKind.DELETE.getValue());
fileDeleteWatchMonitor.setWatcher(new SimpleWatcher(){
@Override
public void onDelete(final WatchEvent> event, final Path currentPath) {
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchMonitor.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchMonitor.java
index d7978e0da..d6b6575aa 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchMonitor.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchMonitor.java
@@ -15,15 +15,15 @@ package org.dromara.hutool.core.io.watch;
import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.io.file.PathUtil;
import org.dromara.hutool.core.io.watch.watchers.WatcherChain;
-import org.dromara.hutool.core.net.url.UrlUtil;
-import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.CharUtil;
+import org.dromara.hutool.core.text.StrUtil;
-import java.io.File;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.file.*;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Serializable;
+import java.nio.file.Path;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchService;
/**
* 路径监听器
@@ -35,237 +35,37 @@ import java.nio.file.*;
*
* @author Looly
*/
-public class WatchMonitor extends WatchServer {
+public class WatchMonitor extends Thread implements Closeable, Serializable {
private static final long serialVersionUID = 1L;
+ private final WatchServiceWrapper watchService;
+
/**
* 监听路径,必须为目录
*/
- private Path path;
- /**
- * 递归目录的最大深度,当小于1时不递归下层目录
- */
- private int maxDepth;
+ private Path dir;
/**
* 监听的文件,对于单文件监听不为空
*/
private Path filePath;
+ /**
+ * 递归目录的最大深度,当小于1时不递归下层目录
+ */
+ private int maxDepth;
/**
* 监听器
*/
private Watcher watcher;
- //------------------------------------------------------ Static method start
-
- /**
- * 创建并初始化监听
- *
- * @param url URL
- * @param events 监听的事件列表,见{@link WatchKind}
- * @return 监听对象
- */
- public static WatchMonitor of(final URL url, final WatchEvent.Kind>... events) {
- return of(url, 0, events);
- }
-
- /**
- * 创建并初始化监听
- *
- * @param url URL
- * @param events 监听的事件列表,见{@link WatchKind}
- * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录
- * @return 监听对象
- */
- public static WatchMonitor of(final URL url, final int maxDepth, final WatchEvent.Kind>... events) {
- return of(UrlUtil.toURI(url), maxDepth, events);
- }
-
- /**
- * 创建并初始化监听
- *
- * @param uri URI
- * @param events 监听的事件列表,见{@link WatchKind}
- * @return 监听对象
- */
- public static WatchMonitor of(final URI uri, final WatchEvent.Kind>... events) {
- return of(uri, 0, events);
- }
-
- /**
- * 创建并初始化监听
- *
- * @param uri URI
- * @param events 监听的事件列表,见{@link WatchKind}
- * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录
- * @return 监听对象
- */
- public static WatchMonitor of(final URI uri, final int maxDepth, final WatchEvent.Kind>... events) {
- return of(Paths.get(uri), maxDepth, events);
- }
-
- /**
- * 创建并初始化监听
- *
- * @param file 文件
- * @param events 监听的事件列表,见{@link WatchKind}
- * @return 监听对象
- */
- public static WatchMonitor of(final File file, final WatchEvent.Kind>... events) {
- return of(file, 0, events);
- }
-
- /**
- * 创建并初始化监听
- *
- * @param file 文件
- * @param events 监听的事件列表,见{@link WatchKind}
- * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录
- * @return 监听对象
- */
- public static WatchMonitor of(final File file, final int maxDepth, final WatchEvent.Kind>... events) {
- return of(file.toPath(), maxDepth, events);
- }
-
- /**
- * 创建并初始化监听
- *
- * @param path 路径
- * @param events 监听的事件列表,见{@link WatchKind}
- * @return 监听对象
- */
- public static WatchMonitor of(final String path, final WatchEvent.Kind>... events) {
- return of(path, 0, events);
- }
-
- /**
- * 创建并初始化监听
- *
- * @param path 路径
- * @param events 监听的事件列表,见{@link WatchKind}
- * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录
- * @return 监听对象
- */
- public static WatchMonitor of(final String path, final int maxDepth, final WatchEvent.Kind>... events) {
- return of(Paths.get(path), maxDepth, events);
- }
-
- /**
- * 创建并初始化监听
- *
- * @param path 路径
- * @param events 监听事件列表,见{@link WatchKind}
- * @return 监听对象
- */
- public static WatchMonitor of(final Path path, final WatchEvent.Kind>... events) {
- return of(path, 0, events);
- }
-
- /**
- * 创建并初始化监听
- *
- * @param path 路径
- * @param events 监听事件列表,见{@link WatchKind}
- * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录
- * @return 监听对象
- */
- public static WatchMonitor of(final Path path, final int maxDepth, final WatchEvent.Kind>... events) {
- return new WatchMonitor(path, maxDepth, events);
- }
-
- //--------- createAll
-
- /**
- * 创建并初始化监听,监听所有事件
- *
- * @param uri URI
- * @param watcher {@link Watcher}
- * @return WatchMonitor
- */
- public static WatchMonitor ofAll(final URI uri, final Watcher watcher) {
- return ofAll(Paths.get(uri), watcher);
- }
-
- /**
- * 创建并初始化监听,监听所有事件
- *
- * @param url URL
- * @param watcher {@link Watcher}
- * @return WatchMonitor
- */
- public static WatchMonitor ofAll(final URL url, final Watcher watcher) {
- try {
- return ofAll(Paths.get(url.toURI()), watcher);
- } catch (final URISyntaxException e) {
- throw new WatchException(e);
- }
- }
-
- /**
- * 创建并初始化监听,监听所有事件
- *
- * @param file 被监听文件
- * @param watcher {@link Watcher}
- * @return WatchMonitor
- */
- public static WatchMonitor ofAll(final File file, final Watcher watcher) {
- return ofAll(file.toPath(), watcher);
- }
-
- /**
- * 创建并初始化监听,监听所有事件
- *
- * @param path 路径
- * @param watcher {@link Watcher}
- * @return WatchMonitor
- */
- public static WatchMonitor ofAll(final String path, final Watcher watcher) {
- return ofAll(Paths.get(path), watcher);
- }
-
- /**
- * 创建并初始化监听,监听所有事件
- *
- * @param path 路径
- * @param watcher {@link Watcher}
- * @return WatchMonitor
- */
- public static WatchMonitor ofAll(final Path path, final Watcher watcher) {
- final WatchMonitor watchMonitor = of(path, WatchKind.ALL);
- watchMonitor.setWatcher(watcher);
- return watchMonitor;
- }
- //------------------------------------------------------ Static method end
-
- //------------------------------------------------------ Constructor method start
/**
* 构造
*
- * @param file 文件
- * @param events 监听的事件列表
- */
- public WatchMonitor(final File file, final WatchEvent.Kind>... events) {
- this(file.toPath(), events);
- }
-
- /**
- * 构造
- *
- * @param path 字符串路径
- * @param events 监听的事件列表
- */
- public WatchMonitor(final String path, final WatchEvent.Kind>... events) {
- this(Paths.get(path), events);
- }
-
- /**
- * 构造
- *
- * @param path 字符串路径
+ * @param dir 字符串路径
* @param events 监听事件列表
*/
- public WatchMonitor(final Path path, final WatchEvent.Kind>... events) {
- this(path, 0, events);
+ public WatchMonitor(final Path dir, final WatchEvent.Kind>... events) {
+ this(dir, 0, events);
}
/**
@@ -277,53 +77,16 @@ public class WatchMonitor extends WatchServer {
* maxDepth = 3 表示监听当前目录以及下两层
*
*
- * @param path 字符串路径
+ * @param dir 字符串路径
* @param maxDepth 递归目录的最大深度,当小于2时不递归下层目录
* @param events 监听事件列表
*/
- public WatchMonitor(final Path path, final int maxDepth, final WatchEvent.Kind>... events) {
- this.path = path;
+ public WatchMonitor(final Path dir, final int maxDepth, final WatchEvent.Kind>... events) {
+ this.watchService = new WatchServiceWrapper().setEvents(events);
+ this.dir = dir;
this.maxDepth = maxDepth;
- this.events = events;
this.init();
}
- //------------------------------------------------------ Constructor method end
-
- /**
- * 初始化
- * 初始化包括:
- *
- * 1、解析传入的路径,判断其为目录还是文件
- * 2、创建{@link WatchService} 对象
- *
- *
- * @throws WatchException 监听异常,IO异常时抛出此异常
- */
- @Override
- public void init() throws WatchException {
- //获取目录或文件路径
- if (!PathUtil.exists(this.path, false)) {
- // 不存在的路径
- final Path lastPathEle = FileUtil.getLastPathEle(this.path);
- if (null != lastPathEle) {
- final String lastPathEleStr = lastPathEle.toString();
- //带有点表示有扩展名,按照未创建的文件对待。Linux下.d的为目录,排除之
- if (StrUtil.contains(lastPathEleStr, CharUtil.DOT) && !StrUtil.endWithIgnoreCase(lastPathEleStr, ".d")) {
- this.filePath = this.path;
- this.path = this.filePath.getParent();
- }
- }
-
- //创建不存在的目录或父目录
- PathUtil.mkdir(this.path);
- } else if (PathUtil.isFile(this.path, false)) {
- // 文件路径
- this.filePath = this.path;
- this.path = this.filePath.getParent();
- }
-
- super.init();
- }
/**
* 设置监听
@@ -356,7 +119,7 @@ public class WatchMonitor extends WatchServer {
* @throws WatchException 监听异常,如果监听关闭抛出此异常
*/
public void watch(final Watcher watcher) throws WatchException {
- if (isClosed) {
+ if (this.watchService.isClosed()) {
throw new WatchException("Watch Monitor is closed !");
}
@@ -364,7 +127,7 @@ public class WatchMonitor extends WatchServer {
registerPath();
// log.debug("Start watching path: [{}]", this.path);
- while (!isClosed) {
+ while (!this.watchService.isClosed()) {
doTakeAndWatch(watcher);
}
}
@@ -389,20 +152,58 @@ public class WatchMonitor extends WatchServer {
//------------------------------------------------------ private method start
+ /**
+ * 初始化
+ * 初始化包括:
+ *
+ * 1、解析传入的路径,判断其为目录还是文件
+ * 2、创建{@link WatchService} 对象
+ *
+ *
+ * @throws WatchException 监听异常,IO异常时抛出此异常
+ */
+ private void init() throws WatchException {
+ //获取目录或文件路径
+ if (!PathUtil.exists(this.dir, false)) {
+ // 不存在的路径
+ final Path lastPathEle = FileUtil.getLastPathEle(this.dir);
+ if (null != lastPathEle) {
+ final String lastPathEleStr = lastPathEle.toString();
+ //带有点表示有扩展名,按照未创建的文件对待。Linux下.d的为目录,排除之
+ if (StrUtil.contains(lastPathEleStr, CharUtil.DOT) && !StrUtil.endWithIgnoreCase(lastPathEleStr, ".d")) {
+ this.filePath = this.dir;
+ this.dir = this.filePath.getParent();
+ }
+ }
+
+ //创建不存在的目录或父目录
+ PathUtil.mkdir(this.dir);
+ } else if (PathUtil.isFile(this.dir, false)) {
+ // 文件路径
+ this.filePath = this.dir;
+ this.dir = this.filePath.getParent();
+ }
+ }
+
/**
* 执行事件获取并处理
*
* @param watcher {@link Watcher}
*/
private void doTakeAndWatch(final Watcher watcher) {
- super.watch(watcher, watchEvent -> null == filePath || filePath.endsWith(watchEvent.context().toString()));
+ this.watchService.watch(watcher, watchEvent -> null == filePath || filePath.endsWith(watchEvent.context().toString()));
}
/**
* 注册监听路径
*/
private void registerPath() {
- registerPath(this.path, (null != this.filePath) ? 0 : this.maxDepth);
+ this.watchService.registerPath(this.dir, (null != this.filePath) ? 0 : this.maxDepth);
+ }
+
+ @Override
+ public void close() throws IOException {
+ this.watchService.close();
}
//------------------------------------------------------ private method end
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchServer.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchServer.java
deleted file mode 100644
index ed3445214..000000000
--- a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchServer.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (c) 2023 looly(loolly@aliyun.com)
- * Hutool is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- * https://license.coscl.org.cn/MulanPSL2
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-package org.dromara.hutool.core.io.watch;
-
-import org.dromara.hutool.core.io.IoUtil;
-import org.dromara.hutool.core.func.SerBiConsumer;
-import org.dromara.hutool.core.array.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.WatchEvent;
-import java.nio.file.WatchKey;
-import java.nio.file.WatchService;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Predicate;
-
-/**
- * 文件监听服务,此服务可以同时监听多个路径。
- *
- * @author loolly
- * @since 5.1.0
- */
-public class WatchServer extends Thread implements Closeable, Serializable {
- private static final long serialVersionUID = 1L;
-
- /**
- * 监听服务
- */
- private WatchService watchService;
- /**
- * 监听事件列表
- */
- protected WatchEvent.Kind>[] events;
- /**
- * 监听选项,例如监听频率等
- */
- private WatchEvent.Modifier[] modifiers;
- /**
- * 监听是否已经关闭
- */
- protected boolean isClosed;
- /**
- * WatchKey 和 Path的对应表
- */
- private final Map watchKeyPathMap = new HashMap<>();
-
- /**
- * 初始化
- * 初始化包括:
- *
- * 1、解析传入的路径,判断其为目录还是文件
- * 2、创建{@link WatchService} 对象
- *
- *
- * @throws WatchException 监听异常,IO异常时抛出此异常
- */
- public void init() throws WatchException {
- //初始化监听
- try {
- watchService = FileSystems.getDefault().newWatchService();
- } catch (final IOException e) {
- throw new WatchException(e);
- }
-
- isClosed = false;
- }
-
- /**
- * 设置监听选项,例如监听频率等,可设置项包括:
- *
- *
- * 1、com.sun.nio.file.StandardWatchEventKinds
- * 2、com.sun.nio.file.SensitivityWatchEventModifier
- *
- *
- * @param modifiers 监听选项,例如监听频率等
- */
- public void setModifiers(final WatchEvent.Modifier[] modifiers) {
- this.modifiers = modifiers;
- }
-
- /**
- * 将指定路径加入到监听中
- *
- * @param path 路径
- * @param maxDepth 递归下层目录的最大深度
- */
- public void registerPath(final Path path, final int maxDepth) {
- final WatchEvent.Kind>[] kinds = ArrayUtil.defaultIfEmpty(this.events, WatchKind.ALL);
-
- try {
- final WatchKey key;
- if (ArrayUtil.isEmpty(this.modifiers)) {
- key = path.register(this.watchService, kinds);
- } else {
- key = path.register(this.watchService, kinds, this.modifiers);
- }
- watchKeyPathMap.put(key, path);
-
- // 递归注册下一层层级的目录
- if (maxDepth > 1) {
- //遍历所有子目录并加入监听
- Files.walkFileTree(path, EnumSet.noneOf(FileVisitOption.class), maxDepth, new SimpleFileVisitor() {
- @Override
- public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
- registerPath(dir, 0);//继续添加目录
- return super.postVisitDirectory(dir, exc);
- }
- });
- }
- } catch (final IOException e) {
- if (!(e instanceof AccessDeniedException)) {
- throw new WatchException(e);
- }
-
- //对于禁止访问的目录,跳过监听
- }
- }
-
- /**
- * 执行事件获取并处理
- *
- * @param action 监听回调函数,实现此函数接口用于处理WatchEvent事件
- * @param watchFilter 监听过滤接口,通过实现此接口过滤掉不需要监听的情况,{@link Predicate#test(Object)}为{@code true}保留,null表示不过滤
- * @since 5.4.0
- */
- public void watch(final SerBiConsumer, Path> action, final Predicate> watchFilter) {
- final WatchKey wk;
- try {
- wk = watchService.take();
- } catch (final InterruptedException | ClosedWatchServiceException e) {
- // 用户中断
- close();
- return;
- }
-
- final Path currentPath = watchKeyPathMap.get(wk);
-
- for (final WatchEvent> event : wk.pollEvents()) {
- // 如果监听文件,检查当前事件是否与所监听文件关联
- if (null != watchFilter && !watchFilter.test(event)) {
- continue;
- }
-
- action.accept(event, currentPath);
- }
-
- wk.reset();
- }
-
- /**
- * 执行事件获取并处理
- *
- * @param watcher {@link Watcher}
- * @param watchFilter 监听过滤接口,通过实现此接口过滤掉不需要监听的情况,{@link Predicate#test(Object)}为{@code true}保留,null表示不过滤
- */
- public void watch(final Watcher watcher, final Predicate> watchFilter) {
- watch((event, currentPath)->{
- final WatchEvent.Kind> kind = event.kind();
-
- if (kind == WatchKind.CREATE.getValue()) {
- watcher.onCreate(event, currentPath);
- } else if (kind == WatchKind.MODIFY.getValue()) {
- watcher.onModify(event, currentPath);
- } else if (kind == WatchKind.DELETE.getValue()) {
- watcher.onDelete(event, currentPath);
- } else if (kind == WatchKind.OVERFLOW.getValue()) {
- watcher.onOverflow(event, currentPath);
- }
- }, watchFilter);
- }
-
- /**
- * 关闭监听
- */
- @Override
- public void close() {
- isClosed = true;
- IoUtil.closeQuietly(watchService);
- }
-}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchServiceWrapper.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchServiceWrapper.java
new file mode 100644
index 000000000..2dce5c96e
--- /dev/null
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchServiceWrapper.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2023. looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * https://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.core.io.watch;
+
+import org.dromara.hutool.core.array.ArrayUtil;
+import org.dromara.hutool.core.io.IoUtil;
+import org.dromara.hutool.core.io.file.PathUtil;
+import org.dromara.hutool.core.lang.wrapper.Wrapper;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.nio.file.*;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
+import java.util.function.Predicate;
+
+/**
+ * {@link WatchEvent} 包装类,提供可选的监听事件和监听选项
+ * 通过多次调用{@link #registerPath(Path, int)} 可以递归注册多个路径
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public class WatchServiceWrapper implements WatchService, Wrapper, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 监听服务
+ */
+ private final WatchService watchService;
+ /**
+ * 监听事件列表
+ */
+ private WatchEvent.Kind>[] events;
+ /**
+ * 监听选项,例如监听频率等
+ */
+ private WatchEvent.Modifier[] modifiers;
+ /**
+ * 监听是否已经关闭
+ */
+ private boolean isClosed;
+
+ /**
+ * 构造
+ */
+ public WatchServiceWrapper() {
+ //初始化监听
+ try {
+ watchService = FileSystems.getDefault().newWatchService();
+ } catch (final IOException e) {
+ throw new WatchException(e);
+ }
+
+ isClosed = false;
+ }
+
+ @Override
+ public WatchService getRaw() {
+ return this.watchService;
+ }
+
+ /**
+ * 是否已关闭
+ *
+ * @return 是否已关闭
+ */
+ public boolean isClosed() {
+ return this.isClosed;
+ }
+
+ @Override
+ public void close() {
+ if (!this.isClosed) {
+ this.isClosed = true;
+ IoUtil.closeQuietly(this.watchService);
+ }
+ }
+
+ @Override
+ public WatchKey poll() {
+ return this.watchService.poll();
+ }
+
+ @Override
+ public WatchKey poll(final long timeout, final TimeUnit unit) throws InterruptedException {
+ return this.watchService.poll(timeout, unit);
+ }
+
+ @Override
+ public WatchKey take() throws InterruptedException {
+ return this.watchService.take();
+ }
+
+ /**
+ * 监听事件列表,见:StandardWatchEventKinds
+ *
+ * @param kinds 事件列表
+ * @return this
+ */
+ @SuppressWarnings("resource")
+ public WatchServiceWrapper setEvents(final WatchKind... kinds) {
+ if (ArrayUtil.isNotEmpty(kinds)) {
+ setEvents(ArrayUtil.mapToArray(kinds, WatchKind::getValue, WatchEvent.Kind>[]::new));
+ }
+ return this;
+ }
+
+ /**
+ * 监听事件列表,见:StandardWatchEventKinds
+ *
+ * @param events 事件列表
+ * @return this
+ */
+ public WatchServiceWrapper setEvents(final WatchEvent.Kind>... events) {
+ this.events = events;
+ return this;
+ }
+
+ /**
+ * 设置监听选项,例如监听频率等,可设置项包括:
+ *
+ *
+ * 1、com.sun.nio.file.StandardWatchEventKinds
+ * 2、com.sun.nio.file.SensitivityWatchEventModifier
+ *
+ *
+ * @param modifiers 监听选项,例如监听频率等
+ * @return this
+ */
+ public WatchServiceWrapper setModifiers(final WatchEvent.Modifier... modifiers) {
+ this.modifiers = modifiers;
+ return this;
+ }
+
+ /**
+ * 注册单一的监听
+ *
+ * @param watchable 可注册对象,如Path
+ * @return {@link WatchKey},如果为{@code null},表示注册失败
+ */
+ public WatchKey register(final Watchable watchable) {
+ final WatchEvent.Kind>[] kinds = ArrayUtil.defaultIfEmpty(this.events, WatchKind.ALL);
+
+ WatchKey watchKey = null;
+ try {
+ if (ArrayUtil.isEmpty(this.modifiers)) {
+ watchKey = watchable.register(this.watchService, kinds);
+ } else {
+ watchKey = watchable.register(this.watchService, kinds, this.modifiers);
+ }
+ } catch (final IOException e) {
+ if (!(e instanceof AccessDeniedException)) {
+ throw new WatchException(e);
+ }
+ }
+
+ return watchKey;
+ }
+
+ /**
+ * 递归将指定路径加入到监听中
+ * 如果提供的是目录,则监听目录本身和目录下的目录和文件,深度取决于maxDepth
+ *
+ * @param path 路径
+ * @param maxDepth 递归下层目录的最大深度
+ */
+ public void registerPath(final Path path, final int maxDepth) {
+ // 注册当前目录或文件
+ final WatchKey watchKey = register(path);
+ if (null == watchKey) {
+ // 注册失败,跳过(可能目录或文件无权限)
+ return;
+ }
+
+ // 递归注册下一层层级的目录和文件
+ PathUtil.walkFiles(path, maxDepth, new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
+ //继续添加目录
+ registerPath(dir, 0);
+ return super.postVisitDirectory(dir, exc);
+ }
+ });
+ }
+
+ /**
+ * 执行事件获取并处理
+ *
+ * @param watcher {@link Watcher}
+ * @param watchFilter 监听过滤接口,通过实现此接口过滤掉不需要监听的情况,{@link Predicate#test(Object)}为{@code true}保留,null表示不过滤
+ */
+ public void watch(final Watcher watcher, final Predicate> watchFilter) {
+ watch((event, watchKey) -> {
+ final WatchEvent.Kind> kind = event.kind();
+
+ if (kind == WatchKind.CREATE.getValue()) {
+ watcher.onCreate(event, watchKey);
+ } else if (kind == WatchKind.MODIFY.getValue()) {
+ watcher.onModify(event, watchKey);
+ } else if (kind == WatchKind.DELETE.getValue()) {
+ watcher.onDelete(event, watchKey);
+ } else if (kind == WatchKind.OVERFLOW.getValue()) {
+ watcher.onOverflow(event, watchKey);
+ }
+ }, watchFilter);
+ }
+
+ /**
+ * 执行事件获取并处理
+ *
+ * @param action 监听回调函数,实现此函数接口用于处理WatchEvent事件
+ * @param watchFilter 监听过滤接口,通过实现此接口过滤掉不需要监听的情况,{@link Predicate#test(Object)}为{@code true}保留,null表示不过滤
+ */
+ public void watch(final BiConsumer, WatchKey> action, final Predicate> watchFilter) {
+ final WatchKey wk;
+ try {
+ wk = watchService.take();
+ } catch (final InterruptedException | ClosedWatchServiceException e) {
+ // 用户中断
+ close();
+ return;
+ }
+
+ for (final WatchEvent> event : wk.pollEvents()) {
+ // 如果监听文件,检查当前事件是否与所监听文件关联
+ if (null != watchFilter && !watchFilter.test(event)) {
+ continue;
+ }
+ action.accept(event, wk);
+ }
+
+ wk.reset();
+ }
+}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchUtil.java
index 4050ddd4f..8cd77b636 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchUtil.java
@@ -34,6 +34,8 @@ import java.nio.file.Watchable;
* @since 3.1.0
*/
public class WatchUtil {
+
+ // region ----- of
/**
* 创建并初始化监听
*
@@ -148,8 +150,9 @@ public class WatchUtil {
public static WatchMonitor of(final Path path, final int maxDepth, final WatchEvent.Kind>... events) {
return new WatchMonitor(path, maxDepth, events);
}
+ // endregion
- // ---------------------------------------------------------------------------------------------------------- createAll
+ // region ----- ofAll
/**
* 创建并初始化监听,监听所有事件
*
@@ -266,8 +269,9 @@ public class WatchUtil {
watchMonitor.setWatcher(watcher);
return watchMonitor;
}
+ // endregion
- // ---------------------------------------------------------------------------------------------------------- createModify
+ // region ----- createModify
/**
* 创建并初始化监听,监听修改事件
*
@@ -394,6 +398,7 @@ public class WatchUtil {
watchMonitor.setWatcher(watcher);
return watchMonitor;
}
+ // endregion
/**
* 注册Watchable对象到WatchService服务
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/Watcher.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/Watcher.java
index 471e5af3d..7d3ff8985 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/Watcher.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/Watcher.java
@@ -12,8 +12,8 @@
package org.dromara.hutool.core.io.watch;
-import java.nio.file.Path;
import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
/**
* 观察者(监视器)
@@ -24,33 +24,33 @@ public interface Watcher {
/**
* 文件创建时执行的方法
*
- * @param event 事件
- * @param currentPath 事件发生的当前Path路径
+ * @param event 事件
+ * @param key 事件发生的{@link WatchKey}
*/
- void onCreate(WatchEvent> event, Path currentPath);
+ void onCreate(WatchEvent> event, WatchKey key);
/**
* 文件修改时执行的方法
* 文件修改可能触发多次
*
- * @param event 事件
- * @param currentPath 事件发生的当前Path路径
+ * @param event 事件
+ * @param key 事件发生的{@link WatchKey}
*/
- void onModify(WatchEvent> event, Path currentPath);
+ void onModify(WatchEvent> event, WatchKey key);
/**
* 文件删除时执行的方法
*
- * @param event 事件
- * @param currentPath 事件发生的当前Path路径
+ * @param event 事件
+ * @param key 事件发生的{@link WatchKey}
*/
- void onDelete(WatchEvent> event, Path currentPath);
+ void onDelete(WatchEvent> event, WatchKey key);
/**
* 事件丢失或出错时执行的方法
*
- * @param event 事件
- * @param currentPath 事件发生的当前Path路径
+ * @param event 事件
+ * @param key 事件发生的{@link WatchKey}
*/
- void onOverflow(WatchEvent> event, Path currentPath);
+ void onOverflow(WatchEvent> event, WatchKey key);
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/SimpleWatcher.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/SimpleWatcher.java
similarity index 87%
rename from hutool-core/src/main/java/org/dromara/hutool/core/io/watch/SimpleWatcher.java
rename to hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/SimpleWatcher.java
index 801924a24..7afdd5663 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/SimpleWatcher.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/SimpleWatcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 looly(loolly@aliyun.com)
+ * Copyright (c) 2023. looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
@@ -10,7 +10,7 @@
* See the Mulan PSL v2 for more details.
*/
-package org.dromara.hutool.core.io.watch;
+package org.dromara.hutool.core.io.watch.watchers;
import org.dromara.hutool.core.io.watch.watchers.IgnoreWatcher;
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/array/ArrayUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/array/ArrayUtilTest.java
index 24b46acfe..493b3229f 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/array/ArrayUtilTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/array/ArrayUtilTest.java
@@ -164,13 +164,20 @@ public class ArrayUtilTest {
}
@Test
- public void mapTest() {
+ public void zipTest() {
final String[] keys = {"a", "b", "c"};
final Integer[] values = {1, 2, 3};
final Map map = ArrayUtil.zip(keys, values, true);
Assertions.assertEquals(Objects.requireNonNull(map).toString(), "{a=1, b=2, c=3}");
}
+ @Test
+ public void mapToArrayTest() {
+ final String[] keys = {"a", "b", "c"};
+ final Integer[] integers = ArrayUtil.mapToArray(keys, String::length, Integer[]::new);
+ Assertions.assertArrayEquals(integers, new Integer[]{1, 1, 1});
+ }
+
@Test
public void castTest() {
final Object[] values = {"1", "2", "3"};
@@ -453,7 +460,7 @@ public class ArrayUtilTest {
final Object a = new int[]{1, 2, 3, 4};
final Object[] wrapA = ArrayUtil.wrap(a);
for (final Object o : wrapA) {
- Assertions.assertTrue(o instanceof Integer);
+ Assertions.assertInstanceOf(Integer.class, o);
}
}
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/io/WatchMonitorTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/io/WatchMonitorTest.java
index 9a267ad24..26c8d26c7 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/io/WatchMonitorTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/io/WatchMonitorTest.java
@@ -15,8 +15,9 @@ package org.dromara.hutool.core.io;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
-import org.dromara.hutool.core.io.watch.SimpleWatcher;
+import org.dromara.hutool.core.io.watch.watchers.SimpleWatcher;
import org.dromara.hutool.core.io.watch.WatchMonitor;
+import org.dromara.hutool.core.io.watch.WatchUtil;
import org.dromara.hutool.core.io.watch.Watcher;
import org.dromara.hutool.core.io.watch.watchers.DelayWatcher;
import org.dromara.hutool.core.lang.Console;
@@ -56,7 +57,8 @@ public class WatchMonitorTest {
}
};
- final WatchMonitor monitor = WatchMonitor.ofAll("d:/test/aaa.txt", new DelayWatcher(watcher, 500));
+ //noinspection resource
+ final WatchMonitor monitor = WatchUtil.ofAll("d:/test/aaa.txt", new DelayWatcher(watcher, 500));
monitor.setMaxDepth(0);
monitor.start();
diff --git a/hutool-setting/src/main/java/org/dromara/hutool/setting/Setting.java b/hutool-setting/src/main/java/org/dromara/hutool/setting/Setting.java
index efbd0f149..7c6409368 100644
--- a/hutool-setting/src/main/java/org/dromara/hutool/setting/Setting.java
+++ b/hutool-setting/src/main/java/org/dromara/hutool/setting/Setting.java
@@ -18,7 +18,7 @@ import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.io.resource.Resource;
import org.dromara.hutool.core.io.resource.ResourceUtil;
-import org.dromara.hutool.core.io.watch.SimpleWatcher;
+import org.dromara.hutool.core.io.watch.watchers.SimpleWatcher;
import org.dromara.hutool.core.io.watch.WatchMonitor;
import org.dromara.hutool.core.io.watch.WatchUtil;
import org.dromara.hutool.core.lang.Assert;
diff --git a/hutool-setting/src/main/java/org/dromara/hutool/setting/props/Props.java b/hutool-setting/src/main/java/org/dromara/hutool/setting/props/Props.java
index 7f43f105c..5ffd52d48 100644
--- a/hutool-setting/src/main/java/org/dromara/hutool/setting/props/Props.java
+++ b/hutool-setting/src/main/java/org/dromara/hutool/setting/props/Props.java
@@ -22,7 +22,7 @@ import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.io.resource.Resource;
import org.dromara.hutool.core.io.resource.ResourceUtil;
-import org.dromara.hutool.core.io.watch.SimpleWatcher;
+import org.dromara.hutool.core.io.watch.watchers.SimpleWatcher;
import org.dromara.hutool.core.io.watch.WatchMonitor;
import org.dromara.hutool.core.io.watch.WatchUtil;
import org.dromara.hutool.core.lang.Assert;