diff --git a/ability/aivfo-framework-build/aivfo-dependencies-parent/pom.xml b/ability/aivfo-framework-build/aivfo-dependencies-parent/pom.xml index d2ea8a1..ce33dd2 100644 --- a/ability/aivfo-framework-build/aivfo-dependencies-parent/pom.xml +++ b/ability/aivfo-framework-build/aivfo-dependencies-parent/pom.xml @@ -54,6 +54,16 @@ transmittable-thread-local ${transmittable-thread-local.version} + + io.swagger + swagger-annotations + 1.5.22 + + + org.reflections + reflections + 0.9.11 + \ No newline at end of file diff --git a/ability/aivfo-framework-build/aivfo-project-build/aivfo-model-parent/pom.xml b/ability/aivfo-framework-build/aivfo-project-build/aivfo-model-parent/pom.xml index dea2937..f41f1ac 100644 --- a/ability/aivfo-framework-build/aivfo-project-build/aivfo-model-parent/pom.xml +++ b/ability/aivfo-framework-build/aivfo-project-build/aivfo-model-parent/pom.xml @@ -11,4 +11,16 @@ 4.0.0 pom aivfo-model-parent + + + com.aivfo + aivfo-element-common + 1.0.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-test + test + + \ No newline at end of file diff --git a/ability/aivfo-supreme/pom.xml b/ability/aivfo-supreme/pom.xml index d4d30ea..174a9a2 100644 --- a/ability/aivfo-supreme/pom.xml +++ b/ability/aivfo-supreme/pom.xml @@ -62,12 +62,12 @@ nexus releases - https://maven.wylgyx.top/repository/maven-releases/ + http://192.168.31.90:8081/repository/maven-releases/ nexus snapshots - https://maven.wylgyx.top/repository/maven-snapshots/ + http://192.168.31.90:8081/repository/maven-snapshots/ diff --git a/ability/pom.xml b/ability/pom.xml index f284b82..5aeceb0 100644 --- a/ability/pom.xml +++ b/ability/pom.xml @@ -11,5 +11,16 @@ aivfo-supreme 4.0.0 - + + + nexus + releases + http://192.168.31.90:8081/repository/maven-releases/ + + + nexus + snapshots + http://192.168.31.90:8081/repository/maven-snapshots/ + + \ No newline at end of file diff --git a/band/aivfo-element/aivfo-element-base/pom.xml b/band/aivfo-element/aivfo-element-base/pom.xml index a416839..abad5e0 100644 --- a/band/aivfo-element/aivfo-element-base/pom.xml +++ b/band/aivfo-element/aivfo-element-base/pom.xml @@ -3,10 +3,10 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - aivfo-model-parent + aivfo-element com.aivfo 1.0.0-SNAPSHOT - ../../../ability/aivfo-framework-build/aivfo-project-build/aivfo-model-parent/pom.xml + ../pom.xml 4.0.0 aivfo-element-base @@ -22,7 +22,10 @@ org.reflections reflections - 0.9.11 + + + org.apache.commons + commons-lang3 com.fasterxml.jackson.core @@ -39,7 +42,36 @@ cn.hutool hutool-core - 5.7.16 + + + org.springframework + spring-beans + + + org.springframework + spring-web + + + javax.servlet + javax.servlet-api + + + io.swagger + swagger-annotations + + + org.springframework + spring-aop + + + org.springframework + spring-context + + + org.apache.commons + commons-text + 1.8 + compile diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/constant/ConfigDefaultValue.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/constant/ConfigDefaultValue.java deleted file mode 100644 index 4e3c07c..0000000 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/constant/ConfigDefaultValue.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.aivfo.el.base.constant; - -import lombok.experimental.UtilityClass; - -/** - * @author: wangyl - * @date: 2023/4/18 - * @description: 常用的配置默认值 - */ -@UtilityClass -public final class ConfigDefaultValue { - /** DEFAULT_TIME_ZONE */ - public static final String DEFAULT_TIME_ZONE = "GMT+8"; - /** DEFAULT_DATE_FORMAT */ - public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; - /** BASE_PACKAGES */ - public static final String BASE_PACKAGES = System.getProperty("PARENT_PACKAGE_NAME", "com.aivfo"); -} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/constant/ConfigKey.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/constant/ConfigKey.java deleted file mode 100644 index 908a13e..0000000 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/constant/ConfigKey.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.aivfo.el.base.constant; - -import lombok.experimental.UtilityClass; - -/** - * @author: wangyl - * @date: 2023/4/18 - * @description: 所有的配置常量 - */ -@UtilityClass -@SuppressWarnings("all") -public final class ConfigKey { - /** JSON_DATE_FORMAT */ - public static final String JSON_DATE_FORMAT = "aivfo.rest.json.date-formate"; - /** JSON_TIME_ZONE */ - public static final String JSON_TIME_ZONE = "aivfo.rest.json.time-zone"; -} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/exception/AivfoBaseException.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/exception/AivfoBaseException.java deleted file mode 100644 index 328c40b..0000000 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/exception/AivfoBaseException.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.aivfo.el.base.exception; - -import com.aivfo.el.base.contex.TraceContext; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * @author: wangyl - * @date: 2023/4/14 - * @description: 统一异常 - */ -@Slf4j -public class AivfoBaseException extends RuntimeException { - - - private static final long serialVersionUID = 8315456934998288154L; - /** - * 错误码 - */ - @Getter - protected String errorCode; - /** - * 错误信息 - */ - protected String message; - /** - * 请求链路id - */ - @Getter - protected String traceId; - - /** - * 默认code - */ - public static final String DEFAULT_ERROR_CODE = "00000001"; - /** - * 默认错误信息 - */ - public static final String DEFAULT_MESSAGE = "服务内部错误"; - - - public AivfoBaseException() { - this(DEFAULT_MESSAGE); - } - - public AivfoBaseException(String errorMsg) { - this(DEFAULT_ERROR_CODE, errorMsg); - } - - public AivfoBaseException(String errorCode, String errorMsg) { - super(errorMsg); - this.init(errorCode, errorMsg); - } - - /** - * Init - * - * @param code code - * @param message message - * @since 1.9.0 - */ - private void init(String code, String message) { - this.errorCode = code; - this.message = message; - this.traceId = TraceContext.getTraceId(); - } - - @Override - public Throwable fillInStackTrace() { - return this; - } - - /** - * 异常打印到日志 - * - * @param - * @return void - * @Date 2023/2/20 - * @Author wangyl - */ - @Override - public void printStackTrace() { - log.error("", this); - } -} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/exception/ErrorCodeInterface.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/exception/ErrorCodeInterface.java deleted file mode 100644 index b153f30..0000000 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/exception/ErrorCodeInterface.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.aivfo.el.base.exception; - -/** - * @author: wangyl - * @date: 2023/4/14 - * @description: 错误码接口 - */ -public interface ErrorCodeInterface { - /** - * @return java.lang.String - * @Description 获取错误码 - * @Date 2020/11/8 15:37 - * @Author wangyl - * @Version V1.0 - */ - String getCode(); - - /** - * @return java.lang.String - * @Description 获取错误信息 - * @Date 2020/11/8 15:38 - * @Author wangyl - * @Version V1.0 - */ - String getMsg(); -} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/AivfoCollectionUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/AivfoCollectionUtils.java deleted file mode 100644 index 718f973..0000000 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/AivfoCollectionUtils.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.aivfo.el.base.utils; - -import org.springframework.util.CollectionUtils; - -/** - * @author: wangyl - * @date: 2023/4/14 - * @description: Collection工具 - */ -public class AivfoCollectionUtils extends CollectionUtils { -} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/AivfoStringUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/AivfoStringUtils.java deleted file mode 100644 index eafe544..0000000 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/AivfoStringUtils.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.aivfo.el.base.utils; - - -import org.springframework.util.StringUtils; - -/** - * @author: wangyl - * @date: 2023/4/14 - * @description: String工具类 - */ -public class AivfoStringUtils extends StringUtils { -} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedCallable.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedCallable.java deleted file mode 100644 index ca35935..0000000 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedCallable.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.aivfo.el.core.function; - - -/** - *

Company: 成都返空汇网络技术有限公司

- *

Description: 受检的 Callable

- * - * @param the type parameter - * @author dong4j - * @version 1.2.3 - * @email "mailto:dongshijie@fkhwl.com" - * @date 2020.01.27 14:54 - * @since 1.0.0 - */ -@FunctionalInterface -@SuppressWarnings("java:S112") -public interface CheckedCallable { - - /** - * Run this callable. - * - * @return result t - * @throws Throwable CheckedException - * @since 1.0.0 - */ - T call() throws Throwable; -} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedConsumer.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedConsumer.java deleted file mode 100644 index 8c757ea..0000000 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedConsumer.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.aivfo.el.core.function; - -/** - *

Company: 成都返空汇网络技术有限公司

- *

Description: 受检的 Consumer

- * - * @param parameter - * @author dong4j - * @version 1.2.3 - * @email "mailto:dongshijie@fkhwl.com" - * @date 2020.01.27 14:55 - * @since 1.0.0 - */ -@FunctionalInterface -@SuppressWarnings("java:S112") -public interface CheckedConsumer { - - /** - * Run the Consumer - * - * @param t T - * @throws Throwable UncheckedException - * @since 1.0.0 - */ - void accept(T t) throws Throwable; - -} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedRunnable.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedRunnable.java deleted file mode 100644 index 760532f..0000000 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedRunnable.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.aivfo.el.core.function; - -/** - *

Company: 成都返空汇网络技术有限公司

- *

Description: 受检的 runnable

- * - * @author dong4j - * @version 1.2.3 - * @email "mailto:dongshijie@fkhwl.com" - * @date 2020.01.27 14:54 - * @since 1.0.0 - */ -@FunctionalInterface -@SuppressWarnings("java:S112") -public interface CheckedRunnable { - - /** - * Run this runnable. - * - * @throws Throwable CheckedException - * @since 1.0.0 - */ - void run() throws Throwable; - -} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/AbstractSkipFilter.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/AbstractSkipFilter.java new file mode 100644 index 0000000..bf36142 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/AbstractSkipFilter.java @@ -0,0 +1,108 @@ +package com.aivfo.el.starter.base; + +import com.aivfo.el.starter.base.utils.SecurityUtils; +import com.aivfo.el.starter.base.utils.StringPool; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.lang.Nullable; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: 能够配置需要跳过的 url 的抽象 filter, 子类 + */ +@Slf4j +@Data +@EqualsAndHashCode(callSuper = true) +public abstract class AbstractSkipFilter extends OncePerRequestFilter { + /** + * Path matcher + */ + protected PathMatcher pathMatcher = new AntPathMatcher(); + /** + * 需要执行过滤器逻辑的 url + */ + @Nullable + protected String[] includePatterns; + /** + * 不需要执行过滤器逻辑的 url + */ + @Nullable + protected String[] excludePatterns; + /** + * 业务自定义需要忽略的 url, 最终会写入到 {@link AbstractSkipFilter#includePatterns} + */ + protected String skipUrl; + + /** + * Abstract skip filter + * + * @since 2.1.0 + */ + public AbstractSkipFilter() { + init(); + } + + /** + * Abstract skip filter + * + * @param skipUrl skip url + * @since 2.1.0 + */ + public AbstractSkipFilter(String skipUrl) { + this.skipUrl = skipUrl; + merge(); + } + + /** + * Init + * + * @since 1.5.0 + */ + @PostConstruct + public void init() { + merge(); + } + + /** + * Merge + * + * @since 2.1.0 + */ + private void merge() { + if (this.includePatterns == null) { + this.includePatterns = new String[]{StringPool.ANY_PATH}; + } + + if (this.excludePatterns == null) { + this.excludePatterns = SecurityUtils.mergeSkipPatterns(this.skipUrl).toArray(new String[0]); + } else { + Set excluded = new HashSet<>(Arrays.asList(this.excludePatterns)); + this.excludePatterns = SecurityUtils.mergeSkipPatterns(excluded, this.skipUrl).toArray(new String[0]); + } + } + + /** + * 不需要拦截的 url + * + * @param request request + * @return the boolean + * @since 1.5.0 + */ + @Override + protected boolean shouldNotFilter(@NotNull HttpServletRequest request) { + return SecurityUtils.matches(request.getRequestURI(), this.includePatterns, this.excludePatterns, this.pathMatcher); + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/Result.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/Result.java new file mode 100644 index 0000000..a396fd1 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/Result.java @@ -0,0 +1,196 @@ +package com.aivfo.el.starter.base; + +import com.aivfo.el.starter.base.utils.StringPool; +import com.aivfo.el.starter.base.utils.StringUtils; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.Serializable; + +/** + *

Description: 请求响应返回结构封装, 所有请求都要求返回此类. + * 为避免在 v4 中引入过多的 v5 依赖, 我们将 {@link Result} 分为了 2 个子类, 分别是 {@link StandardResult} 和 {@link com.fkhwl.starter.core.api.R}, + * StandardResult 在 v4 使用, R 在 v5 使用. + * {@code + * Result result = StandardResult.success(data); + * Result result = R.success(data); + *

+ * 反序列化: + * Result result = JsonUtils.parse(json, new TypeReference>(){}); + * Result result = JsonUtils.parse(json, Result.class); + * }* + *

+ * 由于 jackson 在反序列化抽象类时存在多态问题, 我们使用 {@link Result#TYPE_NAME} 来标识 json 需要被反序列化的 class, 为了避免 v5 的响应 json 数据在 v4 中反序列化失败, + * 我们将所有的 json 都添加了 {@link Result#TYPE_NAME}, 全部都将反序列化为 {@link StandardResult}, 业务端在处理 json 结果时, 应该忽略 'type' 字段. + * 比较推荐的反序列化方式是根据不同的框架来进行反序列化, 避免使用 {@link Result}: + * {@code + * v4: Result result = JsonUtils.parse(json, StandardResult.class); + * v5: Result result = JsonUtils.parse(json, R.class); + * }* + */ +@Getter +@Setter +@ToString +@NoArgsConstructor +@SuppressWarnings("all") +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +public abstract class Result implements Serializable { + + /** + * serialVersionUID + */ + public static final long serialVersionUID = 1L; + /** + * CODE + */ + public static final String CODE = "code"; + /** + * SUCCESS + */ + public static final String SUCCESS = "success"; + /** + * DATA + */ + public static final String DATA = "data"; + /** + * MESSAGE + */ + public static final String MESSAGE = "message"; + /** + * TRACE_ID + */ + public static final String TRACE_ID = "traceId"; + /** + * EXTEND + */ + public static final String EXTEND = "extend"; + /** + * 反序列化时处理多态问题的标识 + */ + public static final String TYPE_NAME = "StandardResult"; + /** + * 请求成功代码 + */ + public static final String SUCCESS_CODE = "2000"; + /** + * 请求成功消息 + */ + public static final String SUCCESS_MESSAGE = "操作成功"; + /** + * 默认的失败代码 + */ + public static final String FAILURE_CODE = "4000"; + /** + * 默认的失败消息 + */ + public static final String FAILURE_MESSAGE = "操作失败: [{}]"; + + /** + * 请求响应状态码 + */ + @ApiModelProperty(value = "状态码", required = true, example = "2000") + protected String code; + /** + * 请求响应成功标识 + */ + @ApiModelProperty(value = "请求成功的状态", required = true, example = "true") + protected boolean success; + /** + * 请求响应的数据 + */ + @ApiModelProperty(value = "承载的数据", required = true) + protected T data; + /** + * 请求响应的消息 + */ + @ApiModelProperty(value = "返回的消息", required = true, example = "操作成功") + protected String message = ""; + /** + * 请求响应的溯源标识 + */ + @ApiModelProperty(value = "溯源标识(业务无需关心此字段)", required = true, example = "1484501823002316800") + protected String traceId; + /** + * 扩展字段 + */ + @ApiModelProperty(value = "扩展字段(业务无需关心此字段, 非生产环境的异常信息会写入到此字段)", required = true, example = "N/A") + protected Object extend = StringPool.NULL_STRING; + + /** + * Result + * + * @param code code + * @param message message + * @param data data + * @param traceId trace id + * @since 1.0.0 + */ + @Contract(pure = true) + protected Result(@NotNull String code, String message, T data, String traceId) { + this.code = code; + if (!SUCCESS_CODE.equals(code) && !code.contains(StringPool.DASH) && !code.contains(StringPool.DOT)) { + this.code = "S.F-" + code; + } + this.data = data; + this.message = message; + this.success = (SUCCESS_CODE.equals(code)); + this.traceId = StringUtils.isBlank(traceId) + ? StringPool.NULL_STRING + : traceId; + } + + /** + * Is ok boolean + * + * @return the boolean + * @since 1.0.0 + */ + @JsonIgnore + public boolean isOk() { + return isOk(this); + } + + /** + * 请求是否成功 + * + * @param result result + * @return the boolean + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isOk(@Nullable Result result) { + return result != null && (SUCCESS_CODE.equals(result.getCode()) && result.isSuccess()); + } + + /** + * Is fail boolean + * + * @return the boolean + * @since 1.0.0 + */ + @JsonIgnore + public boolean isFail() { + return isFail(this); + } + + /** + * 请求是否失败 + * + * @param result result + * @return the boolean + * @since 1.0.0 + */ + @Contract("null -> true") + public static boolean isFail(@Nullable Result result) { + return !isOk(result); + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/BusinessLevel.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/BusinessLevel.java new file mode 100644 index 0000000..e6f5eb4 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/BusinessLevel.java @@ -0,0 +1,15 @@ +package com.aivfo.el.starter.base.annotation; + +import java.lang.annotation.*; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: 错误码来源 业务系统 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface BusinessLevel { + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/ModelSerial.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/ModelSerial.java new file mode 100644 index 0000000..8141b9e --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/ModelSerial.java @@ -0,0 +1,38 @@ +package com.aivfo.el.starter.base.annotation; + +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.*; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: 项目标识 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ModelSerial { + /** + * DEFAULT + */ + String DEFAULT = "F"; + + /** + * Model name + * + * @return the module name + * @since 1.5.0 + */ + @AliasFor("value") + String modelName() default DEFAULT; + + /** + * Value + * + * @return the string + * @since 1.6.0 + */ + @AliasFor("modelName") + String value() default DEFAULT; +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/SerializeValue.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/SerializeValue.java new file mode 100644 index 0000000..f9830d1 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/SerializeValue.java @@ -0,0 +1,39 @@ +package com.aivfo.el.starter.base.annotation; + +import java.lang.annotation.*; + +/** + * 支持普通枚举类字段, 只用在enum类的字段上 + *

当实体类的属性是普通枚举, 且是其中一个字段, 使用该注解来标注枚举类里的那个属性对应字段

+ * {@code + * TableName("student") + * class Student { + * private Integer id; + * private String name; + * private GradeEnum grade;//数据库grade字段类型为int + * } + *

+ * public enum GradeEnum { + * PRIMARY(1,"小学"), + * SECONDORY("2", "中学"), + * HIGH(3, "高中"); + *

+ * @SerializeValue + * private final int code; + * private final String descp; + * } + * }* + *

+ * + * @author yuxiaobin + * @version 1.3.0 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.03.23 19:10 + * @since 1.0.0 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SerializeValue { + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/SystemLevel.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/SystemLevel.java new file mode 100644 index 0000000..3e3fa82 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/SystemLevel.java @@ -0,0 +1,15 @@ +package com.aivfo.el.starter.base.annotation; + +import java.lang.annotation.*; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: 错误码来源 系统错误 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SystemLevel { + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/ThirdLevel.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/ThirdLevel.java new file mode 100644 index 0000000..efebe38 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/annotation/ThirdLevel.java @@ -0,0 +1,14 @@ +package com.aivfo.el.starter.base.annotation; + +import java.lang.annotation.*; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: 错误码来源 三方错误 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface ThirdLevel { +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertEquals.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertEquals.java similarity index 98% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertEquals.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertEquals.java index cf0c749..7e4ece7 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertEquals.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertEquals.java @@ -1,7 +1,7 @@ -package com.aivfo.el.base.asserts; +package com.aivfo.el.starter.base.asserts; -import com.aivfo.el.base.exception.AssertionFailedException; -import com.aivfo.el.core.function.CheckedRunnable; +import com.aivfo.el.starter.base.exception.AssertionFailedException; +import com.aivfo.el.starter.core.function.CheckedRunnable; import org.jetbrains.annotations.Contract; import java.util.function.Supplier; diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertFalse.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertFalse.java similarity index 93% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertFalse.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertFalse.java index 0698e13..0ad4b1a 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertFalse.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertFalse.java @@ -1,8 +1,8 @@ -package com.aivfo.el.base.asserts; +package com.aivfo.el.starter.base.asserts; -import com.aivfo.el.base.exception.AssertionFailedException; -import com.aivfo.el.core.function.CheckedRunnable; +import com.aivfo.el.starter.base.exception.AssertionFailedException; +import com.aivfo.el.starter.core.function.CheckedRunnable; import java.util.function.BooleanSupplier; import java.util.function.Supplier; diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertMatch.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertMatch.java similarity index 92% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertMatch.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertMatch.java index 33976aa..bc27206 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertMatch.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertMatch.java @@ -1,7 +1,7 @@ -package com.aivfo.el.base.asserts; +package com.aivfo.el.starter.base.asserts; -import com.aivfo.el.base.exception.AssertionFailedException; -import com.aivfo.el.core.function.CheckedRunnable; +import com.aivfo.el.starter.base.exception.AssertionFailedException; +import com.aivfo.el.starter.core.function.CheckedRunnable; import java.util.function.Supplier; import java.util.regex.Matcher; diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertNotEquals.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertNotEquals.java similarity index 98% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertNotEquals.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertNotEquals.java index 4fbc9da..66af711 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertNotEquals.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertNotEquals.java @@ -1,6 +1,6 @@ -package com.aivfo.el.base.asserts; +package com.aivfo.el.starter.base.asserts; -import com.aivfo.el.base.exception.AssertionFailedException; +import com.aivfo.el.starter.base.exception.AssertionFailedException; import java.util.function.Supplier; diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertNotMatch.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertNotMatch.java similarity index 92% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertNotMatch.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertNotMatch.java index 0b4c6d4..e3da93a 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertNotMatch.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertNotMatch.java @@ -1,7 +1,7 @@ -package com.aivfo.el.base.asserts; +package com.aivfo.el.starter.base.asserts; -import com.aivfo.el.base.exception.AssertionFailedException; -import com.aivfo.el.core.function.CheckedRunnable; +import com.aivfo.el.starter.base.exception.AssertionFailedException; +import com.aivfo.el.starter.core.function.CheckedRunnable; import java.util.function.Supplier; import java.util.regex.Matcher; diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertNotNull.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertNotNull.java similarity index 89% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertNotNull.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertNotNull.java index 9c5015a..055463e 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertNotNull.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertNotNull.java @@ -1,7 +1,7 @@ -package com.aivfo.el.base.asserts; +package com.aivfo.el.starter.base.asserts; -import com.aivfo.el.base.exception.AssertionFailedException; -import com.aivfo.el.core.function.CheckedRunnable; +import com.aivfo.el.starter.base.exception.AssertionFailedException; +import com.aivfo.el.starter.core.function.CheckedRunnable; import org.jetbrains.annotations.Nullable; import java.util.function.Supplier; diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertNull.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertNull.java similarity index 89% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertNull.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertNull.java index d383b09..2b1b99b 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertNull.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertNull.java @@ -1,7 +1,7 @@ -package com.aivfo.el.base.asserts; +package com.aivfo.el.starter.base.asserts; -import com.aivfo.el.base.exception.AssertionFailedException; -import com.aivfo.el.core.function.CheckedRunnable; +import com.aivfo.el.starter.base.exception.AssertionFailedException; +import com.aivfo.el.starter.core.function.CheckedRunnable; import org.jetbrains.annotations.Nullable; import java.util.function.Supplier; diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertTrue.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertTrue.java similarity index 93% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertTrue.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertTrue.java index 685fb16..076ce9a 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertTrue.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertTrue.java @@ -1,7 +1,7 @@ -package com.aivfo.el.base.asserts; +package com.aivfo.el.starter.base.asserts; -import com.aivfo.el.base.exception.AssertionFailedException; -import com.aivfo.el.core.function.CheckedRunnable; +import com.aivfo.el.starter.base.exception.AssertionFailedException; +import com.aivfo.el.starter.core.function.CheckedRunnable; import java.util.function.BooleanSupplier; import java.util.function.Supplier; diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertUtils.java similarity index 93% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertUtils.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertUtils.java index 0b22479..aedb08e 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/AssertUtils.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/AssertUtils.java @@ -1,9 +1,9 @@ -package com.aivfo.el.base.asserts; +package com.aivfo.el.starter.base.asserts; -import com.aivfo.el.base.exception.AivfoBaseException; -import com.aivfo.el.base.exception.AssertionFailedException; -import com.aivfo.el.base.utils.AivfoStringUtils; -import com.aivfo.el.core.function.CheckedRunnable; +import com.aivfo.el.starter.base.exception.AssertionFailedException; +import com.aivfo.el.starter.base.exception.BasicException; +import com.aivfo.el.starter.base.utils.StringUtils; +import com.aivfo.el.starter.core.function.CheckedRunnable; import lombok.SneakyThrows; import lombok.experimental.UtilityClass; import org.jetbrains.annotations.Contract; @@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.function.Supplier; + @UtilityClass class AssertUtils { @@ -79,7 +80,7 @@ class AssertUtils { String className = (obj != null ? obj.getClass().getName() : "null"); String result = ""; boolean defaultMessage = true; - if (AivfoStringUtils.hasLength(message)) { + if (StringUtils.hasLength(message)) { if (endsWithSeparator(message)) { result = message + " "; } else { @@ -90,7 +91,7 @@ class AssertUtils { if (defaultMessage) { result = result + ("Object 类型 [" + className + "] 不是: " + type); } - throw new AivfoBaseException(result); + throw new BasicException(result); } /** @@ -104,7 +105,7 @@ class AssertUtils { static void assignableCheckFailed(Class superType, @Nullable Class subType, @Nullable String message) { String result = ""; boolean defaultMessage = true; - if (AivfoStringUtils.hasLength(message)) { + if (StringUtils.hasLength(message)) { if (endsWithSeparator(message)) { result = message + " "; } else { @@ -115,7 +116,7 @@ class AssertUtils { if (defaultMessage) { result = result + (subType + " 不是 " + superType + "的子类"); } - throw new AivfoBaseException(result); + throw new BasicException(result); } /** diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/Assertions.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/Assertions.java similarity index 98% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/Assertions.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/Assertions.java index c9eaab8..4028793 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/asserts/Assertions.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/asserts/Assertions.java @@ -1,15 +1,15 @@ -package com.aivfo.el.base.asserts; +package com.aivfo.el.starter.base.asserts; -import com.aivfo.el.base.exception.AssertionFailedException; -import com.aivfo.el.base.utils.AivfoCollectionUtils; -import com.aivfo.el.base.utils.AivfoStringUtils; -import com.aivfo.el.core.function.CheckedCallable; -import com.aivfo.el.core.function.CheckedRunnable; +import com.aivfo.el.starter.base.exception.AssertionFailedException; +import com.aivfo.el.starter.base.utils.StringUtils; +import com.aivfo.el.starter.core.function.CheckedCallable; +import com.aivfo.el.starter.core.function.CheckedRunnable; import lombok.experimental.UtilityClass; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.util.ClassUtils; +import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import java.util.Collection; @@ -168,7 +168,7 @@ public class Assertions { public static void notBlank(@Nullable String content, Supplier exceptionSupplier, CheckedRunnable runnable) { - if (!AivfoStringUtils.hasText(content)) { + if (!StringUtils.hasText(content)) { AssertUtils.fail(exceptionSupplier, runnable); } } @@ -459,8 +459,8 @@ public class Assertions { */ public static void doesNotContain(@Nullable String textToSearch, String substring, Supplier exceptionSupplier) { - if (AivfoStringUtils.hasLength(textToSearch) - && AivfoStringUtils.hasLength(substring) + if (StringUtils.hasLength(textToSearch) + && StringUtils.hasLength(substring) && textToSearch.contains(substring)) { AssertUtils.fail(exceptionSupplier); } @@ -495,7 +495,7 @@ public class Assertions { * @since 1.6.0 */ public static void notEmpty(@Nullable Map map, Supplier exceptionSupplier) { - if (AivfoCollectionUtils.isEmpty(map)) { + if (CollectionUtils.isEmpty(map)) { AssertUtils.fail(exceptionSupplier); } } @@ -602,7 +602,7 @@ public class Assertions { * @since 1.6.0 */ public static void notEmpty(@Nullable Collection collection, Supplier exceptionSupplier) { - if (AivfoCollectionUtils.isEmpty(collection)) { + if (CollectionUtils.isEmpty(collection)) { AssertUtils.fail(exceptionSupplier); } } diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/common/enums/LibraryEnum.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/common/enums/LibraryEnum.java new file mode 100644 index 0000000..8648179 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/common/enums/LibraryEnum.java @@ -0,0 +1,65 @@ +package com.aivfo.el.starter.base.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 启动成功后将实现指定的提示信息, 主要用于 starter 的自动装配类 + */ +@Getter +@AllArgsConstructor +public enum LibraryEnum { + + /** + * Rest library enum + */ + REST("Rest", LibraryEnum.START_URL), + /** + * Agent library enum + */ + AGENT("Agent", "/agent/ping"), + /** + * Dubbo library enum + */ + DUBBO("Dubbo", ""), + /** + * Druid library enum + */ + DRUID("Druid", "/druid/"), + /** + * Swagger rest default library enum + */ + SWAGGER_REST_DEFAULT("Swagger(D)", "/swagger-ui.html"), + /** + * Swagger rest bootstrap library enum + */ + SWAGGER_REST_BOOTSTRAP("Swagger(B)", "/doc.html"), + /** + * Swagger dubbo json + */ + SWAGGER_DUBBO_JSON("Swagger(Dubbo API)", "/swagger-dubbo/api-docs"), + /** + * Swagger dubbo library enum + */ + SWAGGER_DUBBO("Swagger(Dubbo)", "/dubbo.html"), + /** + * Swagger json library enum + */ + SWAGGER_JSON("Swagger(API)", "/v2/api-docs"); + + /** + * START_URL + */ + public static final String START_URL = "/actuator/info"; + + /** + * Name + */ + private final String name; + /** + * Uri + */ + private final String uri; +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/common/util/JustOnceLogger.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/common/util/JustOnceLogger.java new file mode 100644 index 0000000..667b607 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/common/util/JustOnceLogger.java @@ -0,0 +1,96 @@ +package com.aivfo.el.starter.base.common.util; + +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListSet; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 只会输出一次的日志 + */ +@Slf4j +@UtilityClass +public class JustOnceLogger { + + /** + * KNOWN_LOGS + */ + private static final Map> KNOWN_LOGS = new ConcurrentHashMap<>(); + + /** + * Info once + * + * @param loggerName logger name + * @param message message + * @since 1.7.0 + */ + public static void infoOnce(String loggerName, String message) { + if (!log.isInfoEnabled()) { + return; + } + if (check(loggerName, message)) { + return; + } + log.info(message); + } + + /** + * Warn once * + * + * @param loggerName logger name + * @param message message + * @since 1.0.0 + */ + public static void warnOnce(String loggerName, String message) { + if (!log.isWarnEnabled()) { + return; + } + if (check(loggerName, message)) { + return; + } + log.warn(message); + } + + /** + * Print once + * + * @param loggerName logger name + * @param message message + * @since 1.5.0 + */ + public static void printOnce(String loggerName, String message) { + if (check(loggerName, message)) { + return; + } + System.err.println(message); + } + + /** + * Check + * + * @param loggerName logger name + * @param message message + * @return the boolean + * @since 1.5.0 + */ + private static boolean check(String loggerName, String message) { + if (!KNOWN_LOGS.containsKey(loggerName)) { + KNOWN_LOGS.put(loggerName, new ConcurrentSkipListSet<>(Collections.singleton(message))); + } else { + + Set messages = KNOWN_LOGS.get(loggerName); + if (messages.contains(message)) { + return true; + } + messages.add(message); + } + return false; + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/constant/BasicConstant.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/constant/BasicConstant.java new file mode 100644 index 0000000..72f528a --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/constant/BasicConstant.java @@ -0,0 +1,54 @@ +package com.aivfo.el.starter.base.constant; + +import lombok.experimental.UtilityClass; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: + */ +@UtilityClass +public final class BasicConstant { + /** + * BOOST_EXECUTOR + */ + public static final String BOOST_EXECUTOR = "boostExecutor"; + /** + * DYNAMIC_EXECUTOR + */ + public static final String DYNAMIC_EXECUTOR = "dynamicExecutor"; + /** + * BOOST_EXECUTORSERVICE + */ + public static final String BOOST_EXECUTORSERVICE = "boostExecutorService"; + /** + * JSON + */ + public static final String JSON = "json"; + /** + * TRACE_ID + */ + public static final String TRACE_ID = "traceId"; + /** + * 客户端标识 + */ + public static final String HEADER_CLIENT_ID = "X-Client-Id"; + /** + * 网关写入的原始请求地址 + */ + public static final String X_GATEWAY_ORIGINAL = "X-Gatetay-Original"; + /** + * 网关写入的路由日志 + */ + public static final String X_GATEWAY_ROUTER = "X-Gatetay-Router"; + /** + * 对 rest 和 agent 结果进行解包/装包 的字段名 + */ + public static final String RESULT_WRAPPER_VALUE_KEY = "value"; + + /** + * REQUEST_EXCEPTION_INFO_ATTR + */ + public static final String REQUEST_EXCEPTION_INFO_ATTR = "javax.servlet.error.exception"; + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/constant/ConfigDefaultValue.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/constant/ConfigDefaultValue.java new file mode 100644 index 0000000..4eb70c4 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/constant/ConfigDefaultValue.java @@ -0,0 +1,60 @@ +package com.aivfo.el.starter.base.constant; + +import com.aivfo.el.starter.base.utils.StringPool; +import lombok.experimental.UtilityClass; + +/** + * @author: wangyl + * @date: 2023/4/18 + * @description: 常用的配置默认值 + */ +@UtilityClass +public final class ConfigDefaultValue { + + /** + * DEFAULT_PROPERTY_INCLUSION_VALUE + */ + public static final String DEFAULT_PROPERTY_INCLUSION_VALUE = "non_null"; + /** + * DEFAULT_ENCRYPTOR_PASSWORD + */ + public static final String DEFAULT_ENCRYPTOR_PASSWORD = "06020986-3127-40be-9134-90fd033896a1"; + /** + * DEFAULT_TIME_ZONE + */ + public static final String DEFAULT_TIME_ZONE = "GMT+8"; + /** + * DEFAULT_DATE_FORMAT + */ + public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; + /** + * TRUE_STRING + */ + public static final String TRUE_STRING = "true"; + /** + * TRUE + */ + public static final Boolean TRUE = Boolean.TRUE; + /** + * FALSE_STRING + */ + public static final String FALSE_STRING = "false"; + /** + * FALSE + */ + public static final Boolean FALSE = Boolean.FALSE; + /** + * EMPTY + */ + public static final String EMPTY = StringPool.EMPTY; + /** + * 应用启动成功后, 将在此路径下创建一个 app.pid 文件, 内容是当前应用的 pid + */ + public static final String PROP_PID_FILE = "./app.pid"; + + /** + * BASE_PACKAGES + */ + public static final String BASE_PACKAGES = System.getProperty("PARENT_PACKAGE_NAME", "com.aivfo"); + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/constant/ConfigKey.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/constant/ConfigKey.java new file mode 100644 index 0000000..71e8cb9 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/constant/ConfigKey.java @@ -0,0 +1,156 @@ +package com.aivfo.el.starter.base.constant; + +import com.aivfo.el.starter.base.utils.StringPool; +import lombok.experimental.UtilityClass; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 所有的配置常量 + */ +@UtilityClass +@SuppressWarnings("all") +public final class ConfigKey { + /** + * PARENT_PACKAGE_NAME + */ + public static final String PARENT_PACKAGE_NAME = "PARENT_PACKAGE_NAME"; + /** + * 框架版本 + */ + public static final String APPLICATION_FRAMEWORK_VERSION = "aivfo.framework.version"; + /** + * EXTEND_ENABLE_AUTOWIRED_IS_NULL + */ + public static final String EXTEND_ENABLE_AUTOWIRED_IS_NULL = "aivfo.extend.enable-autowired-is-null"; + /** + * EXTEND_ENABLE_RESOURCE_IS_NULL + */ + public static final String EXTEND_ENABLE_RESOURCE_IS_NULL = "aivfo.extend.enable-resource-is-null"; + /** + * REST_ENABLE_GLOBAL_CACHE_FILTER + */ + public static final String WEB_ENABLE_GLOBAL_CACHE_FILTER = "aivfo.web.enable-global-cache-filter"; + /** + * REST_ENABLE_EXCEPTION_FILTER + */ + public static final String WEB_ENABLE_EXCEPTION_FILTER = "aivfo.web.enable-exception-filter"; + /** + * REST_ENABLE_REQUEST_LOG + */ + public static final String REST_ENABLE_REQUEST_LOG = "aivfo.rest.enable-request-log"; + /** + * REST_ENABLE_GLOBAL_PARAMETER_FILTER + */ + public static final String REST_ENABLE_GLOBAL_PARAMETER_FILTER = "aivfo.rest.enable-global-parameter-filter"; + /** + * XSS_ENABLE_XSS_FILTER + */ + public static final String XSS_ENABLE_XSS_FILTER = "aivfo.xss.enable-xss-filter"; + /** + * JSON_TIME_ZONE + */ + public static final String JSON_TIME_ZONE = "aivfo.rest.json.time-zone"; + /** + * JSON_DATE_FORMAT + */ + public static final String JSON_DATE_FORMAT = "aivfo.rest.json.date-formate"; + /** + * jackson null 处理配置 + */ + public static final String JSON_SERIALIZE_NULL = "aivfo.rest.json.default-property-inclusion"; + /** + * MYBATIS_ENABLE_ILLEGAL_SQL_INTERCEPTOR + */ + public static final String MYBATIS_ENABLE_ILLEGAL_SQL_INTERCEPTOR = "aivfo.mybatis.enable-illegal-sql-interceptor"; + /** + * MYBATIS_ENABLE_SQL_EXPLAIN_INTERCEPTOR + */ + public static final String MYBATIS_ENABLE_SQL_EXPLAIN_INTERCEPTOR = "aivfo.mybatis.enable-sql-explain-interceptor"; + /** + * POM_INFO_VERSION + */ + public static final String POM_INFO_VERSION = "info.version"; + /** + * POM_INFO_GROUPID + */ + public static final String POM_INFO_GROUPID = "info.groupId"; + /** + * POM_INFO_ARTIFACTID + */ + public static final String POM_INFO_ARTIFACTID = "info.artifactId"; + /** + * SERVICE_VERSION + */ + public static final String SERVICE_VERSION = "aivfo.service.version"; + /** + * JASYPT_ENCRYPTOR_PASSWORD + */ + public static final String JASYPT_ENCRYPTOR_PASSWORD = "jasypt.encryptor.password"; + /** + * 是否开启 banner + */ + public static final String AIVFO_ENABLE_BANNER = "aivfo.enable-banner"; + /** + * 应用分组 + */ + public static final String AIVFO_APP_GROUP = "aivfo.app.group"; + /** + * aivfo_APP_CONFIG_GROUP + */ + public static final String AIVFO_APP_CONFIG_GROUP = "aivfo.app.config-group"; + /** + * DISCOVERY_GROUP + */ + public static final String AIVFO_APP_DISCOVERY_GROUP = "aivfo.app.discovery-group"; + + @UtilityClass + public static class SpringConfigKey { + /** + * SERVER_CONTEXT_PATH + */ + public static final String SERVER_CONTEXT_PATH = "server.servlet.context-path"; + /** + * SERVER_PORT + */ + public static final String SERVER_PORT = "server.port"; + /** + * MANAGEMENT_SERVER_PORT + */ + public static final String MANAGEMENT_SERVER_PORT = "management.server.port"; + /** + * PROP_APPLICATION_NAME + */ + public static final String APPLICATION_NAME = "spring.application.name"; + /** + * PACKAGE_NAME + */ + public static final String PACKAGE_NAME = "package.name"; + /** + * PROFILE_ACTIVE + */ + public static final String PROFILE_ACTIVE = "spring.profiles.active"; + /** + * MAIN_ALLOW_BEAN_DEFINITION_OVERRIDING + */ + public static final String MAIN_ALLOW_BEAN_DEFINITION_OVERRIDING = "spring.main.allow-bean-definition-overriding"; + /** + * 根据此配置判断是否创建 pid 文件 + */ + public static final String PID_FILE = "spring.pid.file"; + /** + * 排除的自动装配类 + */ + public static final String AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude"; + /** + * jackson 序列化配置 + */ + public static final String JACKSON_DEFAULT_PROPERTY_INCLUSION = "spring.jackson.default-property-inclusion"; + /** + * DATASOURCE_URL + */ + public static final String DATASOURCE_URL = "spring.datasource.url"; + } + + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/contex/AgentRequestContextHolder.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/contex/AgentRequestContextHolder.java new file mode 100644 index 0000000..1128c25 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/contex/AgentRequestContextHolder.java @@ -0,0 +1,68 @@ +package com.aivfo.el.starter.base.contex; + +import com.alibaba.ttl.TransmittableThreadLocal; +import lombok.experimental.UtilityClass; +import org.springframework.lang.Nullable; +import org.springframework.web.context.request.RequestAttributes; + +@UtilityClass +public class AgentRequestContextHolder { + + /** + * inheritableRequestAttributesHolder + */ + private static final TransmittableThreadLocal INHERITABLE_REQUEST_ATTRIBUTES_HOLDER + = new TransmittableThreadLocal<>(); + + /** + * Reset the RequestAttributes for the current thread. + * + * @since 1.8.0 + */ + public static void resetRequestAttributes() { + INHERITABLE_REQUEST_ATTRIBUTES_HOLDER.remove(); + } + + /** + * Bind the given RequestAttributes to the current thread, + * not exposing it as inheritable for child threads. + * + * @param attributes the RequestAttributes to expose + * @since 1.8.0 + */ + public static void setRequestAttributes(@Nullable RequestAttributes attributes) { + if (attributes == null) { + resetRequestAttributes(); + } else { + INHERITABLE_REQUEST_ATTRIBUTES_HOLDER.set(attributes); + } + } + + /** + * Return the RequestAttributes currently bound to the thread. + * + * @return the RequestAttributes currently bound to the thread, or {@code null} if none bound + * @since 1.8.0 + */ + @Nullable + public static RequestAttributes getRequestAttributes() { + return INHERITABLE_REQUEST_ATTRIBUTES_HOLDER.get(); + } + + + /** + * Current request attributes + * + * @return the request attributes + * @throws IllegalStateException illegal state exception + * @since 1.8.0 + */ + public static RequestAttributes currentRequestAttributes() throws IllegalStateException { + RequestAttributes attributes = getRequestAttributes(); + if (attributes == null) { + throw new IllegalStateException("当前线程不存在 Request 对象"); + } + return attributes; + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/contex/GlobalContext.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/contex/GlobalContext.java new file mode 100644 index 0000000..df077f4 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/contex/GlobalContext.java @@ -0,0 +1,74 @@ +package com.aivfo.el.starter.base.contex; + +import com.alibaba.ttl.TransmittableThreadLocal; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: 全局上下文 + */ +@Slf4j +public final class GlobalContext { + + /** + * 公共使用 + */ + private static final TransmittableThreadLocal> COMMON = new TransmittableThreadLocal<>(); + + /** + * common + * + * @param key key + * @return the transmittable thread local + * @since 2022.1.1 + */ + @Contract(pure = true) + public static @Nullable Object get(String key) { + Map cache = COMMON.get(); + if (cache == null) { + return null; + } else { + return cache.get(key); + } + } + + /** + * Put + * + * @param key key + * @param value value + * @since 2022.1.1 + */ + @Contract(pure = true) + public static void put(String key, Object value) { + Map cache = COMMON.get(); + if (cache == null) { + cache = new HashMap<>(8); + cache.put(key, value); + COMMON.set(cache); + } else if (cache.get(key) != null) { + // 直接覆盖 + cache.put(key, value); + } + } + + /** + * Clear + * + * @param key key + * @since 2022.1.1 + */ + @Contract(pure = true) + public static void clear(String key) { + final Map cache = COMMON.get(); + if (cache != null) { + cache.remove(key); + } + } +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/contex/TraceContext.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/contex/Trace.java similarity index 72% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/contex/TraceContext.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/contex/Trace.java index 7614600..778e19a 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/contex/TraceContext.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/contex/Trace.java @@ -1,4 +1,4 @@ -package com.aivfo.el.base.contex; +package com.aivfo.el.starter.base.contex; import com.alibaba.ttl.TransmittableThreadLocal; import lombok.experimental.UtilityClass; @@ -6,11 +6,11 @@ import org.jetbrains.annotations.Contract; /** * @author: wangyl - * @date: 2023/4/14 - * @description: 链路id的上下文 + * @date: 2023/5/16 + * @description: TraceId 用来存储traceID相关信息 */ @UtilityClass -public class TraceContext { +public class Trace { /** * context */ @@ -23,8 +23,8 @@ public class TraceContext { * @since 1.6.0 */ @Contract(pure = true) - public static String getTraceId() { - return CONTEXT.get(); + public static TransmittableThreadLocal context() { + return CONTEXT; } /** diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/exception/AssertionFailedException.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/exception/AssertionFailedException.java similarity index 84% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/exception/AssertionFailedException.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/exception/AssertionFailedException.java index db16c76..188fd23 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/exception/AssertionFailedException.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/exception/AssertionFailedException.java @@ -1,11 +1,11 @@ -package com.aivfo.el.base.exception; +package com.aivfo.el.starter.base.exception; /** * @author: wangyl * @date: 2023/4/14 * @description: Assert异常信息 */ -public class AssertionFailedException extends AivfoBaseException { +public class AssertionFailedException extends BasicException { /** * DEFAULT_MESSAGE diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/exception/BasicException.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/exception/BasicException.java new file mode 100644 index 0000000..6dea1dd --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/exception/BasicException.java @@ -0,0 +1,157 @@ +package com.aivfo.el.starter.base.exception; + +import com.aivfo.el.starter.base.contex.Trace; +import com.aivfo.el.starter.base.support.StrFormatter; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.io.PrintStream; + +/** + * @author: wangyl + * @date: 2023/4/14 + * @description: 统一异常 + */ +@Slf4j +public class BasicException extends RuntimeException { + /** + * serialVersionUID + */ + private static final long serialVersionUID = 3076052230646484392L; + /** + * 异常消息参数 + */ + protected transient Object[] args; + /** + * Code + */ + @Getter + protected String code; + /** + * Message + */ + protected String message; + /** + * Trace id + */ + @Getter + protected String traceId; + /** + * DEFAULT_ERROR_CODE + */ + public static final String DEFAULT_ERROR_CODE = "B.F-5000"; + /** + * DEFAULT_MESSAGE + */ + public static final String DEFAULT_MESSAGE = "服务内部错误"; + + /** + * Basic exception + * + * @since 1.4.0 + */ + public BasicException() { + this(DEFAULT_MESSAGE); + } + + /** + * msg 占位符替换 + * + * @param message message + * @param args args + * @since 1.0.0 + */ + public BasicException(String message, Object... args) { + this(StrFormatter.mergeFormat(message, args)); + } + + /** + * Instantiates a new Base exception. + * + * @param message message + * @since 1.0.0 + */ + public BasicException(String message) { + this(DEFAULT_ERROR_CODE, message); + } + + /** + * Basic exception + * + * @param code code + * @param message message + * @since 1.6.0 + */ + public BasicException(String code, String message) { + super(message); + this.init(code, message); + } + + /** + * Base exception + * + * @param cause cause + * @since 1.0.0 + */ + public BasicException(Throwable cause) { + this(DEFAULT_MESSAGE, cause); + } + + /** + * Base exception + * + * @param message message + * @param cause cause + * @since 1.0.0 + */ + public BasicException(String message, Throwable cause) { + this(DEFAULT_ERROR_CODE, message, cause); + } + + /** + * Basic exception + * + * @param code code + * @param message message + * @param cause cause + * @since 1.6.0 + */ + public BasicException(String code, String message, Throwable cause) { + super(message, cause); + this.init(code, message); + } + + /** + * Init + * + * @param code code + * @param message message + * @since 1.9.0 + */ + private void init(String code, String message) { + this.code = code; + this.message = message; + this.traceId = Trace.context().get(); + } + + /** + * 重写打印异常堆栈, 转为日志输出. + * + * @since 1.0.0 + */ + @Override + public void printStackTrace() { + log.error("", this); + } + + /** + * Prints this throwable and its backtrace to the specified print stream. + * + * @param s {@code PrintStream} to use for output + * @since 1.6.0 + */ + @Override + public void printStackTrace(PrintStream s) { + log.error("", this); + } +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/exception/ServiceInternalException.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/exception/ServiceInternalException.java new file mode 100644 index 0000000..e25aee1 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/exception/ServiceInternalException.java @@ -0,0 +1,144 @@ +package com.aivfo.el.starter.base.exception; + +import com.aivfo.el.starter.base.utils.StringUtils; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.io.PrintStream; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: 服务内部错误异常, 会输出堆栈信息, traceId, 当前应用名和 ip + */ +@Slf4j +public class ServiceInternalException extends RuntimeException { + /** + * serialVersionUID + */ + private static final long serialVersionUID = -3466744408568354601L; + + /** + * 应用名 + */ + @Setter + @Getter + protected String applicationName; + /** + * 应用 ip + */ + @Setter + @Getter + protected String ip; + /** + * 应用端口 + */ + @Setter + @Getter + protected Integer port; + /** + * 当前环境 + */ + @Setter + @Getter + protected String env; + /** + * Rpc + */ + @Setter + @Getter + protected boolean rpc; + /** + * Trace id + */ + @Setter + @Getter + protected String traceId; + /** + * DEFAULT_ERROR_CODE + */ + public static final String DEFAULT_ERROR_CODE = "S.I-5000"; + /** + * DEFAULT_MESSAGE + */ + public static final String DEFAULT_MESSAGE = "服务内部错误"; + + /** + * Basic exception + * + * @since 1.7.0 + */ + public ServiceInternalException() { + super(); + } + + /** + * Service internal exception + * + * @param throwable throwable + * @since 1.7.0 + */ + public ServiceInternalException(Throwable throwable) { + super(DEFAULT_MESSAGE, throwable); + } + + /** + * Service internal exception + * + * @param message message + * @since 1.7.0 + */ + public ServiceInternalException(String message) { + super(message); + } + + /** + * Service internal exception + * + * @param message message + * @param throwable throwable + * @since 1.7.0 + */ + public ServiceInternalException(String message, Throwable throwable) { + super(message, throwable); + } + + /** + * 重写打印异常堆栈, 转为日志输出. + * + * @since 1.7.0 + */ + @Override + public void printStackTrace() { + log.error("", this); + } + + /** + * Prints this throwable and its backtrace to the specified print stream. + * + * @param s {@code PrintStream} to use for output + * @since 1.7.0 + */ + @Override + public void printStackTrace(PrintStream s) { + log.error("", this); + } + + /** + * Gets message * + * + * @return the message + * @since 1.7.0 + */ + @Override + public String getMessage() { + return super.getMessage() + + (StringUtils.isBlank(this.applicationName) ? "" : ": applicationName='" + this.applicationName + '\'') + + (StringUtils.isBlank(this.ip) ? "" : ", ip='" + this.ip + '\'') + + (this.port == null ? "" : ", port='" + this.port + '\'') + + (StringUtils.isBlank(this.env) ? "" : ", env='" + this.env + '\'') + + (StringUtils.isBlank(this.traceId) ? "" : ", traceId='" + this.traceId); + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/exception/TimeoutUtilsExecuteException.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/exception/TimeoutUtilsExecuteException.java new file mode 100644 index 0000000..3865d90 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/exception/TimeoutUtilsExecuteException.java @@ -0,0 +1,24 @@ +package com.aivfo.el.starter.base.exception; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 超时工具异常 + */ +public class TimeoutUtilsExecuteException extends Exception { + /** + * serialVersionUID + */ + private static final long serialVersionUID = 5896555103722435483L; + + /** + * Timeout utils execute exception + * + * @param message message + * @since 1.7.1 + */ + public TimeoutUtilsExecuteException(String message) { + super(message); + + } +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/serialize/StringTrimmerDeserializer.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/serialize/StringTrimmerDeserializer.java similarity index 80% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/serialize/StringTrimmerDeserializer.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/serialize/StringTrimmerDeserializer.java index 91e4722..ca19621 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/serialize/StringTrimmerDeserializer.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/serialize/StringTrimmerDeserializer.java @@ -1,6 +1,6 @@ -package com.aivfo.el.base.serialize; +package com.aivfo.el.starter.base.serialize; -import com.aivfo.el.base.utils.AivfoStringUtils; +import com.aivfo.el.starter.base.utils.StringUtils; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; @@ -21,7 +21,7 @@ public class StringTrimmerDeserializer extends JsonDeserializer { @Override @SuppressWarnings("java:S3252") public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { - return AivfoStringUtils.trimWhitespace(jsonParser.getValueAsString()); + return StringUtils.trimWhitespace(jsonParser.getValueAsString()); } } diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/serialize/StringTrimmerSerializer.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/serialize/StringTrimmerSerializer.java similarity index 80% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/serialize/StringTrimmerSerializer.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/serialize/StringTrimmerSerializer.java index 81250fc..04f177f 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/serialize/StringTrimmerSerializer.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/serialize/StringTrimmerSerializer.java @@ -1,9 +1,10 @@ -package com.aivfo.el.base.serialize; +package com.aivfo.el.starter.base.serialize; -import com.aivfo.el.base.utils.AivfoStringUtils; +import com.aivfo.el.starter.base.utils.StringUtils; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; + import java.io.IOException; public class StringTrimmerSerializer extends JsonSerializer { @@ -20,6 +21,6 @@ public class StringTrimmerSerializer extends JsonSerializer { @Override @SuppressWarnings("java:S3252") public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { - jsonGenerator.writeString(AivfoStringUtils.trimWhitespace(s)); + jsonGenerator.writeString(StringUtils.trimWhitespace(s)); } } diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/support/FastStringWriter.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/support/FastStringWriter.java similarity index 92% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/support/FastStringWriter.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/support/FastStringWriter.java index a038a1d..64c7302 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/support/FastStringWriter.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/support/FastStringWriter.java @@ -1,6 +1,6 @@ -package com.aivfo.el.base.support; +package com.aivfo.el.starter.base.support; -import com.aivfo.el.base.utils.AivfoStringPool; +import com.aivfo.el.starter.base.utils.StringPool; import org.jetbrains.annotations.NotNull; import org.springframework.lang.Nullable; @@ -12,7 +12,9 @@ import java.io.Writer; * @description: 借助 StringBuilder 提供快读的字符串写出, 相比 jdk 的 StringWriter 非线程安全, 速度更快. */ public class FastStringWriter extends Writer { - /** Builder */ + /** + * Builder + */ private final StringBuilder builder; /** @@ -90,7 +92,7 @@ public class FastStringWriter extends Writer { @Override public void write(char[] cbuilder, int off, int len) { if ((off < 0) || (off > cbuilder.length) - || (len < 0) || ((off + len) > cbuilder.length) || ((off + len) < 0)) { + || (len < 0) || ((off + len) > cbuilder.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; @@ -132,7 +134,7 @@ public class FastStringWriter extends Writer { @Override public FastStringWriter append(CharSequence csq) { if (csq == null) { - this.write(AivfoStringPool.NULL); + this.write(StringPool.NULL); } else { this.write(csq.toString()); } @@ -150,7 +152,7 @@ public class FastStringWriter extends Writer { */ @Override public FastStringWriter append(CharSequence csq, int start, int end) { - CharSequence cs = (csq == null ? AivfoStringPool.NULL : csq); + CharSequence cs = (csq == null ? StringPool.NULL : csq); this.write(cs.subSequence(start, end).toString()); return this; } diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/support/StrFormatter.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/support/StrFormatter.java new file mode 100644 index 0000000..67eba7e --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/support/StrFormatter.java @@ -0,0 +1,131 @@ +package com.aivfo.el.starter.base.support; + +import com.aivfo.el.starter.base.utils.CharPool; +import com.aivfo.el.starter.base.utils.StringPool; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.util.StringUtils; + +import java.text.MessageFormat; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: 字符串格式化 + */ +@UtilityClass +public class StrFormatter { + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用: format("this is {} for {}", "a", "b") =》 this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") =》 this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") =》 this is \a for b
+ * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 string + * @since 1.0.0 + */ + @SuppressWarnings("checkstyle:ReturnCount") + public static String format(String strPattern, Object... argArray) { + boolean isEmpty = argArray == null || argArray.length == 0; + if (!StringUtils.hasText(strPattern) || isEmpty) { + return strPattern; + } + int strPatternLength = strPattern.length(); + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + // 记录已经处理到的位置 + int handledPosition = 0; + // 占位符所在位置 + int delimIndex; + for (int argIndex = 0, length = argArray.length; argIndex < length; argIndex++) { + delimIndex = strPattern.indexOf(StringPool.EMPTY_JSON, handledPosition); + // 剩余部分无占位符 + if (delimIndex == -1) { + // 不带占位符的模板直接返回 + if (handledPosition == 0) { + return strPattern; + } else { + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } else { + // 转义符 + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == CharPool.BACK_SLASH) { + // 双转义符 + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == CharPool.BACK_SLASH) { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(toStr(argArray[argIndex])); + handledPosition = delimIndex + 2; + } else { + //占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(StringPool.LEFT_BRACE); + handledPosition = delimIndex + 1; + } + } else { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(toStr(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + return sbuf.toString(); + } + + /** + * To str string + * + * @param str str + * @return the string + * @since 1.0.0 + */ + @Contract(pure = true) + public static String toStr(Object str) { + return toStr(str, ""); + } + + /** + * 强转string,并去掉多余空格 + * + * @param str 字符串 + * @param defaultValue 默认值 + * @return String string + * @since 1.0.0 + */ + @Contract(value = "null, _ -> param2", pure = true) + public static String toStr(Object str, String defaultValue) { + if (null == str) { + return defaultValue; + } + return String.valueOf(str); + } + + /** + * 同时兼容 {} 和 {0} 2 种格式 + * + * @param value value + * @param params params + * @return the string + * @since 1.4.0 + */ + @NotNull + public static String mergeFormat(@NotNull String value, @NotNull Object... params) { + return params.length > 0 && value.indexOf('{') >= 0 + ? value.contains("{0") + ? MessageFormat.format(value, params) + : format(value, params) + : value; + } +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/CharPool.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/CharPool.java new file mode 100644 index 0000000..fceb9fd --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/CharPool.java @@ -0,0 +1,193 @@ +package com.aivfo.el.starter.base.utils; + +import lombok.experimental.UtilityClass; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: char 常量池 + */ +@UtilityClass +@SuppressWarnings("all") +public class CharPool { + /** + * UPPER_A + */ + public static final char UPPER_A = 'A'; + /** + * LOWER_A + */ + public static final char LOWER_A = 'a'; + /** + * UPPER_Z + */ + public static final char UPPER_Z = 'Z'; + /** + * LOWER_Z + */ + public static final char LOWER_Z = 'z'; + /** + * DOT + */ + public static final char DOT = '.'; + /** + * AT + */ + public static final char AT = '@'; + /** + * LEFT_BRACE + */ + public static final char LEFT_BRACE = '{'; + /** + * RIGHT_BRACE + */ + public static final char RIGHT_BRACE = '}'; + /** + * LEFT_BRACKET + */ + public static final char LEFT_BRACKET = '('; + /** + * RIGHT_BRACKET + */ + public static final char RIGHT_BRACKET = ')'; + /** + * DASH + */ + public static final char DASH = '-'; + /** + * PERCENT + */ + public static final char PERCENT = '%'; + /** + * PIPE + */ + public static final char PIPE = '|'; + /** + * PLUS + */ + public static final char PLUS = '+'; + /** + * QUESTION_MARK + */ + public static final char QUESTION_MARK = '?'; + /** + * EXCLAMATION_MARK + */ + public static final char EXCLAMATION_MARK = '!'; + /** + * EQUALS + */ + public static final char EQUALS = '='; + /** + * AMPERSAND + */ + public static final char AMPERSAND = '&'; + /** + * ASTERISK + */ + public static final char ASTERISK = '*'; + /** + * STAR + */ + public static final char STAR = ASTERISK; + /** + * BACK_SLASH + */ + public static final char BACK_SLASH = '\\'; + /** + * COLON + */ + public static final char COLON = ':'; + /** + * COMMA + */ + public static final char COMMA = ','; + /** + * DOLLAR + */ + public static final char DOLLAR = '$'; + /** + * SLASH + */ + public static final char SLASH = '/'; + /** + * HASH + */ + public static final char HASH = '#'; + /** + * HAT + */ + public static final char HAT = '^'; + /** + * LEFT_CHEV + */ + public static final char LEFT_CHEV = '<'; + /** + * NEWLINE + */ + public static final char NEWLINE = '\n'; + /** + * N + */ + public static final char N = 'n'; + /** + * Y + */ + public static final char Y = 'y'; + /** + * QUOTE + */ + public static final char QUOTE = '\"'; + /** + * RETURN + */ + public static final char RETURN = '\r'; + /** + * TAB + */ + public static final char TAB = '\t'; + /** + * RIGHT_CHEV + */ + public static final char RIGHT_CHEV = '>'; + /** + * SEMICOLON + */ + public static final char SEMICOLON = ';'; + /** + * SINGLE_QUOTE + */ + public static final char SINGLE_QUOTE = '\''; + /** + * BACKTICK + */ + public static final char BACKTICK = '`'; + /** + * SPACE + */ + public static final char SPACE = ' '; + /** + * TILDA + */ + public static final char TILDA = '~'; + /** + * LEFT_SQ_BRACKET + */ + public static final char LEFT_SQ_BRACKET = '['; + /** + * RIGHT_SQ_BRACKET + */ + public static final char RIGHT_SQ_BRACKET = ']'; + /** + * UNDERSCORE + */ + public static final char UNDERSCORE = '_'; + /** + * ONE + */ + public static final char ONE = '1'; + /** + * ZERO + */ + public static final char ZERO = '0'; +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/Charsets.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/Charsets.java new file mode 100644 index 0000000..841c1a7 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/Charsets.java @@ -0,0 +1,55 @@ +package com.aivfo.el.starter.base.utils; + +import lombok.experimental.UtilityClass; +import org.springframework.util.StringUtils; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.charset.UnsupportedCharsetException; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 字符集工具类 + */ +@UtilityClass +public class Charsets { + + /** + * 字符集ISO-8859-1 + */ + public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1; + /** + * The constant ISO_8859_1_NAME. + */ + public static final String ISO_8859_1_NAME = ISO_8859_1.name(); + /** + * 字符集GBK + */ + public static final Charset GBK = of("GBK"); + /** + * The constant GBK_NAME. + */ + public static final String GBK_NAME = GBK.name(); + /** + * 字符集utf-8 + */ + public static final Charset UTF_8 = StandardCharsets.UTF_8; + /** + * The constant UTF_8_NAME. + */ + public static final String UTF_8_NAME = UTF_8.name(); + + /** + * 转换为Charset对象 + * + * @param charsetName 字符集,为空则返回默认字符集 + * @return Charsets charset + * @throws UnsupportedCharsetException 编码不支持 + * @since 1.0.0 + */ + public static Charset of(String charsetName) throws UnsupportedCharsetException { + return StringUtils.hasText(charsetName) ? Charset.forName(charsetName) : Charset.defaultCharset(); + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/ClassUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/ClassUtils.java new file mode 100644 index 0000000..032607a --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/ClassUtils.java @@ -0,0 +1,293 @@ +package com.aivfo.el.starter.base.utils; + +import com.aivfo.el.starter.base.exception.BasicException; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.core.BridgeMethodResolver; +import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.MethodParameter; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.SynthesizingMethodParameter; +import org.springframework.util.Assert; +import org.springframework.web.method.HandlerMethod; + +import java.lang.annotation.Annotation; +import java.lang.reflect.*; +import java.util.Arrays; +import java.util.List; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 类工具类 + */ +@Slf4j +@SuppressWarnings("all") +public abstract class ClassUtils extends org.springframework.util.ClassUtils { + + /** + * PARAMETER_NAME_DISCOVERER + */ + public static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer(); + /** + * PACKAGE_SEPARATOR + */ + public static final char PACKAGE_SEPARATOR = '.'; + /** + * 代理 class 的名称 + */ + protected static final List PROXY_CLASS_NAMES = Arrays.asList( + "net.sf.cglib.proxy.Factory", + "org.springframework.cglib.proxy.Factory", + "javassist.util.proxy.ProxyObject", + "org.apache.ibatis.javassist.util.proxy.ProxyObject"); + + /** + * 获取方法参数信息 + * + * @param constructor 构造器 + * @param parameterIndex 参数序号 + * @return {MethodParameter} + * @since 1.0.0 + */ + public static @NotNull MethodParameter getMethodParameter(Constructor constructor, int parameterIndex) { + MethodParameter methodParameter = new SynthesizingMethodParameter(constructor, parameterIndex); + methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER); + return methodParameter; + } + + /** + * 获取方法参数信息 + * + * @param method 方法 + * @param parameterIndex 参数序号 + * @return {MethodParameter} + * @since 1.0.0 + */ + public static @NotNull MethodParameter getMethodParameter(Method method, int parameterIndex) { + MethodParameter methodParameter = new SynthesizingMethodParameter(method, parameterIndex); + methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER); + return methodParameter; + } + + /** + * 获取Annotation + * + * @param 泛型标记 + * @param method Method + * @param annotationType 注解类 + * @return {Annotation} + * @since 1.0.0 + */ + public static A getAnnotation(@NotNull Method method, Class annotationType) { + Class targetClass = method.getDeclaringClass(); + // The method may be on an interface, but we need attributes from the target class. + // If the target class is null, the method will be unchanged. + Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); + // If we are dealing with method with generic parameters, find the original method. + specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); + // 先找方法,再找方法上的类 + A annotation = AnnotatedElementUtils.findMergedAnnotation(specificMethod, annotationType); + if (null != annotation) { + return annotation; + } + // 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类 + return AnnotatedElementUtils.findMergedAnnotation(specificMethod.getDeclaringClass(), annotationType); + } + + /** + * 获取Annotation + * + * @param 泛型标记 + * @param handlerMethod HandlerMethod + * @param annotationType 注解类 + * @return {Annotation} + * @since 1.0.0 + */ + public static A getAnnotation(@NotNull HandlerMethod handlerMethod, Class annotationType) { + // 先找方法,再找方法上的类 + A annotation = handlerMethod.getMethodAnnotation(annotationType); + if (null != annotation) { + return annotation; + } + // 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类 + Class beanType = handlerMethod.getBeanType(); + return AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType); + } + + /** + * 获取接口上的泛型T + * + * @param clazz clazz + * @param interfaceClass interface class + * @param index 泛型索引 + * @return the interface t + * @since 1.0.0 + */ + public static @NotNull Class getInterfaceT(@NotNull Class clazz, Class interfaceClass, int index) { + Type[] types = clazz.getGenericInterfaces(); + Type targetType = Arrays.stream(types).filter(type -> type.toString().contains(interfaceClass.getName())).findAny().orElse(null); + if (targetType == null) { + throw new BasicException("[{}] 未实现 [{}] 接口", clazz, interfaceClass); + } + ParameterizedType parameterizedType = (ParameterizedType) targetType; + Type type = parameterizedType.getActualTypeArguments()[index]; + return checkType(type, index); + } + + /** + * 获取父类上的泛型 + * + * @param clazz clazz + * @param index index + * @return the class + * @since 1.0.0 + */ + public static @NotNull Class getSuperClassT(@NotNull Class clazz, @NotNull Integer index) { + Type type = clazz.getGenericSuperclass(); + ParameterizedType parameterizedType = (ParameterizedType) type; + Type actType = parameterizedType.getActualTypeArguments()[index]; + return checkType(actType, index); + } + + /** + * 递归获取泛型类型 + * + * @param type type + * @param index index + * @return the class + * @since 1.0.0 + */ + @Contract("null, _ -> fail") + private static @NotNull Class checkType(Type type, int index) { + if (type instanceof Class) { + return (Class) type; + } else if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + Type t = parameterizedType.getActualTypeArguments()[index]; + return checkType(t, index); + } else { + String className = type == null ? "null" : type.getClass().getName(); + throw new BasicException("Expected a Class, ParameterizedType" + ", but <" + type + "> is of type " + className); + } + } + + /** + * 判断传入的类型是否是布尔类型 + * + * @param type 类型 + * @return 如果是原生布尔或者包装类型布尔 , 均返回 true + * @since 1.0.0 + */ + @Contract(pure = true) + public static boolean isBoolean(Class type) { + return type == boolean.class || Boolean.class == type; + } + + /** + * 判断是否为代理对象 + * + * @param clazz 传入 class 对象 + * @return 如果对象class是代理 class, 返回 true + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isProxy(Class clazz) { + if (clazz != null) { + for (Class cls : clazz.getInterfaces()) { + if (PROXY_CLASS_NAMES.contains(cls.getName())) { + return true; + } + } + } + return false; + } + + /** + * 获取当前对象的 class + * + * @param clazz 传入 + * @return 如果是代理的class , 返回父 class, 否则返回自身 + * @since 1.0.0 + */ + public static @NotNull Class getClass(Class clazz) { + return isProxy(clazz) ? clazz.getSuperclass() : clazz; + } + + /** + * 获取当前对象的class + * + * @param object 对象 + * @return 返回对象的 user class + * @since 1.0.0 + */ + public static @NotNull Class getClass(Object object) { + Assert.notNull(object, "Error: Instance must not be null"); + return getClass(object.getClass()); + } + + /** + * 根据指定的 class , 实例化一个对象, 根据构造参数来实例化 + * 在 java9 及其之后的版本 Class.newInstance() 方法已被废弃 + * + * @param 类型, 由输入类型决定 + * @param clazz 需要实例化的对象 + * @return 返回新的实例 @ not null t + * @since 1.0.0 + */ + public static @NotNull T newInstance(@NotNull Class clazz) { + try { + Constructor constructor = clazz.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new BasicException("实例化对象时出现错误,请尝试给 %s 添加无参的构造方法", e, clazz.getName()); + } + } + + /** + * 请仅在确定类存在的情况下调用该方法 + * + * @param name 类名称 + * @return 返回转换后的 Class + * @since 1.0.0 + */ + public static @NotNull Class toClassConfident(String name) { + try { + return Class.forName(name); + } catch (ClassNotFoundException e) { + throw new BasicException("找不到指定的class!请仅在明确确定会有 class 的时候, 调用该方法", e); + } + } + + + /** + * Determine the name of the package of the given class, + * e.g. "java.lang" for the {@code java.lang.String} class. + * + * @param clazz the class + * @return the package name, or the empty String if the class is defined in the default package + * @since 1.0.0 + */ + public static @NotNull String getPackageName(Class clazz) { + Assert.notNull(clazz, "Class must not be null"); + return getPackageName(clazz.getName()); + } + + /** + * Determine the name of the package of the given fully-qualified class name, + * e.g. "java.lang" for the {@code java.lang.String} class name. + * + * @param fqClassName the fully-qualified class name + * @return the package name, or the empty String if the class is defined in the default package + * @since 1.0.0 + */ + public static @NotNull String getPackageName(String fqClassName) { + Assert.notNull(fqClassName, "Class name must not be null"); + int lastDotIndex = fqClassName.lastIndexOf(PACKAGE_SEPARATOR); + return (lastDotIndex != -1 ? fqClassName.substring(0, lastDotIndex) : ""); + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/ConcurrentDateFormat.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/ConcurrentDateFormat.java similarity index 91% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/ConcurrentDateFormat.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/ConcurrentDateFormat.java index 8b12e90..b75a566 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/ConcurrentDateFormat.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/ConcurrentDateFormat.java @@ -1,4 +1,4 @@ -package com.aivfo.el.base.utils; +package com.aivfo.el.starter.base.utils; import lombok.Getter; import org.jetbrains.annotations.Contract; @@ -14,19 +14,26 @@ import java.util.concurrent.ConcurrentLinkedQueue; /** * @author: wangyl - * @date: 2023/4/18 + * @date: 2023/5/16 * @description: 安全的时间格式化 - * 参考 tomcat8 中的并发 DateFormat, {@link SimpleDateFormat}的线程安全包装器. 不使用ThreadLocal,创建足够的SimpleDateFormat对象来满足并发性要求. */ public final class ConcurrentDateFormat { - /** Format */ + /** + * Format + */ @Getter private final String format; - /** Locale */ + /** + * Locale + */ private final Locale locale; - /** Timezone */ + /** + * Timezone + */ private final TimeZone timezone; - /** Queue */ + /** + * Queue + */ private final Queue queue = new ConcurrentLinkedQueue<>(); /** diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/Exceptions.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/Exceptions.java similarity index 85% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/Exceptions.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/Exceptions.java index b74088b..3c969ca 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/Exceptions.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/Exceptions.java @@ -1,8 +1,8 @@ -package com.aivfo.el.base.utils; +package com.aivfo.el.starter.base.utils; import cn.hutool.core.exceptions.ExceptionUtil; -import com.aivfo.el.base.exception.AivfoBaseException; -import com.aivfo.el.base.support.FastStringWriter; +import com.aivfo.el.starter.base.exception.BasicException; +import com.aivfo.el.starter.base.support.FastStringWriter; import lombok.experimental.UtilityClass; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -12,9 +12,14 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.UndeclaredThrowableException; /** - * @author: wangyl - * @date: 2023/4/18 - * @description: 异常处理工具类 + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 异常处理工具类

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.11.21 09:59 + * @since 1.0.0 */ @UtilityClass public class Exceptions extends ExceptionUtil { @@ -44,12 +49,12 @@ public class Exceptions extends ExceptionUtil { public static RuntimeException unchecked(String message, Throwable e) { RuntimeException runtimeException; if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException - || e instanceof NoSuchMethodException) { + || e instanceof NoSuchMethodException) { runtimeException = new IllegalArgumentException(e); } else if (e instanceof InvocationTargetException) { runtimeException = new RuntimeException(((InvocationTargetException) e).getTargetException()); - } else if (e instanceof AivfoBaseException) { - runtimeException = (AivfoBaseException) e; + } else if (e instanceof BasicException) { + runtimeException = (BasicException) e; } else if (e instanceof RuntimeException) { runtimeException = (RuntimeException) e; } else { diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/HostUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/HostUtils.java new file mode 100644 index 0000000..e39c2b6 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/HostUtils.java @@ -0,0 +1,216 @@ +package com.aivfo.el.starter.base.utils; + +import cn.hutool.core.io.FileUtil; +import com.aivfo.el.starter.base.support.StrFormatter; +import org.apache.commons.lang3.SystemUtils; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 修改 hosts 文件 + */ +public abstract class HostUtils { + + /** + * SPLITTER + */ + private static final String SPLITTER = " "; + + /** + * 读取 hosts, 排除注释项, 将一行记录解析为 [ip + 空格 + domain} + * + * @return the set + * @since 1.5.0 + */ + public static Set read() { + return FileUtil.readLines(getHostFile(), Charsets.UTF_8).stream() + .filter(it -> !it.trim().matches("(^#.*)|(\\s*)")) + .map(it -> it.replaceAll("#.*", "").trim().replaceAll("\\s+", SPLITTER)) + .collect(Collectors.toSet()); + } + + /** + * Combine + * + * @param ip ip + * @param domain domain + * @return the string + * @since 1.5.0 + */ + @Contract(pure = true) + public static @NotNull String combine(String ip, String domain) { + if (StringUtils.isBlank(domain)) { + throw new IllegalArgumentException("ERROR: domain must be specified"); + } + return (ip + SPLITTER + domain).trim(); + } + + /** + * Exists + * + * @param domain domain + * @return the boolean + * @since 1.5.0 + */ + public static boolean exists(String domain) { + return exists(StringPool.EMPTY, domain); + } + + /** + * Exists + * + * @param ip ip + * @param domain domain + * @return the boolean + * @since 1.5.0 + */ + public static boolean exists(String ip, String domain) { + return exists(read(), ip, domain); + } + + /** + * Exists + * + * @param records records + * @param ip ip + * @param domain domain + * @return the boolean + * @since 1.5.0 + */ + public static boolean exists(@NotNull Set records, String ip, String domain) { + String combine = combine(ip, domain); + for (String record : records) { + if (record.contains(combine)) { + return true; + } + } + return false; + } + + /** + * Get ips + * + * @param records records + * @param domain domain + * @return the set + * @since 1.5.0 + */ + public static @NotNull Set getIps(@NotNull Set records, String domain) { + Set ips = new HashSet<>(4); + + String combine = combine(StringPool.EMPTY, domain); + for (String record : records) { + if (record.contains(combine)) { + ips.add(ip(record)); + } + } + + return ips; + } + + /** + * Ip + * + * @param record record + * @return the string + * @since 1.5.0 + */ + public static String ip(@NotNull String record) { + String[] split = check(record); + return split[0]; + } + + /** + * Doamin + * + * @param record record + * @return the string + * @since 1.5.0 + */ + public static String doamin(@NotNull String record) { + String[] split = check(record); + return split[1]; + } + + /** + * Check + * + * @param record record + * @return the string [ ] + * @since 1.5.0 + */ + @NotNull + @SuppressWarnings("PMD.UndefineMagicConstantRule") + private static String[] check(@NotNull String record) { + String[] split = record.split(SPLITTER); + if (split.length != 2) { + throw new IllegalArgumentException("ERROR: ip & domain must be specified"); + } + return split; + } + + /** + * 获取 host 文件路径 + * + * @return host file + * @since 1.5.0 + */ + public static @NotNull String getHostFile() { + String fileName; + + // 判断系统 + if (SystemUtils.IS_OS_LINUX || SystemUtils.IS_OS_MAC) { + fileName = "/etc/hosts"; + } else { + fileName = System.getenv("windir") + "\\system32\\drivers\\etc\\hosts"; + } + return fileName; + } + + /** + * 根据输入 IP 和 Domain, 删除 host 文件中的某个 host 配置 + * + * @param ip ip + * @param domain domain + * @since 1.5.0 + */ + public static synchronized void deleteHost(String ip, String domain) { + String record = combine(ip, domain); + + if (exists(ip, domain)) { + Set lines = read(); + lines.removeIf(s -> s.equals(record)); + FileUtil.writeLines(lines, getHostFile(), Charsets.UTF_8); + } + } + + /** + * 根据输入 IP 和 Domain, 更新 host 文件中的某个 host 配置 + * + * @param ip ip + * @param domain domain + * @since 1.5.0 + */ + public static synchronized void updateHost(String ip, String domain) { + if (StringUtils.isAnyBlank(ip, domain)) { + throw new IllegalArgumentException("ERROR: ip & domain must be specified"); + } + + String record = combine(ip, domain); + // 先删除, 如果不存在则不操作 + deleteHost(ip, domain); + + Set lines = read(); + lines.add(record); + FileUtil.writeLines(lines, getHostFile(), Charsets.UTF_8); + + System.err.println(StrFormatter.format("更新 hosts: {} {}", ip, domain)); + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/IoUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/IoUtils.java new file mode 100644 index 0000000..fe6ad64 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/IoUtils.java @@ -0,0 +1,130 @@ +package com.aivfo.el.starter.base.utils; + +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.lang.Nullable; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: IoUtil + */ +@UtilityClass +public class IoUtils extends org.springframework.util.StreamUtils { + + /** + * byte[] 转 String, 编码默认 UTF-8 + * + * @param input input + * @return the string + * @since 1.0.0 + */ + @NotNull + public static String toString(byte[] input) { + return toString(input, Charsets.UTF_8_NAME); + } + + /** + * 使用指定的字符编码以字符串形式获取 byte[] 的内容 + * + * @param input the byte array to read from + * @param encoding the encoding to use, null means platform default + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs (never occurs) + * @since 1.0.0 + */ + @NotNull + @Contract("_, _ -> new") + public static String toString(byte[] input, String encoding) { + return new String(input, Charsets.of(encoding)); + } + + /** + * InputStream to String utf-8 + * + * @param input the InputStream to read from + * @return the requested String + * @since 1.0.0 + */ + @NotNull + public static String toString(InputStream input) { + return toString(input, Charsets.UTF_8); + } + + /** + * InputStream to String + * + * @param input the InputStream to read from + * @param charset the Charsets + * @return the requested String + * @since 1.0.0 + */ + @NotNull + public static String toString(@Nullable InputStream input, java.nio.charset.Charset charset) { + try { + return IoUtils.copyToString(input, charset); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } finally { + IoUtils.closeQuietly(input); + } + } + + /** + * closeQuietly + * + * @param closeable 自动关闭 + * @since 1.0.0 + */ + public static void closeQuietly(@Nullable Closeable closeable) { + try { + if (closeable != null) { + closeable.close(); + } + } catch (IOException ioe) { + // ignore + } + } + + /** + * To byte array byte [ ]. + * + * @param input the input + * @return the byte [ ] + * @since 1.0.0 + */ + public static byte[] toByteArray(@Nullable InputStream input) { + try { + return IoUtils.copyToByteArray(input); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } finally { + IoUtils.closeQuietly(input); + } + } + + /** + * Writes chars from a String to bytes on an + * OutputStream using the specified character encoding. + *

+ * This method uses {@link String#getBytes(String)}. + * + * @param data the String to write, null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws IOException if an I/O error occurs + * @since 1.0.0 + */ + public static void write(@Nullable String data, OutputStream output, java.nio.charset.Charset encoding) throws IOException { + if (data != null) { + output.write(data.getBytes(encoding)); + } + } +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/AivfoJsonUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/JsonUtils.java similarity index 97% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/AivfoJsonUtils.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/JsonUtils.java index fd295a9..1e14678 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/AivfoJsonUtils.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/JsonUtils.java @@ -1,10 +1,10 @@ -package com.aivfo.el.base.utils; +package com.aivfo.el.starter.base.utils; -import com.aivfo.el.base.asserts.Assertions; -import com.aivfo.el.base.constant.ConfigDefaultValue; -import com.aivfo.el.base.constant.ConfigKey; -import com.aivfo.el.base.serialize.StringTrimmerDeserializer; -import com.aivfo.el.base.serialize.StringTrimmerSerializer; +import com.aivfo.el.starter.base.asserts.Assertions; +import com.aivfo.el.starter.base.constant.ConfigDefaultValue; +import com.aivfo.el.starter.base.constant.ConfigKey; +import com.aivfo.el.starter.base.serialize.StringTrimmerDeserializer; +import com.aivfo.el.starter.base.serialize.StringTrimmerSerializer; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.core.JsonParser; @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.type.CollectionLikeType; import com.fasterxml.jackson.databind.type.MapType; import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.time.StopWatch; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,8 +33,6 @@ import org.reflections.util.ConfigurationBuilder; import org.reflections.util.FilterBuilder; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; -import org.springframework.util.StopWatch; -import org.springframework.util.StringUtils; import javax.validation.ConstraintViolation; import javax.validation.Validator; @@ -44,30 +43,32 @@ import java.text.SimpleDateFormat; import java.util.*; /** - *

Description: Jackson工具类 + * @author: wangyl + * @date: 2023/5/16 + * @description: Jackson工具类 * 系统中使用到 {@link ObjectMapper} 的地方定将用此类进行初始化, 提供 2 种方式: - * 1. {@link AivfoJsonUtils#getInstance()}; - * 2. {@link AivfoJsonUtils#getCopyMapper()}; + * 1. {@link JsonUtils#getInstance()}; + * 2. {@link JsonUtils#getCopyMapper()}; * 第一种方式将返回一个单例对象, 请确保在使用 {@link ObjectMapper} 的过程中不会修改 {@link ObjectMapper} 的配置, 如果需要定制化 {@link ObjectMapper}, * 可使用第二种方式, 此方式将每次返回一个新的 {@link ObjectMapper} 对象. * 此类每个工具方法都进行过重载, 如果需要使用自定义 {@link ObjectMapper} 对 json 处理, 请调用具有 {@link ObjectMapper} 参数的工具方法. *

- * - * @author dong4j - * @version 1.2.3 - * @email "mailto:dongshijie@fkhwl.com" - * @date 2019.12.26 21:35 - * @since 1.0.0 */ @Slf4j @UtilityClass @SuppressWarnings("checkstyle:MethodLimit") -public class AivfoJsonUtils { - /** PATTERN_DATETIME */ +public class JsonUtils { + /** + * PATTERN_DATETIME + */ public static final String PATTERN_DATETIME = System.getProperty(ConfigKey.JSON_DATE_FORMAT, ConfigDefaultValue.DEFAULT_DATE_FORMAT); - /** Empty array */ + /** + * Empty array + */ public static final byte[] EMPTY_ARRAY = new byte[0]; - /** MESSAGE */ + /** + * MESSAGE + */ private static final String MESSAGE = "待解析的数据为空"; /** @@ -81,7 +82,7 @@ public class AivfoJsonUtils { } /** - * 获取单例对象, 这里只提供全局的基础配置, 让整个应用配置保持一致, 如果需要其他配置, 请使用 {@link AivfoJsonUtils#getCopyMapper()}. + * 获取单例对象, 这里只提供全局的基础配置, 让整个应用配置保持一致, 如果需要其他配置, 请使用 {@link JsonUtils#getCopyMapper()}. * * @return the instance * @since 1.0.0 @@ -258,7 +259,7 @@ public class AivfoJsonUtils { @SuppressWarnings("java:S3252") public static String toJson(ObjectMapper mapper, Object object, boolean pretty, boolean always) { if (object == null) { - return AivfoStringPool.EMPTY_JSON; + return StringPool.EMPTY_JSON; } // 如果是 string, 先转为 object 再转为 json, 避免转义字符 if (object instanceof String) { @@ -1064,9 +1065,13 @@ public class AivfoJsonUtils { */ @SuppressWarnings("all") private static class JacksonHolder { - /** INSTANCE */ + /** + * INSTANCE + */ private static final ObjectMapper INSTANCE; - /** CHINA */ + /** + * CHINA + */ private static final Locale CHINA = Locale.CHINA; static { diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/SecurityUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/SecurityUtils.java new file mode 100644 index 0000000..64eafdd --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/SecurityUtils.java @@ -0,0 +1,115 @@ +package com.aivfo.el.starter.base.utils; + +import com.google.common.collect.Sets; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.NotNull; +import org.springframework.util.ObjectUtils; +import org.springframework.util.PathMatcher; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +@UtilityClass +public class SecurityUtils { + /** + * 应用默认的忽略 url 列表 + */ + public static final Set DEFAULT_SKIP_URL = Sets.newHashSetWithExpectedSize(32); + + static { + // 忽略应用检查接口 + DEFAULT_SKIP_URL.add("/actuator/**"); + // 忽略 swagger 相关接口 + DEFAULT_SKIP_URL.add("/v2/api-docs/**"); + DEFAULT_SKIP_URL.add("/v2/api-docs-ext/**"); + DEFAULT_SKIP_URL.add("/swagger-resources/**"); + DEFAULT_SKIP_URL.add("/swagger-dubbo/**"); + DEFAULT_SKIP_URL.add("/doc.html"); + // 忽略验证码相关接口 + DEFAULT_SKIP_URL.add("/captcha/**"); + DEFAULT_SKIP_URL.add("/request-urls"); + // 检查是否需要验证码 + DEFAULT_SKIP_URL.add("/check"); + // 忽略 oauth 相关接口 + DEFAULT_SKIP_URL.add("/oauth/**"); + // 忽略日志相关接口 + DEFAULT_SKIP_URL.add("/log/**"); + // 忽略错误接口 + DEFAULT_SKIP_URL.add("/error/**"); + // 忽略静态资源 + DEFAULT_SKIP_URL.add("/**/*.ico"); + DEFAULT_SKIP_URL.add("/**/*.css"); + DEFAULT_SKIP_URL.add("/**/*.js"); + DEFAULT_SKIP_URL.add("/**/*.html"); + DEFAULT_SKIP_URL.add("/**/*.map"); + DEFAULT_SKIP_URL.add("/**/*.svg"); + DEFAULT_SKIP_URL.add("/**/*.png"); + DEFAULT_SKIP_URL.add("/**/*.gif"); + DEFAULT_SKIP_URL.add("/**/*.bmp"); + } + + /** + * 将自定义需要忽略的 url 和默认被忽略的 url 合并 + * + * @param consumterIgnoreUrls 自定义的需要忽略的 url + * @return the set 合并后的 url + * @since 1.5.0 + */ + public static @NotNull Set mergeSkipPatterns(String consumterIgnoreUrls) { + return mergeSkipPatterns(SecurityUtils.DEFAULT_SKIP_URL, consumterIgnoreUrls); + } + + /** + * Merge skip patterns + * + * @param defaultSkipUrl default skip url + * @param consumterIgnoreUrls consumter ignore urls + * @return the set + * @since 1.5.0 + */ + public static @NotNull Set mergeSkipPatterns(Collection defaultSkipUrl, String consumterIgnoreUrls) { + Set ignores = new HashSet<>(defaultSkipUrl); + + if (StringUtils.isNotBlank(consumterIgnoreUrls)) { + String[] split = consumterIgnoreUrls.split(StringPool.COMMA); + ignores.addAll(Arrays.stream(split).filter(url -> StringUtils.hasText(url.trim())).collect(Collectors.toSet())); + } + + return ignores; + } + + /** + * 返回 true 则不进入 filter, 否则进入 filter + * + * @param lookupPath lookup path + * @param includePatterns include patterns + * @param excludePatterns exclude patterns + * @param pathMatcher path matcher + * @return the boolean + * @since 1.5.0 + */ + public static boolean matches(String lookupPath, + String[] includePatterns, + String[] excludePatterns, + PathMatcher pathMatcher) { + if (!ObjectUtils.isEmpty(excludePatterns)) { + for (String pattern : excludePatterns) { + if (pathMatcher.match(pattern, lookupPath)) { + return true; + } + } + } + if (ObjectUtils.isEmpty(includePatterns)) { + return false; + } + for (String pattern : includePatterns) { + if (pathMatcher.match(pattern, lookupPath)) { + return false; + } + } + return false; + } +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/AivfoStringPool.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/StringPool.java similarity index 56% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/AivfoStringPool.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/StringPool.java index 1d2490e..c65159f 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/base/utils/AivfoStringPool.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/StringPool.java @@ -1,131 +1,249 @@ - package com.aivfo.el.base.utils; +package com.aivfo.el.starter.base.utils; import lombok.experimental.UtilityClass; /** * @author: wangyl - * @date: 2023/4/18 + * @date: 2023/5/16 * @description: 静态 String 池 */ @UtilityClass -public final class AivfoStringPool { +public final class StringPool { - /** AMPERSAND */ + /** + * AMPERSAND + */ public static final String AMPERSAND = "&"; - /** AND */ + /** + * AND + */ public static final String AND = "and"; - /** AT */ + /** + * AT + */ public static final String AT = "@"; - /** ASTERISK */ + /** + * ASTERISK + */ public static final String ASTERISK = "*"; - /** STAR */ + /** + * STAR + */ public static final String STAR = ASTERISK; - /** SLASH */ + /** + * SLASH + */ public static final String SLASH = "/"; - /** DOUBLE_SLASH */ + /** + * DOUBLE_SLASH + */ public static final String DOUBLE_SLASH = "#//"; - /** COLON */ + /** + * COLON + */ public static final String COLON = ":"; - /** COMMA */ + /** + * COMMA + */ public static final String COMMA = ","; - /** DASH */ + /** + * DASH + */ public static final String DASH = "-"; - /** DOLLAR */ + /** + * DOLLAR + */ public static final String DOLLAR = "$"; - /** DOT */ + /** + * DOT + */ public static final String DOT = "."; - /** EMPTY */ + /** + * EMPTY + */ public static final String EMPTY = ""; - /** EMPTY_JSON */ + /** + * EMPTY_JSON + */ public static final String EMPTY_JSON = "{}"; - /** EQUALS */ + /** + * EQUALS + */ public static final String EQUALS = "="; - /** FALSE */ + /** + * FALSE + */ public static final String FALSE = "false"; - /** HASH */ + /** + * HASH + */ public static final String HASH = "#"; - /** HAT */ + /** + * HAT + */ public static final String HAT = "^"; - /** LEFT_BRACE */ + /** + * LEFT_BRACE + */ public static final String LEFT_BRACE = "{"; - /** LEFT_BRACKET */ + /** + * LEFT_BRACKET + */ public static final String LEFT_BRACKET = "("; - /** LEFT_CHEV */ + /** + * LEFT_CHEV + */ public static final String LEFT_CHEV = "<"; - /** NEWLINE */ + /** + * NEWLINE + */ public static final String NEWLINE = "\n"; - /** N */ + /** + * N + */ public static final String N = "n"; - /** NO */ + /** + * NO + */ public static final String NO = "no"; - /** NULL */ + /** + * NULL + */ public static final String NULL = "null"; - /** OFF */ + /** + * OFF + */ public static final String OFF = "off"; - /** ON */ + /** + * ON + */ public static final String ON = "on"; - /** PERCENT */ + /** + * PERCENT + */ public static final String PERCENT = "%"; - /** PIPE */ + /** + * PIPE + */ public static final String PIPE = "|"; - /** PLUS */ + /** + * PLUS + */ public static final String PLUS = "+"; - /** QUESTION_MARK */ + /** + * QUESTION_MARK + */ public static final String QUESTION_MARK = "?"; - /** EXCLAMATION_MARK */ + /** + * EXCLAMATION_MARK + */ public static final String EXCLAMATION_MARK = "!"; - /** QUOTE */ + /** + * QUOTE + */ public static final String QUOTE = "\""; - /** RETURN */ + /** + * RETURN + */ public static final String RETURN = "\r"; - /** TAB */ + /** + * TAB + */ public static final String TAB = "\t"; - /** RIGHT_BRACE */ + /** + * RIGHT_BRACE + */ public static final String RIGHT_BRACE = "}"; - /** RIGHT_BRACKET */ + /** + * RIGHT_BRACKET + */ public static final String RIGHT_BRACKET = ")"; - /** RIGHT_CHEV */ + /** + * RIGHT_CHEV + */ public static final String RIGHT_CHEV = ">"; - /** SEMICOLON */ + /** + * SEMICOLON + */ public static final String SEMICOLON = ";"; - /** SINGLE_QUOTE */ + /** + * SINGLE_QUOTE + */ public static final String SINGLE_QUOTE = "'"; - /** BACKTICK */ + /** + * BACKTICK + */ public static final String BACKTICK = "`"; - /** SPACE */ + /** + * SPACE + */ public static final String SPACE = " "; - /** TILDA */ + /** + * TILDA + */ public static final String TILDA = "~"; - /** LEFT_SQ_BRACKET */ + /** + * LEFT_SQ_BRACKET + */ public static final String LEFT_SQ_BRACKET = "["; - /** RIGHT_SQ_BRACKET */ + /** + * RIGHT_SQ_BRACKET + */ public static final String RIGHT_SQ_BRACKET = "]"; - /** TRUE */ + /** + * TRUE + */ public static final String TRUE = "true"; - /** UNDERSCORE */ + /** + * UNDERSCORE + */ public static final String UNDERSCORE = "_"; -// /** UTF_8 */ -// public static final String UTF_8 = Charsets.UTF_8_NAME; -// /** GBK */ -// public static final String GBK = Charsets.GBK_NAME; -// /** ISO_8859_1 */ -// public static final String ISO_8859_1 = Charsets.ISO_8859_1_NAME; - /** Y */ + /** + * UTF_8 + */ + public static final String UTF_8 = Charsets.UTF_8_NAME; + /** + * GBK + */ + public static final String GBK = Charsets.GBK_NAME; + /** + * ISO_8859_1 + */ + public static final String ISO_8859_1 = Charsets.ISO_8859_1_NAME; + /** + * Y + */ public static final String Y = "y"; - /** YES */ + /** + * YES + */ public static final String YES = "yes"; - /** ONE */ + /** + * ONE + */ public static final String ONE = "1"; - /** ZERO */ + /** + * ZERO + */ public static final String ZERO = "0"; - /** DOLLAR_LEFT_BRACE */ + /** + * DOLLAR_LEFT_BRACE + */ public static final String DOLLAR_LEFT_BRACE = "${"; - /** NULL_STRING */ + /** + * NULL_STRING + */ public static final String NULL_STRING = "N/A"; - /** any_Url_Patterns */ + /** + * any_Url_Patterns + */ public static final String ANY_URL_PATTERNS = SLASH + ASTERISK; - /** DOUBLE_ASTERISK */ + /** + * DOUBLE_ASTERISK + */ public static final String DOUBLE_ASTERISK = ASTERISK + ASTERISK; - /** any_PATH */ + /** + * any_PATH + */ public static final String ANY_PATH = SLASH + DOUBLE_ASTERISK; } diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/StringUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/StringUtils.java new file mode 100644 index 0000000..f03c471 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/StringUtils.java @@ -0,0 +1,1826 @@ +package com.aivfo.el.starter.base.utils; + +import cn.hutool.core.text.StrSplitter; +import com.aivfo.el.starter.base.support.StrFormatter; +import com.aivfo.el.starter.core.util.NumberUtils; +import org.apache.commons.text.StringSubstitutor; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.util.ObjectUtils; +import org.springframework.web.util.HtmlUtils; + +import java.io.StringReader; +import java.io.StringWriter; +import java.text.MessageFormat; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 继承自Spring util的工具类 + */ +@SuppressWarnings("all") +public abstract class StringUtils extends org.springframework.util.StringUtils { + /** + * INDEX_NOT_FOUND + */ + public static final int INDEX_NOT_FOUND = -1; + /** + * 特殊字符正则,sql特殊字符和空白符 + */ + public static final Pattern SPECIAL_CHARS_REGEX = Pattern.compile("[`'\"|/,;()-+*%#·•� \\s]"); + /** + * BLANK_SYMBOL + */ + public static final String BLANK_SYMBOL = "\\s*|\t|\r|\n"; + /** + * 随机字符串因子 + */ + public static final String INT_STR = "0123456789"; + /** + * STR_STR + */ + public static final String STR_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + /** + * ALL_STR + */ + public static final String ALL_STR = INT_STR + STR_STR; + + /** + * 有 任意 一个 Blank + * + * @param css CharSequence + * @return boolean boolean + * @since 1.0.0 + */ + public static boolean isAnyBlank(CharSequence... css) { + if (ObjectUtils.isEmpty(css)) { + return true; + } + return Stream.of(css).anyMatch(StringUtils::isBlank); + } + + /** + * Check whether the given {@code CharSequence} contains actual text. + *

More specifically, this method returns {@code true} if the + * {@code CharSequence} is not {@code null}, its length is greater than + * 0, and it contains at least one non-whitespace character. + *

+     * StringUtil.isBlank(null) = true
+     * StringUtil.isBlank("") = true
+     * StringUtil.isBlank(" ") = true
+     * StringUtil.isBlank("12345") = false
+     * StringUtil.isBlank(" 12345 ") = false
+     * 
+ * + * @param cs the {@code CharSequence} to check (may be {@code null}) + * @return {@code true} if the {@code CharSequence} is not {@code null}, its length is greater than 0, and it does not contain + * whitespace only + * @see Character#isWhitespace Character#isWhitespaceCharacter#isWhitespace + * @since 1.0.0 + */ + @Contract("null -> true") + public static boolean isBlank(CharSequence cs) { + return !StringUtils.hasText(cs); + } + + /** + * 是否全部都不是 blank + * + * @param css CharSequence + * @return boolean boolean + * @since 1.0.0 + */ + public static boolean isNoneBlank(CharSequence... css) { + if (ObjectUtils.isEmpty(css)) { + return false; + } + return Stream.of(css).allMatch(StringUtils::isNotBlank); + } + + /** + * 是否全部都是 blank + * + * @param css css + * @return the boolean + * @since 1.0.0 + */ + public static boolean isAllBlank(CharSequence... css) { + if (ObjectUtils.isEmpty(css)) { + return true; + } + return Stream.of(css).allMatch(StringUtils::isBlank); + } + + /** + *

Checks if a CharSequence is not empty (""), not null and not whitespace only.

+ *
+     * StringUtil.isNotBlank(null)      = false
+     * StringUtil.isNotBlank("")        = false
+     * StringUtil.isNotBlank(" ")       = false
+     * StringUtil.isNotBlank("bob")     = true
+     * StringUtil.isNotBlank("  bob  ") = true
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is not empty and not null and not whitespace + * @see Character#isWhitespace Character#isWhitespaceCharacter#isWhitespace + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isNotBlank(CharSequence cs) { + return StringUtils.hasText(cs); + } + + /** + * 判断一个字符串是否是数字 + * + * @param cs the CharSequence to check, may be null + * @return {boolean} + * @since 1.0.0 + */ + public static boolean isNumeric(CharSequence cs) { + if (isBlank(cs)) { + return false; + } + for (int i = cs.length(); --i >= 0; ) { + int chr = cs.charAt(i); + if (chr < 48 || chr > 57) { + return false; + } + } + return true; + } + + /** + * Convert a {@code Collection} into a delimited {@code String} (e.g., CSV). + *

Useful for {@code toString()} implementations. + * + * @param coll the {@code Collection} to convert + * @return the delimited {@code String} + * @since 1.0.0 + */ + @NotNull + public static String join(Collection coll) { + return StringUtils.collectionToCommaDelimitedString(coll); + } + + /** + * Convert a {@code Collection} into a delimited {@code String} (e.g. CSV). + *

Useful for {@code toString()} implementations. + * + * @param coll the {@code Collection} to convert + * @param delim the delimiter to use (typically a ",") + * @return the delimited {@code String} + * @since 1.0.0 + */ + @NotNull + public static String join(Collection coll, String delim) { + return StringUtils.collectionToDelimitedString(coll, delim); + } + + /** + * Convert a {@code String} array into a comma delimited {@code String} + * (i.e., CSV). + *

Useful for {@code toString()} implementations. + * + * @param arr the array to display + * @return the delimited {@code String} + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static String join(Object[] arr) { + return StringUtils.arrayToCommaDelimitedString(arr); + } + + /** + * Convert a {@code String} array into a delimited {@code String} (e.g. CSV). + *

Useful for {@code toString()} implementations. + * + * @param arr the array to display + * @param delim the delimiter to use (typically a ",") + * @return the delimited {@code String} + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static String join(Object[] arr, String delim) { + return StringUtils.arrayToDelimitedString(arr, delim); + } + + /** + * 生成uuid + * + * @return UUID string + * @since 1.0.0 + */ + @NotNull + public static String randomUid() { + ThreadLocalRandom random = ThreadLocalRandom.current(); + return new UUID(random.nextLong(), random.nextLong()).toString().replace(StringPool.DASH, StringPool.EMPTY); + } + + /** + * 转义HTML用于安全过滤 + * + * @param html html + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String escapeHtml(String html) { + return HtmlUtils.htmlEscape(html); + } + + /** + * 清理字符串,清理出某些不可见字符 + * + * @param txt 字符串 + * @return {String} + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static String cleanChars(@NotNull String txt) { + return txt.replaceAll("[  `·•�\\f\\t\\v\\s]", ""); + } + + + /** + * 有序的格式化文本,使用{number}做为占位符
+ * 例:
+ * 通常使用: format("this is {0} for {1}", "a", "b") =》 this is a for b
+ * + * @param pattern 文本格式 + * @param arguments 参数 + * @return 格式化后的文本 string + * @since 1.0.0 + */ + @NotNull + public static String indexedFormat(@NotNull CharSequence pattern, Object... arguments) { + return MessageFormat.format(pattern.toString(), arguments); + } + + /** + * 切分字符串,不去除切分后每个元素两边的空白符,不去除空白项 + * + * @param str 被切分的字符串 + * @param separator 分隔符字符 + * @param limit 限制分片数,-1不限制 + * @return 切分后的集合 list + * @since 1.0.0 + */ + @Contract("null, _, _ -> new") + public static List split(CharSequence str, char separator, int limit) { + return split(str, separator, limit, false, false); + } + + /** + * 切分字符串 + * + * @param str 被切分的字符串 + * @param separator 分隔符字符 + * @param limit 限制分片数,-1不限制 + * @param isTrim 是否去除切分字符串后每个元素两边的空格 + * @param ignoreEmpty 是否忽略空串 + * @return 切分后的集合 list + * @since 3.0.8 + */ + @Contract("null, _, _, _, _ -> new") + public static List split(CharSequence str, char separator, int limit, boolean isTrim, boolean ignoreEmpty) { + if (null == str) { + return new ArrayList<>(0); + } + return StrSplitter.split(str.toString(), separator, limit, isTrim, ignoreEmpty); + } + + /** + * 切分字符串,去除切分后每个元素两边的空白符,去除空白项 + * + * @param str 被切分的字符串 + * @param separator 分隔符字符 + * @return 切分后的集合 list + * @since 3.1.2 + */ + @Contract("null, _ -> new") + public static List splitTrim(CharSequence str, char separator) { + return splitTrim(str, separator, -1); + } + + /** + * 切分字符串,去除切分后每个元素两边的空白符,去除空白项 + * + * @param str 被切分的字符串 + * @param separator 分隔符字符 + * @param limit 限制分片数,-1不限制 + * @return 切分后的集合 list + * @since 3.1.0 + */ + @Contract("null, _, _ -> new") + private static List splitTrim(CharSequence str, char separator, int limit) { + return split(str, separator, limit, true, true); + } + + /** + * 切分字符串,去除切分后每个元素两边的空白符,去除空白项 + * + * @param str 被切分的字符串 + * @param separator 分隔符字符 + * @return 切分后的集合 list + * @since 3.2.0 + */ + @Contract("null, _ -> new") + public static List splitTrim(CharSequence str, CharSequence separator) { + return splitTrim(str, separator, -1); + } + + /** + * 切分字符串,去除切分后每个元素两边的空白符,去除空白项 + * + * @param str 被切分的字符串 + * @param separator 分隔符字符 + * @param limit 限制分片数,-1不限制 + * @return 切分后的集合 list + * @since 3.2.0 + */ + @Contract("null, _, _ -> new") + private static List splitTrim(CharSequence str, CharSequence separator, int limit) { + return split(str, separator, limit, true, true); + } + + /** + * 切分字符串 + * + * @param str 被切分的字符串 + * @param separator 分隔符字符 + * @param limit 限制分片数,-1不限制 + * @param isTrim 是否去除切分字符串后每个元素两边的空格 + * @param ignoreEmpty 是否忽略空串 + * @return 切分后的集合 list + * @since 3.2.0 + */ + @Contract("null, _, _, _, _ -> new") + public static List split(CharSequence str, CharSequence separator, int limit, boolean isTrim, boolean ignoreEmpty) { + if (null == str) { + return new ArrayList<>(0); + } + String separatorStr = (null == separator) ? null : separator.toString(); + return StrSplitter.split(str.toString(), separatorStr, limit, isTrim, ignoreEmpty); + } + + /** + * 切分字符串,不限制分片数量 + * + * @param str 被切分的字符串 + * @param separator 分隔符字符 + * @param isTrim 是否去除切分字符串后每个元素两边的空格 + * @param ignoreEmpty 是否忽略空串 + * @return 切分后的集合 list + * @since 3.0.8 + */ + @Contract("null, _, _, _ -> new") + public static List split(CharSequence str, char separator, boolean isTrim, boolean ignoreEmpty) { + return split(str, separator, 0, isTrim, ignoreEmpty); + } + + /** + * 切分字符串 + * + * @param str 被切分的字符串 + * @param separator 分隔符 + * @return 字符串 string [ ] + * @since 1.0.0 + */ + @NotNull + @Contract("null, _ -> new") + public static String[] split(CharSequence str, CharSequence separator) { + if (str == null) { + return new String[0]; + } + + String separatorStr = (null == separator) ? null : separator.toString(); + return StrSplitter.splitToArray(str.toString(), separatorStr, 0, false, false); + } + + /** + * 根据给定长度,将给定字符串截取为多个部分 + * + * @param str 字符串 + * @param len 每一个小节的长度 + * @return 截取后的字符串数组 string [ ] + * @see StrSpliter#splitByLength(String, int) StrSpliter#splitByLength(String, int)StrSpliter#splitByLength(String, int) + * @since 1.0.0 + */ + @Contract("null, _ -> new") + public static String[] split(CharSequence str, int len) { + if (null == str) { + return new String[0]; + } + return StrSplitter.splitByLength(str.toString(), len); + } + + /** + * 指定字符是否在字符串中出现过 + * + * @param str 字符串 + * @param searchChar 被查找的字符 + * @return 是否包含 boolean + * @since 3.1.2 + */ + public static boolean contains(CharSequence str, char searchChar) { + return indexOf(str, searchChar) > -1; + } + + /** + * 指定范围内查找指定字符 + * + * @param str 字符串 + * @param searchChar 被查找的字符 + * @return 位置 int + * @since 1.0.0 + */ + public static int indexOf(CharSequence str, char searchChar) { + return indexOf(str, searchChar, 0); + } + + /** + * 指定范围内查找指定字符 + * + * @param str 字符串 + * @param searchChar 被查找的字符 + * @param start 起始位置,如果小于0,从0开始查找 + * @return 位置 int + * @since 1.0.0 + */ + public static int indexOf(CharSequence str, char searchChar, int start) { + if (str instanceof String) { + return ((String) str).indexOf(searchChar, start); + } else { + return indexOf(str, searchChar, start, -1); + } + } + + /** + * 指定范围内查找指定字符 + * + * @param str 字符串 + * @param searchChar 被查找的字符 + * @param start 起始位置,如果小于0,从0开始查找 + * @param end 终止位置,如果超过str.length()则默认查找到字符串末尾 + * @return 位置 int + * @since 1.0.0 + */ + public static int indexOf(@NotNull CharSequence str, char searchChar, int start, int end) { + int len = str.length(); + if (start < 0 || start > len) { + start = 0; + } + if (end > len || end < 0) { + end = len; + } + for (int i = start; i < end; i++) { + if (str.charAt(i) == searchChar) { + return i; + } + } + return -1; + } + + /** + * 截取分隔字符串之前的字符串,不包括分隔字符串
+ * 如果给定的字符串为空串 (null或"") 或者分隔字符串为null,返回原字符串
+ * 如果分隔字符串为空串"",则返回空串,如果分隔字符串未找到,返回原字符串 + *

+ * 栗子: + *

+     * StringUtil.subBefore(null, *)      = null
+     * StringUtil.subBefore("", *)        = ""
+     * StringUtil.subBefore("abc", "a")   = ""
+     * StringUtil.subBefore("abcba", "b") = "a"
+     * StringUtil.subBefore("abc", "c")   = "ab"
+     * StringUtil.subBefore("abc", "d")   = "abc"
+     * StringUtil.subBefore("abc", "")    = ""
+     * StringUtil.subBefore("abc", null)  = "abc"
+     * 
+ * + * @param string 被查找的字符串 + * @param separator 分隔字符串 (不包括) + * @param isLastSeparator 是否查找最后一个分隔字符串 (多次出现分隔字符串时选取最后一个) ,true为选取最后一个 + * @return 切割后的字符串 string + * @since 3.1.1 + */ + @Nullable + public static String subBefore(CharSequence string, CharSequence separator, boolean isLastSeparator) { + if (StringUtils.isEmpty(string) || separator == null) { + return null == string ? null : string.toString(); + } + + String str = string.toString(); + String sep = separator.toString(); + if (sep.isEmpty()) { + return StringPool.EMPTY; + } + int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep); + if (pos == INDEX_NOT_FOUND) { + return str; + } + return str.substring(0, pos); + } + + /** + * 默认查找最后一个分隔符 + * + * @param string string + * @param separator separator + * @return the string + * @since 1.0.0 + */ + public static String subBefore(CharSequence string, CharSequence separator) { + return subBefore(string, separator, true); + } + + /** + * 截取分隔字符串之后的字符串,不包括分隔字符串
+ * 如果给定的字符串为空串 (null或"") ,返回原字符串
+ * 如果分隔字符串为空串 (null或"") ,则返回空串,如果分隔字符串未找到,返回空串 + *

+ * 栗子: + *

+     * StringUtil.subAfter(null, *)      = null
+     * StringUtil.subAfter("", *)        = ""
+     * StringUtil.subAfter(*, null)      = ""
+     * StringUtil.subAfter("abc", "a")   = "bc"
+     * StringUtil.subAfter("abcba", "b") = "cba"
+     * StringUtil.subAfter("abc", "c")   = ""
+     * StringUtil.subAfter("abc", "d")   = ""
+     * StringUtil.subAfter("abc", "")    = "abc"
+     * 
+ * + * @param string 被查找的字符串 + * @param separator 分隔字符串 (不包括) + * @param isLastSeparator 是否查找最后一个分隔字符串 (多次出现分隔字符串时选取最后一个) ,true为选取最后一个 + * @return 切割后的字符串 string + * @since 3.1.1 + */ + @Nullable + public static String subAfter(CharSequence string, CharSequence separator, boolean isLastSeparator) { + if (StringUtils.isEmpty(string)) { + return null == string ? null : string.toString(); + } + if (separator == null) { + return StringPool.EMPTY; + } + String str = string.toString(); + String sep = separator.toString(); + int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep); + if (pos == INDEX_NOT_FOUND) { + return StringPool.EMPTY; + } + return str.substring(pos + separator.length()); + } + + /** + * 默认查找第一个分隔符 + * + * @param string string + * @param separator separator + * @return the string + * @since 1.0.0 + */ + public static String subAfter(CharSequence string, CharSequence separator) { + return subAfter(string, separator, false); + } + + /** + * 截取指定字符串中间部分,不包括标识字符串
+ *

+ * 栗子: + *

+     * StringUtil.subBetween(null, *)            = null
+     * StringUtil.subBetween("", "")             = ""
+     * StringUtil.subBetween("", "tag")          = null
+     * StringUtil.subBetween("tagabctag", null)  = null
+     * StringUtil.subBetween("tagabctag", "")    = ""
+     * StringUtil.subBetween("tagabctag", "tag") = "abc"
+     * 
+ * + * @param str 被切割的字符串 + * @param beforeAndAfter 截取开始和结束的字符串标识 + * @return 截取后的字符串 string + * @since 3.1.1 + */ + @Contract("null, _ -> null; !null, null -> null") + public static String subBetween(CharSequence str, CharSequence beforeAndAfter) { + return subBetween(str, beforeAndAfter, beforeAndAfter); + } + + /** + * 截取指定字符串中间部分,不包括标识字符串
+ *

+ * 栗子: + *

+     * StringUtil.subBetween("wx[b]yz", "[", "]") = "b"
+     * StringUtil.subBetween(null, *, *)          = null
+     * StringUtil.subBetween(*, null, *)          = null
+     * StringUtil.subBetween(*, *, null)          = null
+     * StringUtil.subBetween("", "", "")          = ""
+     * StringUtil.subBetween("", "", "]")         = null
+     * StringUtil.subBetween("", "[", "]")        = null
+     * StringUtil.subBetween("yabcz", "", "")     = ""
+     * StringUtil.subBetween("yabcz", "y", "z")   = "abc"
+     * StringUtil.subBetween("yabczyabcz", "y", "z")   = "abc"
+     * 
+ * + * @param str 被切割的字符串 + * @param before 截取开始的字符串标识 + * @param after 截取到的字符串标识 + * @return 截取后的字符串 string + * @since 3.1.1 + */ + @Contract("null, _, _ -> null; !null, null, _ -> null; !null, !null, null -> null") + private static String subBetween(CharSequence str, CharSequence before, CharSequence after) { + if (str == null || before == null || after == null) { + return null; + } + + String str2 = str.toString(); + String before2 = before.toString(); + String after2 = after.toString(); + + int start = str2.indexOf(before2); + if (start != INDEX_NOT_FOUND) { + int end = str2.indexOf(after2, start + before2.length()); + if (end != INDEX_NOT_FOUND) { + return str2.substring(start + before2.length(), end); + } + } + return null; + } + + /** + * 去掉指定前缀 + * + * @param str 字符串 + * @param prefix 前缀 + * @return 切掉后的字符串 ,若前缀不是 preffix, 返回原字符串 + * @since 1.0.0 + */ + public static String removePrefix(CharSequence str, CharSequence prefix) { + if (StringUtils.isEmpty(str) || StringUtils.isEmpty(prefix)) { + return StringPool.EMPTY; + } + + String str2 = str.toString(); + if (str2.startsWith(prefix.toString())) { + return subSuf(str2, prefix.length()); + } + return str2; + } + + /** + * 切割指定位置之后部分的字符串 + * + * @param string 字符串 + * @param fromIndex 切割开始的位置 (包括) + * @return 切割后后剩余的后半部分字符串 string + * @since 1.0.0 + */ + @Nullable + public static String subSuf(CharSequence string, int fromIndex) { + if (StringUtils.isEmpty(string)) { + return StringPool.EMPTY; + } + return sub(string, fromIndex, string.length()); + } + + /** + * 改进JDK subString
+ * index从0开始计算,最后一个字符为-1
+ * 如果from和to位置一样,返回 ""
+ * 如果from或to为负数,则按照length从后向前数位置,如果绝对值大于字符串长度,则from归到0,to归到length
+ * 如果经过修正的index中from大于to,则互换from和to example:
+ * abcdefgh 2 3 =》 c
+ * abcdefgh 2 -3 =》 cde
+ * + * @param str String + * @param fromIndex 开始的index (包括) + * @param toIndex 结束的index (不包括) + * @return 字串 string + * @since 1.0.0 + */ + public static String sub(CharSequence str, int fromIndex, int toIndex) { + if (StringUtils.isEmpty(str)) { + return StringPool.EMPTY; + } + int len = str.length(); + + if (fromIndex < 0) { + fromIndex = len + fromIndex; + if (fromIndex < 0) { + fromIndex = 0; + } + } else if (fromIndex > len) { + fromIndex = len; + } + + if (toIndex < 0) { + toIndex = len + toIndex; + if (toIndex < 0) { + toIndex = len; + } + } else if (toIndex > len) { + toIndex = len; + } + + if (toIndex < fromIndex) { + int tmp = fromIndex; + fromIndex = toIndex; + toIndex = tmp; + } + + if (fromIndex == toIndex) { + return StringPool.EMPTY; + } + + return str.toString().substring(fromIndex, toIndex); + } + + /** + * 忽略大小写去掉指定前缀 + * + * @param str 字符串 + * @param prefix 前缀 + * @return 切掉后的字符串 ,若前缀不是 prefix, 返回原字符串 + * @since 1.0.0 + */ + public static String removePrefixIgnoreCase(CharSequence str, CharSequence prefix) { + if (StringUtils.isEmpty(str) || StringUtils.isEmpty(prefix)) { + return StringPool.EMPTY; + } + + String str2 = str.toString(); + if (str2.toLowerCase().startsWith(prefix.toString().toLowerCase())) { + return subSuf(str2, prefix.length()); + } + return str2; + } + + /** + * 去掉指定后缀,并小写首字母 + * + * @param str 字符串 + * @param suffix 后缀 + * @return 切掉后的字符串 ,若后缀不是 suffix, 返回原字符串 + * @since 1.0.0 + */ + @NotNull + public static String removeSufAndLowerFirst(CharSequence str, CharSequence suffix) { + return firstCharToLower(removeSuffix(str, suffix)); + } + + /** + * 首字母变小写 + * + * @param str 字符串 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String firstCharToLower(@NotNull String str) { + char firstChar = str.charAt(0); + if (firstChar >= CharPool.UPPER_A && firstChar <= CharPool.UPPER_Z) { + char[] arr = str.toCharArray(); + arr[0] += (CharPool.LOWER_A - CharPool.UPPER_A); + return new String(arr); + } + return str; + } + + /** + * 去掉指定后缀 + * + * @param str 字符串 + * @param suffix 后缀 + * @return 切掉后的字符串 ,若后缀不是 suffix, 返回原字符串 + * @since 1.0.0 + */ + public static String removeSuffix(CharSequence str, CharSequence suffix) { + if (StringUtils.isEmpty(str) || StringUtils.isEmpty(suffix)) { + return StringPool.EMPTY; + } + + String str2 = str.toString(); + if (str2.endsWith(suffix.toString())) { + return subPre(str2, str2.length() - suffix.length()); + } + return str2; + } + + /** + * 切割指定位置之前部分的字符串 + * + * @param string 字符串 + * @param toIndex 切割到的位置 (不包括) + * @return 切割后的剩余的前半部分字符串 string + * @since 1.0.0 + */ + private static String subPre(CharSequence string, int toIndex) { + return sub(string, 0, toIndex); + } + + /** + * 忽略大小写去掉指定后缀 + * + * @param str 字符串 + * @param suffix 后缀 + * @return 切掉后的字符串 ,若后缀不是 suffix, 返回原字符串 + * @since 1.0.0 + */ + public static String removeSuffixIgnoreCase(CharSequence str, CharSequence suffix) { + if (StringUtils.isEmpty(str) || StringUtils.isEmpty(suffix)) { + return StringPool.EMPTY; + } + + String str2 = str.toString(); + if (str2.toLowerCase().endsWith(suffix.toString().toLowerCase())) { + return subPre(str2, str2.length() - suffix.length()); + } + return str2; + } + + /** + * 首字母变大写 + * + * @param str 字符串 + * @return {String} + * @since 1.0.0 + */ + public static String firstCharToUpper(String str) { + if (isBlank(str)) { + return str; + } + char firstChar = str.charAt(0); + if (firstChar >= CharPool.LOWER_A && firstChar <= CharPool.LOWER_Z) { + char[] arr = str.toCharArray(); + arr[0] -= (CharPool.LOWER_A - CharPool.UPPER_A); + return new String(arr); + } + return str; + } + + /** + * 指定范围内查找字符串,忽略大小写
+ *
+     * StringUtil.indexOfIgnoreCase(null, *, *)          = -1
+     * StringUtil.indexOfIgnoreCase(*, null, *)          = -1
+     * StringUtil.indexOfIgnoreCase("", "", 0)           = 0
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "A", 0)  = 0
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "B", 0)  = 2
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "B", 3)  = 5
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "B", 9)  = -1
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "", 2)   = 2
+     * StringUtil.indexOfIgnoreCase("abc", "", 9)        = -1
+     * 
+ * + * @param str 字符串 + * @param searchStr 需要查找位置的字符串 + * @return 位置 int + * @since 3.2.1 + */ + public static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr) { + return indexOfIgnoreCase(str, searchStr, 0); + } + + /** + * 指定范围内查找字符串 + *
+     * StringUtil.indexOfIgnoreCase(null, *, *)          = -1
+     * StringUtil.indexOfIgnoreCase(*, null, *)          = -1
+     * StringUtil.indexOfIgnoreCase("", "", 0)           = 0
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "A", 0)  = 0
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "B", 0)  = 2
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "B", 3)  = 5
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "B", 9)  = -1
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
+     * StringUtil.indexOfIgnoreCase("aabaabaa", "", 2)   = 2
+     * StringUtil.indexOfIgnoreCase("abc", "", 9)        = -1
+     * 
+ * + * @param str 字符串 + * @param searchStr 需要查找位置的字符串 + * @param fromIndex 起始位置 + * @return 位置 int + * @since 3.2.1 + */ + private static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr, int fromIndex) { + return indexOf(str, searchStr, fromIndex, true); + } + + /** + * 指定范围内反向查找字符串 + * + * @param str 字符串 + * @param searchStr 需要查找位置的字符串 + * @param fromIndex 起始位置 + * @param ignoreCase 是否忽略大小写 + * @return 位置 int + * @since 3.2.1 + */ + public static int indexOf(CharSequence str, CharSequence searchStr, int fromIndex, boolean ignoreCase) { + if (str == null || searchStr == null) { + return INDEX_NOT_FOUND; + } + if (fromIndex < 0) { + fromIndex = 0; + } + + int endLimit = str.length() - searchStr.length() + 1; + if (fromIndex > endLimit) { + return INDEX_NOT_FOUND; + } + if (searchStr.length() == 0) { + return fromIndex; + } + + if (!ignoreCase) { + // 不忽略大小写调用JDK方法 + return str.toString().indexOf(searchStr.toString(), fromIndex); + } + + for (int i = fromIndex; i < endLimit; i++) { + if (isSubEquals(str, i, searchStr, 0, searchStr.length(), true)) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * 截取两个字符串的不同部分 (长度一致) ,判断截取的子串是否相同
+ * 任意一个字符串为null返回false + * + * @param str1 第一个字符串 + * @param start1 第一个字符串开始的位置 + * @param str2 第二个字符串 + * @param start2 第二个字符串开始的位置 + * @param length 截取长度 + * @param ignoreCase 是否忽略大小写 + * @return 子串是否相同 boolean + * @since 3.2.1 + */ + @SuppressWarnings("checkstyle:ParameterNumber") + @Contract("null, _, _, _, _, _ -> false; !null, _, null, _, _, _ -> false") + private static boolean isSubEquals(CharSequence str1, + int start1, + CharSequence str2, + int start2, + int length, + boolean ignoreCase) { + if (null == str1 || null == str2) { + return false; + } + + return str1.toString().regionMatches(ignoreCase, start1, str2.toString(), start2, length); + } + + /** + * 指定范围内查找字符串,忽略大小写
+ * + * @param str 字符串 + * @param searchStr 需要查找位置的字符串 + * @return 位置 int + * @since 3.2.1 + */ + public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr) { + return lastIndexOfIgnoreCase(str, searchStr, str.length()); + } + + /** + * 指定范围内查找字符串,忽略大小写
+ * + * @param str 字符串 + * @param searchStr 需要查找位置的字符串 + * @param fromIndex 起始位置,从后往前计数 + * @return 位置 int + * @since 3.2.1 + */ + private static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr, int fromIndex) { + return lastIndexOf(str, searchStr, fromIndex, true); + } + + /** + * 指定范围内查找字符串
+ * + * @param str 字符串 + * @param searchStr 需要查找位置的字符串 + * @param fromIndex 起始位置,从后往前计数 + * @param ignoreCase 是否忽略大小写 + * @return 位置 int + * @since 3.2.1 + */ + private static int lastIndexOf(CharSequence str, CharSequence searchStr, int fromIndex, boolean ignoreCase) { + if (str == null || searchStr == null) { + return INDEX_NOT_FOUND; + } + if (fromIndex < 0) { + fromIndex = 0; + } + fromIndex = Math.min(fromIndex, str.length()); + + if (searchStr.length() == 0) { + return fromIndex; + } + + if (!ignoreCase) { + // 不忽略大小写调用JDK方法 + return str.toString().lastIndexOf(searchStr.toString(), fromIndex); + } + + for (int i = fromIndex; i > 0; i--) { + if (isSubEquals(str, i, searchStr, 0, searchStr.length(), true)) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * 返回字符串 searchStr 在字符串 str 中第 ordinal 次出现的位置.
+ * 此方法来自: Apache-Commons-Lang + *

+ * 栗子 (*代表任意字符) : + *

+     * StringUtil.ordinalIndexOf(null, *, *)          = -1
+     * StringUtil.ordinalIndexOf(*, null, *)          = -1
+     * StringUtil.ordinalIndexOf("", "", *)           = 0
+     * StringUtil.ordinalIndexOf("aabaabaa", "a", 1)  = 0
+     * StringUtil.ordinalIndexOf("aabaabaa", "a", 2)  = 1
+     * StringUtil.ordinalIndexOf("aabaabaa", "b", 1)  = 2
+     * StringUtil.ordinalIndexOf("aabaabaa", "b", 2)  = 5
+     * StringUtil.ordinalIndexOf("aabaabaa", "ab", 1) = 1
+     * StringUtil.ordinalIndexOf("aabaabaa", "ab", 2) = 4
+     * StringUtil.ordinalIndexOf("aabaabaa", "", 1)   = 0
+     * StringUtil.ordinalIndexOf("aabaabaa", "", 2)   = 0
+     * 
+ * + * @param str 被检查的字符串,可以为null + * @param searchStr 被查找的字符串,可以为null + * @param ordinal 第几次出现的位置 + * @return 查找到的位置 int + * @since 3.2.3 + */ + public static int ordinalIndexOf(String str, String searchStr, int ordinal) { + if (str == null || searchStr == null || ordinal <= 0) { + return INDEX_NOT_FOUND; + } + if (searchStr.length() == 0) { + return 0; + } + int found = 0; + int index = INDEX_NOT_FOUND; + do { + index = str.indexOf(searchStr, index + 1); + if (index < 0) { + return index; + } + found++; + } while (found < ordinal); + return index; + } + + /** + * 比较两个字符串 (大小写敏感) . + *
+     * equalsIgnoreCase(null, null)   = true
+     * equalsIgnoreCase(null, "abc")  = false
+     * equalsIgnoreCase("abc", null)  = false
+     * equalsIgnoreCase("abc", "abc") = true
+     * equalsIgnoreCase("abc", "ABC") = true
+     * 
+ * + * @param str1 要比较的字符串1 + * @param str2 要比较的字符串2 + * @return 如果两个字符串相同 ,或者都是null,则返回true + * @since 1.0.0 + */ + @Contract("null, null -> true; null, !null -> false; !null, null -> false") + public static boolean equals(CharSequence str1, CharSequence str2) { + return equals(str1, str2, false); + } + + /** + * 比较两个字符串是否相等. + * + * @param str1 要比较的字符串1 + * @param str2 要比较的字符串2 + * @param ignoreCase 是否忽略大小写 + * @return 如果两个字符串相同 ,或者都是null,则返回true + * @since 3.2.0 + */ + @Contract("null, null, _ -> true; null, !null, _ -> false; !null, null, _ -> false") + public static boolean equals(CharSequence str1, CharSequence str2, boolean ignoreCase) { + if (null == str1) { + // 只有两个都为null才判断相等 + return str2 == null; + } + if (null == str2) { + // 字符串2空,字符串1非空,直接false + return false; + } + + if (ignoreCase) { + return str1.toString().equalsIgnoreCase(str2.toString()); + } else { + return str1.equals(str2); + } + } + + /** + * 比较两个字符串 (大小写不敏感) . + *
+     * equalsIgnoreCase(null, null)   = true
+     * equalsIgnoreCase(null, "abc")  = false
+     * equalsIgnoreCase("abc", null)  = false
+     * equalsIgnoreCase("abc", "abc") = true
+     * equalsIgnoreCase("abc", "ABC") = true
+     * 
+ * + * @param str1 要比较的字符串1 + * @param str2 要比较的字符串2 + * @return 如果两个字符串相同 ,或者都是null,则返回true + * @since 1.0.0 + */ + @Contract("null, null -> true; null, !null -> false; !null, null -> false") + public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) { + return equals(str1, str2, true); + } + + /** + * 创建StringBuilder对象 + * + * @return StringBuilder对象 string builder + * @since 1.0.0 + */ + @NotNull + @Contract(value = " -> new", pure = true) + public static StringBuilder builder() { + return new StringBuilder(); + } + + /** + * 创建StringBuilder对象 + * + * @param capacity 初始大小 + * @return StringBuilder对象 string builder + * @since 1.0.0 + */ + @NotNull + @Contract(value = "_ -> new", pure = true) + public static StringBuilder builder(int capacity) { + return new StringBuilder(capacity); + } + + /** + * 创建StringBuilder对象 + * + * @param strs 初始字符串列表 + * @return StringBuilder对象 string builder + * @since 1.0.0 + */ + public static StringBuilder builder(@NotNull CharSequence... strs) { + StringBuilder sb = new StringBuilder(); + for (CharSequence str : strs) { + sb.append(str); + } + return sb; + } + + /** + * 创建StringBuilder对象 + * + * @param sb 初始StringBuilder + * @param strs 初始字符串列表 + * @return StringBuilder对象 string builder + * @since 1.0.0 + */ + @Contract("_, _ -> param1") + public static StringBuilder appendBuilder(StringBuilder sb, @NotNull CharSequence... strs) { + for (CharSequence str : strs) { + sb.append(str); + } + return sb; + } + + /** + * 获得StringReader + * + * @param str 字符串 + * @return StringReader reader + * @since 1.0.0 + */ + @Contract("null -> null; !null -> new") + public static StringReader getReader(CharSequence str) { + if (null == str) { + return null; + } + return new StringReader(str.toString()); + } + + /** + * 获得StringWriter + * + * @return StringWriter writer + * @since 1.0.0 + */ + @NotNull + @Contract(value = " -> new", pure = true) + public static StringWriter getWriter() { + return new StringWriter(); + } + + + /** + * 统计指定内容中包含指定字符的数量 + * + * @param content 内容 + * @param charForSearch 被统计的字符 + * @return 包含数量 int + * @since 1.0.0 + */ + public static int count(CharSequence content, char charForSearch) { + int count = 0; + if (StringUtils.isEmpty(content)) { + return 0; + } + int contentLength = content.length(); + for (int i = 0; i < contentLength; i++) { + if (charForSearch == content.charAt(i)) { + count++; + } + } + return count; + } + + /** + * 下划线转驼峰 + * + * @param para 字符串 + * @return String string + * @since 1.0.0 + */ + @NotNull + public static String underlineToHump(@NotNull String para) { + return toHump(para, "_"); + } + + /** + * 驼峰转下划线 + * + * @param para 字符串 + * @return String string + * @since 1.0.0 + */ + @NotNull + public static String humpToUnderline(String para) { + return fromHump(para, "_"); + } + + /** + * 横线转驼峰 + * + * @param para 字符串 + * @return String string + * @since 1.0.0 + */ + @NotNull + public static String lineToHump(@NotNull String para) { + return toHump(para, "-"); + } + + /** + * 驼峰转横线 + * + * @param para 字符串 + * @return String string + * @since 1.0.0 + */ + @NotNull + public static String humpToLine(String para) { + return fromHump(para, "-"); + } + + /** + * To hump string + * + * @param para para + * @param s2 s 2 + * @return the string + * @since 1.0.0 + */ + @NotNull + private static String toHump(@NotNull String para, String s2) { + StringBuilder result = new StringBuilder(); + String[] a = para.split(s2); + for (String s : a) { + if (result.length() == 0) { + result.append(s.toLowerCase()); + } else { + result.append(s.substring(0, 1).toUpperCase()); + result.append(s.substring(1).toLowerCase()); + } + } + return result.toString(); + } + + /** + * From hump string + * + * @param para para + * @param s s + * @return the string + * @since 1.0.0 + */ + @NotNull + private static String fromHump(String para, String s) { + para = firstCharToLower(para); + StringBuilder sb = new StringBuilder(para); + int temp = 0; + for (int i = 0; i < para.length(); i++) { + if (Character.isUpperCase(para.charAt(i))) { + sb.insert(i + temp, s); + temp += 1; + } + } + return sb.toString().toLowerCase(); + } + + /** + * 重复某个字符串到指定长度 + * + * @param str 被重复的字符 + * @param padLen 指定长度 + * @return 重复字符字符串 string + * @since 4.3.2 + */ + @Contract("null, _ -> null") + private static String repeatByLength(CharSequence str, int padLen) { + if (null == str) { + return null; + } + if (padLen <= 0) { + return StringPool.EMPTY; + } + int strLen = str.length(); + if (strLen == padLen) { + return str.toString(); + } else if (strLen > padLen) { + return subPre(str, padLen); + } + + // 重复,直到达到指定长度 + char[] padding = new char[padLen]; + for (int i = 0; i < padLen; i++) { + padding[i] = str.charAt(i % strLen); + } + return new String(padding); + } + + /** + * 补充字符串以满足最小长度 + *
+     * StrUtil.padPre(null, *, *);//null
+     * StrUtil.padPre("1", 3, "ABC");//"AB1"
+     * StrUtil.padPre("123", 2, "ABC");//"12"
+     * 
+ * + * @param str 字符串 + * @param minLength 最小长度 + * @param padStr 补充的字符 + * @return 补充后的字符串 string + * @since 1.0.0 + */ + @Contract("null, _, _ -> null") + private static String padPre(CharSequence str, int minLength, CharSequence padStr) { + if (null == str) { + return null; + } + int strLen = str.length(); + if (strLen == minLength) { + return str.toString(); + } else if (strLen > minLength) { + return subPre(str, minLength); + } + + return repeatByLength(padStr, minLength - strLen).concat(str.toString()); + } + + /** + * 切割指定长度的后部分的字符串 + *
+     * StrUtil.subSufByLength("abcde", 3)      =    "cde"
+     * StrUtil.subSufByLength("abcde", 0)      =    ""
+     * StrUtil.subSufByLength("abcde", -5)     =    ""
+     * StrUtil.subSufByLength("abcde", -1)     =    ""
+     * StrUtil.subSufByLength("abcde", 5)       =    "abcde"
+     * StrUtil.subSufByLength("abcde", 10)     =    "abcde"
+     * StrUtil.subSufByLength(null, 3)               =    null
+     * 
+ * + * @param string 字符串 + * @param length 切割长度 + * @return 切割后后剩余的后半部分字符串 string + * @since 4.0.1 + */ + @Nullable + private static String subSufByLength(CharSequence string, int length) { + if (StringUtils.isEmpty(string)) { + return null; + } + if (length <= 0) { + return StringPool.EMPTY; + } + return sub(string, -length, string.length()); + } + + /** + * 补充字符串以满足最小长度 + *
+     * StrUtil.padAfter(null, *, *);//null
+     * StrUtil.padAfter("1", 3, "ABC");//"1AB"
+     * StrUtil.padAfter("123", 2, "ABC");//"23"
+     * 
+ * + * @param str 字符串,如果为null,按照空串处理 + * @param minLength 最小长度 + * @param padStr 补充的字符 + * @return 补充后的字符串 string + * @since 4.3.2 + */ + @Contract("null, _, _ -> null") + public static String padAfter(CharSequence str, int minLength, CharSequence padStr) { + if (null == str) { + return null; + } + int strLen = str.length(); + if (strLen == minLength) { + return str.toString(); + } else if (strLen > minLength) { + return subSufByLength(str, minLength); + } + + return str.toString().concat(repeatByLength(padStr, minLength - strLen)); + } + + /** + * {@link CharSequence} 转为字符串,null安全 + * + * @param cs {@link CharSequence} + * @return 字符串 string + * @since 1.0.0 + */ + @Contract("null -> null; !null -> !null") + public static String str(CharSequence cs) { + return null == cs ? null : cs.toString(); + } + + /** + * 居中字符串,两边补充指定字符串,如果指定长度小于字符串,则返回原字符串 + *
+     * StrUtil.center(null, *, *)     = null
+     * StrUtil.center("", 4, " ")     = "    "
+     * StrUtil.center("ab", -1, " ")  = "ab"
+     * StrUtil.center("ab", 4, " ")   = " ab "
+     * StrUtil.center("abcd", 2, " ") = "abcd"
+     * StrUtil.center("a", 4, " ")    = " a  "
+     * StrUtil.center("a", 4, "yz")   = "yayz"
+     * StrUtil.center("abc", 7, null) = "  abc  "
+     * StrUtil.center("abc", 7, "")   = "  abc  "
+     * 
+ * + * @param str 字符串 + * @param size 指定长度 + * @param padStr 两边补充的字符串 + * @return 补充后的字符串 string + * @since 1.0.0 + */ + public static String center(CharSequence str, int size, CharSequence padStr) { + if (str == null || size <= 0) { + return str(str); + } + if (StringUtils.isEmpty(padStr)) { + padStr = StringPool.SPACE; + } + int strLen = str.length(); + int pads = size - strLen; + if (pads <= 0) { + return str.toString(); + } + str = padPre(str, strLen + pads / 2, padStr); + str = padAfter(str, size, padStr); + return str.toString(); + } + + /** + * 清理字符串,清理出某些不可见字符和一些sql特殊字符 + * + * @param txt 文本 + * @return {String} + * @since 1.0.0 + */ + @Contract("null -> null") + @Nullable + public static String cleanText(@Nullable String txt) { + if (txt == null) { + return null; + } + return SPECIAL_CHARS_REGEX.matcher(txt).replaceAll(StringPool.EMPTY); + } + + /** + * 获取标识符,用于参数清理 + * + * @param param 参数 + * @return 清理后的标识符 string + * @since 1.0.0 + */ + @Contract("null -> null") + @Nullable + public static String cleanIdentifier(@Nullable String param) { + if (param == null) { + return null; + } + StringBuilder paramBuilder = new StringBuilder(); + for (int i = 0; i < param.length(); i++) { + char c = param.charAt(i); + if (Character.isJavaIdentifierPart(c)) { + paramBuilder.append(c); + } + } + return paramBuilder.toString(); + } + + /** + * 删除空白符 + * + * @param str the str + * @return the string + * @since 1.0.0 + */ + public static String replaceBlank(String str) { + String dest = ""; + if (str != null) { + Pattern p = Pattern.compile(BLANK_SYMBOL); + Matcher m = p.matcher(str); + dest = m.replaceAll(""); + } + return dest; + } + + /** + * 生成uuid,采用 jdk 9 的形式,优化性能 + * + * @return UUID uuid + * @since 1.0.0 + */ + @NotNull + @Contract(" -> new") + public static String getUid() { + ThreadLocalRandom random = ThreadLocalRandom.current(); + long lsb = random.nextLong(); + long msb = random.nextLong(); + char[] buf = new char[32]; + formatUnsignedLong(lsb, buf, 20, 12); + formatUnsignedLong(lsb >>> 48, buf, 16, 4); + formatUnsignedLong(msb, buf, 12, 4); + formatUnsignedLong(msb >>> 16, buf, 8, 4); + formatUnsignedLong(msb >>> 32, buf, 0, 8); + return new String(buf); + } + + /** + * Format unsigned long * + * + * @param val val + * @param buf buf + * @param offset offset + * @param len len + * @since 1.0.0 + */ + private static void formatUnsignedLong(long val, @NotNull char @NotNull [] buf, int offset, int len) { + int charPos = offset + len; + int radix = 1 << 4; + int mask = radix - 1; + do { + buf[--charPos] = NumberUtils.DIGITS[((int) val) & mask]; + val >>>= 4; + } while (charPos > offset); + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用: format("this is {} for {}", "a", "b") =》 this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") =》 this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") =》 this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 string + * @since 1.0.0 + */ + @Contract("null, _ -> null") + public static String format(CharSequence template, Object... params) { + if (null == template) { + return null; + } + return StrFormatter.format(template.toString(), params); + } + + /** + * 格式化文本,使用 {varName} 占位
+ * map = {a: "aValue", b: "bValue"} format("{a} and {b}", map) ---=》 aValue and bValue + * + * @param template 文本模板,被替换的部分用 {key} 表示 + * @param map 参数值对 + * @return 格式化后的文本 string + * @since 1.0.0 + */ + @Contract("null, _ -> null; !null, null -> !null") + public static String format(CharSequence template, Map map) { + if (null == template) { + return null; + } + if (null == map || map.isEmpty()) { + return template.toString(); + } + + String template2 = template.toString(); + for (Map.Entry entry : map.entrySet()) { + template2 = template2.replace("{" + entry.getKey() + "}", toStr(entry.getValue())); + } + return template2; + } + + /** + * 强转string,并去掉多余空格 + * + * @param str 字符串 + * @return String string + * @since 1.0.0 + */ + @Contract(pure = true) + public static String toStr(Object str) { + return toStr(str, ""); + } + + /** + * 强转string,并去掉多余空格 + * + * @param str 字符串 + * @param defaultValue 默认值 + * @return String string + * @since 1.0.0 + */ + @Contract(value = "null, _ -> param2", pure = true) + public static String toStr(Object str, String defaultValue) { + return StrFormatter.toStr(str, defaultValue); + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param testStrs 需要检查的字符串数组 + * @return 是否包含任意一个字符串 boolean + * @since 3.2.0 + */ + public static boolean containsAny(CharSequence str, CharSequence... testStrs) { + return null != getContainsStr(str, testStrs); + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串,如果包含返回找到的第一个字符串 + * + * @param str 指定字符串 + * @param testStrs 需要检查的字符串数组 + * @return 被包含的第一个字符串 contains str + * @since 3.2.0 + */ + @Nullable + private static String getContainsStr(CharSequence str, CharSequence... testStrs) { + if (isEmpty(str)) { + return null; + } + for (CharSequence checkStr : testStrs) { + if (str.toString().contains(checkStr)) { + return checkStr.toString(); + } + } + return null; + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串
+ * 忽略大小写 + * + * @param str 指定字符串 + * @param testStrs 需要检查的字符串数组 + * @return 是否包含任意一个字符串 boolean + * @since 3.2.0 + */ + public static boolean containsAnyIgnoreCase(CharSequence str, CharSequence... testStrs) { + return null != getContainsStrIgnoreCase(str, testStrs); + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串,如果包含返回找到的第一个字符串
+ * 忽略大小写 + * + * @param str 指定字符串 + * @param testStrs 需要检查的字符串数组 + * @return 被包含的第一个字符串 contains str ignore case + * @since 3.2.0 + */ + @Nullable + private static String getContainsStrIgnoreCase(CharSequence str, CharSequence... testStrs) { + if (isEmpty(str)) { + return null; + } + for (CharSequence testStr : testStrs) { + if (containsIgnoreCase(str, testStr)) { + return testStr.toString(); + } + } + return null; + } + + /** + * 是否包含特定字符,忽略大小写,如果给定两个参数都为null,返回true + * + * @param str 被检测字符串 + * @param testStr 被测试是否包含的字符串 + * @return 是否包含 boolean + * @since 1.0.0 + */ + @Contract("null, null -> true; null, !null -> false") + private static boolean containsIgnoreCase(CharSequence str, CharSequence testStr) { + if (null == str) { + // 如果被监测字符串和 + return null == testStr; + } + return str.toString().toLowerCase().contains(testStr.toString().toLowerCase()); + } + + /** + * Replace string + * + * @param source source + * @param parameter parameter + * @return the string + * @since 1.0.0 + */ + public static String replace(String source, Map parameter) { + return replace(source, + parameter, + StringSubstitutor.DEFAULT_VAR_START, + StringSubstitutor.DEFAULT_VAR_END, + true); + } + + + /** + * 替换 + * + * @param source 源内容 + * @param parameter 占位符参数 + * @param prefix 占位符前缀 例如:${ + * @param suffix 占位符后缀 例如:} + * @param enableSubstitutionInVariables 是否在变量名称中进行替换 例如:${system-${版本}} 转义符默认为'$'. + * 如果这个字符放在一个变量引用之前, 这个引用将被忽略, 不会被替换 如$${a}将直接输出${a} + * @return string string + * @since 1.0.0 + */ + public static String replace(String source, + Map parameter, + String prefix, + String suffix, + boolean enableSubstitutionInVariables) { + + StringSubstitutor strSubstitutor = new StringSubstitutor(parameter, prefix, suffix); + strSubstitutor.setEnableSubstitutionInVariables(enableSubstitutionInVariables); + return strSubstitutor.replace(source); + } + +} + diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/TimeoutUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/TimeoutUtils.java new file mode 100644 index 0000000..15e6ef2 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/base/utils/TimeoutUtils.java @@ -0,0 +1,100 @@ +package com.aivfo.el.starter.base.utils; + +import com.aivfo.el.starter.base.exception.TimeoutUtilsExecuteException; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Contract; + +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.*; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 简单超时工具类 + */ +@Slf4j +@UtilityClass +@SuppressWarnings("PMD.ThreadPoolCreationRule") +public final class TimeoutUtils { + /** + * executor + */ + private static final ExecutorService TIMEOUT_EXECUTOR = Executors.newSingleThreadExecutor(); + + /** + * Process + * + * @param parameter + * @param task task + * @param timeout timeout + * @return the t + * @throws TimeoutUtilsExecuteException timeout utils execute exception + * @since 1.5.0 + */ + @Contract("null, _ -> null") + public static T process(Callable task, long timeout) throws Exception { + return process(task, timeout, TimeUnit.SECONDS); + } + + /** + * Process + * + * @param parameter + * @param task task + * @param timeout timeout + * @param unit unit + * @return the t + * @throws TimeoutUtilsExecuteException timeout utils execute exception + * @since 1.7.1 + */ + @Contract("null, _, _ -> null") + public static T process(Callable task, long timeout, TimeUnit unit) throws Exception { + return process(TIMEOUT_EXECUTOR, task, timeout, unit); + } + + /** + * Process + * todo-dong4j : (2021.09.18 16:17) [executorService.submit(TtlCallable.get(task))] + * + * @param parameter + * @param executorService executor service + * @param task task + * @param timeout timeout + * @param unit unit + * @return the t + * @throws TimeoutUtilsExecuteException timeout utils execute exception + * @since 1.7.1 + */ + @Contract("_, null, _, _ -> null") + public static T process(ExecutorService executorService, + Callable task, + long timeout, + TimeUnit unit) throws Exception { + if (task == null) { + return null; + } + Future futureRet = executorService.submit(task); + try { + return futureRet.get(timeout, unit); + } catch (InterruptedException e) { + log.error("任务被中断: {}", e.getMessage()); + throw e; + } catch (ExecutionException e) { + // 任务抛出异常时将被包装为 ExecutionException + Throwable cause = e.getCause(); + if (cause instanceof InvocationTargetException) { + // 如果通过反射调用将会抛出 InvocationTargetException, 这里抛出原始异常 + throw (InvocationTargetException) cause; + } + // 否则抛出 ExecutionException, 由业务端处理 + throw e; + } catch (TimeoutException e) { + if (!futureRet.isCancelled()) { + futureRet.cancel(true); + } + } + + throw new TimeoutUtilsExecuteException("process timeout: [" + timeout + " " + unit.name().toLowerCase() + "]"); + } +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedCallable.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedCallable.java new file mode 100644 index 0000000..4477f59 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedCallable.java @@ -0,0 +1,16 @@ +package com.aivfo.el.starter.core.function; + + +@FunctionalInterface +@SuppressWarnings("java:S112") +public interface CheckedCallable { + + /** + * Run this callable. + * + * @return result t + * @throws Throwable CheckedException + * @since 1.0.0 + */ + T call() throws Throwable; +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedComparator.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedComparator.java similarity index 53% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedComparator.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedComparator.java index 2c46789..cfe5b39 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedComparator.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedComparator.java @@ -1,16 +1,5 @@ -package com.aivfo.el.core.function; +package com.aivfo.el.starter.core.function; -/** - *

Company: 成都返空汇网络技术有限公司

- *

Description: 受检的 Comparator

- * - * @param parameter - * @author dong4j - * @version 1.2.3 - * @email "mailto:dongshijie@fkhwl.com" - * @date 2020.01.27 14:55 - * @since 1.0.0 - */ @FunctionalInterface @SuppressWarnings("java:S112") public interface CheckedComparator { diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedConsumer.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedConsumer.java new file mode 100644 index 0000000..cb98cb1 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedConsumer.java @@ -0,0 +1,16 @@ +package com.aivfo.el.starter.core.function; + +@FunctionalInterface +@SuppressWarnings("java:S112") +public interface CheckedConsumer { + + /** + * Run the Consumer + * + * @param t T + * @throws Throwable UncheckedException + * @since 1.0.0 + */ + void accept(T t) throws Throwable; + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedFunction.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedFunction.java similarity index 52% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedFunction.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedFunction.java index 70c25e9..18088a8 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedFunction.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedFunction.java @@ -1,19 +1,7 @@ -package com.aivfo.el.core.function; +package com.aivfo.el.starter.core.function; import org.springframework.lang.Nullable; -/** - *

Company: 成都返空汇网络技术有限公司

- *

Description: 受检的 function

- * - * @param parameter - * @param parameter - * @author dong4j - * @version 1.2.3 - * @email "mailto:dongshijie@fkhwl.com" - * @date 2020.01.27 14:55 - * @since 1.0.0 - */ @FunctionalInterface @SuppressWarnings("java:S112") public interface CheckedFunction { diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedRunnable.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedRunnable.java new file mode 100644 index 0000000..f0e23ed --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedRunnable.java @@ -0,0 +1,15 @@ +package com.aivfo.el.starter.core.function; + +@FunctionalInterface +@SuppressWarnings("java:S112") +public interface CheckedRunnable { + + /** + * Run this runnable. + * + * @throws Throwable CheckedException + * @since 1.0.0 + */ + void run() throws Throwable; + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedSupplier.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedSupplier.java similarity index 51% rename from band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedSupplier.java rename to band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedSupplier.java index b492cc0..0b65972 100644 --- a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/core/function/CheckedSupplier.java +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/function/CheckedSupplier.java @@ -1,18 +1,7 @@ -package com.aivfo.el.core.function; +package com.aivfo.el.starter.core.function; import org.springframework.lang.Nullable; -/** - *

Company: 成都返空汇网络技术有限公司

- *

Description: 受检的 Supplier

- * - * @param parameter - * @author dong4j - * @version 1.2.3 - * @email "mailto:dongshijie@fkhwl.com" - * @date 2020.01.27 14:54 - * @since 1.0.0 - */ @FunctionalInterface @SuppressWarnings("java:S112") public interface CheckedSupplier { diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/AopTargetUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/AopTargetUtils.java new file mode 100644 index 0000000..a74fd9c --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/AopTargetUtils.java @@ -0,0 +1,75 @@ +package com.aivfo.el.starter.core.util; + +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.NotNull; +import org.springframework.aop.framework.AdvisedSupport; +import org.springframework.aop.framework.AopProxy; +import org.springframework.aop.support.AopUtils; + +import java.lang.reflect.Field; + +@UtilityClass +@SuppressWarnings("all") +public class AopTargetUtils { + + /** + * 获取 目标对象 + * + * @param proxy 代理对象 + * @return target + * @throws Exception exception + * @since 1.6.0 + */ + public static Object getTarget(Object proxy) throws Exception { + + if (!AopUtils.isAopProxy(proxy)) { + return proxy; + } + + if (AopUtils.isJdkDynamicProxy(proxy)) { + return getJdkDynamicProxyTargetObject(proxy); + } else { + return getCglibProxyTargetObject(proxy); + } + + } + + /** + * Gets cglib proxy target object * + * + * @param proxy proxy + * @return the cglib proxy target object + * @throws Exception exception + * @since 1.6.0 + */ + private static Object getCglibProxyTargetObject(@NotNull Object proxy) throws Exception { + Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0"); + h.setAccessible(true); + Object dynamicAdvisedInterceptor = h.get(proxy); + + Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised"); + advised.setAccessible(true); + + return ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget(); + } + + /** + * Gets jdk dynamic proxy target object * + * + * @param proxy proxy + * @return the jdk dynamic proxy target object + * @throws Exception exception + * @since 1.6.0 + */ + private static Object getJdkDynamicProxyTargetObject(@NotNull Object proxy) throws Exception { + Field h = proxy.getClass().getSuperclass().getDeclaredField("h"); + h.setAccessible(true); + AopProxy aopProxy = (AopProxy) h.get(proxy); + + Field advised = aopProxy.getClass().getDeclaredField("advised"); + advised.setAccessible(true); + + return ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget(); + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/CIDRUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/CIDRUtils.java new file mode 100644 index 0000000..f48c8ff --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/CIDRUtils.java @@ -0,0 +1,177 @@ +package com.aivfo.el.starter.core.util; + +import org.jetbrains.annotations.NotNull; + +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +/** + * A class that enables to get an IP range from CIDR specification. It supports + * both IPv4 and IPv6. + *

+ * From https://github.com/edazdarevic/CIDRUtils/blob/master/CIDRUtils.java + */ +@SuppressWarnings("all") +public class CIDRUtils { + /** + * Cidr + */ + private final String CIDR; + /** + * Inet address + */ + private final InetAddress inetAddress; + /** + * Start address + */ + private InetAddress startAddress; + /** + * End address + */ + private InetAddress endAddress; + /** + * Prefix length + */ + private final int prefixLength; + + + /** + * Cidr utils + * + * @param cidr cidr + * @throws UnknownHostException unknown host exception + * @since 1.5.0 + */ + public CIDRUtils(String cidr) throws UnknownHostException { + + this.CIDR = cidr; + + /* split CIDR to address and prefix part */ + if (this.CIDR.contains("/")) { + int index = this.CIDR.indexOf("/"); + String addressPart = this.CIDR.substring(0, index); + String networkPart = this.CIDR.substring(index + 1); + + this.inetAddress = InetAddress.getByName(addressPart); + this.prefixLength = Integer.parseInt(networkPart); + + this.calculate(); + } else { + throw new IllegalArgumentException("not an valid CIDR format!"); + } + } + + + /** + * Calculate + * + * @throws UnknownHostException unknown host exception + * @since 1.5.0 + */ + private void calculate() throws UnknownHostException { + + ByteBuffer maskBuffer; + int targetSize; + if (this.inetAddress.getAddress().length == 4) { + maskBuffer = + ByteBuffer + .allocate(4) + .putInt(-1); + targetSize = 4; + } else { + maskBuffer = ByteBuffer.allocate(16) + .putLong(-1L) + .putLong(-1L); + targetSize = 16; + } + + BigInteger mask = (new BigInteger(1, maskBuffer.array())).not().shiftRight(this.prefixLength); + + ByteBuffer buffer = ByteBuffer.wrap(this.inetAddress.getAddress()); + BigInteger ipVal = new BigInteger(1, buffer.array()); + + BigInteger startIp = ipVal.and(mask); + BigInteger endIp = startIp.add(mask.not()); + + byte[] startIpArr = this.toBytes(startIp.toByteArray(), targetSize); + byte[] endIpArr = this.toBytes(endIp.toByteArray(), targetSize); + + this.startAddress = InetAddress.getByAddress(startIpArr); + this.endAddress = InetAddress.getByAddress(endIpArr); + + } + + /** + * To bytes + * + * @param array array + * @param targetSize target size + * @return the byte [ ] + * @since 1.5.0 + */ + private byte @NotNull [] toBytes(byte[] array, int targetSize) { + int counter = 0; + List newArr = new ArrayList(); + while (counter < targetSize && (array.length - 1 - counter >= 0)) { + newArr.add(0, array[array.length - 1 - counter]); + counter++; + } + + int size = newArr.size(); + for (int i = 0; i < (targetSize - size); i++) { + + newArr.add(0, (byte) 0); + } + + byte[] ret = new byte[newArr.size()]; + for (int i = 0; i < newArr.size(); i++) { + ret[i] = newArr.get(i); + } + return ret; + } + + /** + * Gets network address * + * + * @return the network address + * @since 1.5.0 + */ + public String getNetworkAddress() { + + return this.startAddress.getHostAddress(); + } + + /** + * Gets broadcast address * + * + * @return the broadcast address + * @since 1.5.0 + */ + public String getBroadcastAddress() { + return this.endAddress.getHostAddress(); + } + + /** + * Is in range + * + * @param ipAddress ip address + * @return the boolean + * @throws UnknownHostException unknown host exception + * @since 1.5.0 + */ + public boolean isInRange(String ipAddress) throws UnknownHostException { + InetAddress address = InetAddress.getByName(ipAddress); + BigInteger start = new BigInteger(1, this.startAddress.getAddress()); + BigInteger end = new BigInteger(1, this.endAddress.getAddress()); + BigInteger target = new BigInteger(1, address.getAddress()); + + int st = start.compareTo(target); + int te = target.compareTo(end); + + return (st < 0 || st == 0) && (te < 0 || te == 0); + } +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/DataTypeUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/DataTypeUtils.java new file mode 100644 index 0000000..8e475e2 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/DataTypeUtils.java @@ -0,0 +1,1685 @@ +package com.aivfo.el.starter.core.util; + +import com.aivfo.el.starter.base.exception.BasicException; +import com.aivfo.el.starter.base.support.StrFormatter; +import com.aivfo.el.starter.base.utils.JsonUtils; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.time.DateUtils; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.*; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.*; + +@Slf4j +@UtilityClass +@SuppressWarnings("all") +public class DataTypeUtils { + + /** + * 把输入数据转换为预期的输出类型 + * 转换数据类型通常都是子类转换为父类或者转换为接口,这种情况至少占到程序编码90%以上,此方法内部默认此种判断为第一级判断,从而保证效率最优 + * 此工具类的其他代码专注于解决另外的10%复杂情况,旨在把常见的容错代码,封装于此,避免调用端编写复杂、重复、冗长、不健壮的代码 + *

容错概述:

+ * 1.自动拆箱装箱 + * 2.数字与字符串向枚举转换 + * 3.数组与Collection自动互转 + * 4.除boolean外的七种基本数据类型互转时取值范围自适应 + * 5.字符串与时间与基本数据类型互转 + * 6.以及对上述容错情况自由排列组合后所产生的更复杂情况自动容错 + * 7.请格外注意,由于Boolean在Java中的通俗约定较之其他语言例如C非常不同,不存在其他数据类型与之兼容取值范围的习惯做法,因此并未容错,如果想要让数字类型转换为boolean, + * 注意这里是未作支持的,现仅以最低兼容方式构造Boolean,即仅支持Java官方约定的通过字符串构造Boolean对象以及boolean基本值构造Boolean对象 + *

常见情景:

+ * 1.比如一个int数组完全可以放入long数组或 List 中, 用此工具方法自动处理,即可避免自己的代码篇幅冗长,或者代码编写质量不高导致程序健壮性不高的问题 + * 2.如果long数组数值并不是大数,当然也可以放入int数组,其他数值类型同理,反之亦然 + * 3.很多数据拿到的时候是Object类型,如果用强制类型转换,很容易出错,比如到底Object是数组还是集合类,都有可能,这种时候强制类型转换必然出错 + * 4.实现类自动转换为接口,比如HashMap、LinkedHashMap转换为Map + *

示例:

+ * log.info("{}", convert(byte[].class, "-128")); + * log.info("{}", convert(byte.class, "-128")); + * log.info("{}", convert(byte[].class, -128)); + * log.info("{}", convert(byte.class, -128)); + * log.info("{}", convert(Byte[].class, "-128")); + * log.info("{}", convert(Byte.class, "-128")); + * log.info("{}", convert(Byte[].class, -128)); + * log.info("{}", convert(Byte.class, -128)); + * log.info("{}", convert(byte[].class, "-128.00000")); + * log.info("{}", convert(byte.class, "-128.00000")); + * log.info("{}", convert(byte[].class, -128.00000)); + * log.info("{}", convert(byte.class, -128.00000)); + * log.info("{}", convert(Byte[].class, "-128.00000")); + * log.info("{}", convert(Byte.class, "-128.00000")); + * log.info("{}", convert(Byte[].class, -128.00000)); + * log.info("{}", convert(Byte.class, -128.00000)); + * log.info("{}", convert(float[].class, "-128.00000")); + * log.info("{}", convert(float.class, "-128.00000")); + * log.info("{}", convert(float[].class, -128.00000)); + * log.info("{}", convert(float.class, -128.00000)); + * log.info("{}", convert(Float[].class, "-128.00000")); + * log.info("{}", convert(Float.class, "-128.00000")); + * log.info("{}", convert(Float[].class, -128.00000)); + * log.info("{}", convert(Float.class, -128.00000)); + * log.info("{}", convert(BigDecimal.class, "-128")); + * log.info("{}", convert(BigDecimal.class, -128)); + * log.info("{}", convert(BigDecimal.class, "-128.00000")); + * log.info("{}", convert(BigDecimal.class, -128.00000)); + * log.info("{}", convert(Date.class, "0")); + * log.info("{}", convert(Date.class, "1451577600000")); + * log.info("{}", convert(Date[].class, new Object[]{"0","2016-01-01 00:00:00","1451577600000",1451577600000L})); + * log.info("{}", convert(Date[].class, Arrays.asList(new Object[]{"0","2016-01-01 00:00:00","1451577600000",1451577600000L}))); + * log.info("{}", convert(List.class, new Date[]{new Date(0),new Date(1451577600000L)})); + * log.info("{}", convert(List.class, Arrays.asList(new Object[]{(byte)1,"1",1,1L}))); + * log.info("{}", convert(Set.class, Arrays.asList(new Object[]{(byte)1,"1",1,1L}))); + * log.info("{}", convert(Set.class, Arrays.asList(new Long[]{1L,1L,2L}))); + * log.info("{}", convert(Set.class, new Long[]{1L,1L,2L})); + * log.info("{}", convert(BigDecimal[].class, new Object[]{(byte)1,1,1L,"1"})); + * + * @param the type parameter + * @param destClazz 预期将要输出为何种数据类型 + * @param srcObj 输入任意类型的数据 + * @return the t + * @since 1.0.0 + */ + @Contract("_, null -> null; null, !null -> null") + public static T convert(Class destClazz, Object srcObj) { + if (null == srcObj) { + return null; + } + if (null == destClazz) { + return null; + } + if (destClazz.isAssignableFrom(srcObj.getClass())) { + return (T) srcObj; + } + + if (isPrimitive(destClazz)) { + Class boxingType = typeBoxing(destClazz); + return (T) unBoxing(convert(boxingType, srcObj)); + } else if (destClazz.isArray()) { + return convertArray(destClazz, srcObj); + } else if (destClazz.isEnum()) { + return Object2Enum.convert(destClazz, srcObj); + } else { + T ret = null; + if (String.class.isAssignableFrom(destClazz)) { + ret = (T) Object2String.convert(srcObj); + } else if (isNumber(destClazz)) { + if (Long.class.isAssignableFrom(destClazz)) { + ret = (T) Object2Long.convert(srcObj); + } else if (Integer.class.isAssignableFrom(destClazz)) { + ret = (T) Object2Integer.convert(srcObj); + } else if (BigDecimal.class.isAssignableFrom(destClazz)) { + ret = (T) Object2BigDecimal.convert(srcObj); + } else if (Double.class.isAssignableFrom(destClazz)) { + ret = (T) Object2Double.convert(srcObj); + } else if (Float.class.isAssignableFrom(destClazz)) { + ret = (T) Object2Float.convert(srcObj); + } else if (Short.class.isAssignableFrom(destClazz)) { + ret = (T) Object2Short.convert(srcObj); + } else if (Byte.class.isAssignableFrom(destClazz)) { + ret = (T) Object2Byte.convert(srcObj); + } + } else if (Date.class.isAssignableFrom(destClazz)) { + ret = (T) Object2Date.convert(srcObj); + } else if (Boolean.class.isAssignableFrom(destClazz)) { + ret = (T) Object2Boolean.convert(srcObj); + } else if (Character.class.isAssignableFrom(destClazz)) { + ret = (T) Object2Character.convert(srcObj); + } else if (Collection.class.isAssignableFrom(destClazz)) { + return (T) toCollection(destClazz, srcObj); + } else { + throw new BasicException("指定的数据类型 [{}] 暂不支持,无法转换", destClazz); + } + if (null == ret) { + throw new BasicException("[{}] 无法转换为 [{}] Data: [{}]", + srcObj.getClass(), + destClazz.getName(), + srcObj); + } else { + return ret; + } + } + } + + /** + * To collection collection + * + * @param parameter + * @param destClazz dest clazz + * @param srcObj src obj + * @return the collection + * @since 1.0.0 + */ + @SuppressWarnings("unchecked") + private static Collection toCollection(@NotNull Class destClazz, Object srcObj) { + Collection coll; + if (destClazz.isInterface()) { + if (List.class.isAssignableFrom(destClazz)) { + coll = new ArrayList<>(); + } else if (Set.class.isAssignableFrom(destClazz)) { + coll = new HashSet<>(); + } else if (Queue.class.isAssignableFrom(destClazz)) { + coll = new LinkedList<>(); + } else { + throw new BasicException("暂不支持向集合类型 {} 转换", destClazz); + } + } else { + try { + coll = (Collection) destClazz.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new BasicException("无法对类型 {} 进行实例化", destClazz); + } + } + Class objClazz = srcObj.getClass(); + if (objClazz.isArray()) { + coll.addAll(Arrays.asList((Object[]) boxing(srcObj))); + } else if (srcObj instanceof Collection) { + coll.addAll((Collection) srcObj); + } else { + coll.add(srcObj); + } + return coll; + } + + /** + * Convert array t + * + * @param parameter + * @param destClazz dest clazz + * @param srcObj src obj + * @return the t + * @since 1.0.0 + */ + @SuppressWarnings("unchecked") + private static T convertArray(Class destClazz, @NotNull Object srcObj) { + Object[] targetObjectArray; + Class objClazz = srcObj.getClass(); + if (objClazz.isArray()) { + targetObjectArray = (Object[]) boxing(srcObj); + } else if (Collection.class.isAssignableFrom(objClazz)) { + targetObjectArray = ((Collection) srcObj).toArray(); + } else { + targetObjectArray = new Object[]{srcObj}; + } + Class everyClazz = destClazz.getComponentType(); + Object[] newOjb = (Object[]) Array.newInstance(everyClazz, targetObjectArray.length); + for (int i = 0; i < targetObjectArray.length; i++) { + newOjb[i] = convert(everyClazz, targetObjectArray[i]); + } + return (T) newOjb; + } + + /** + * 判断输入数据是否为null 数组与集合类内的每个元素都为null那么输入数据被视作null + * + * @param obj the obj + * @return the boolean + * @since 1.0.0 + */ + @Contract("null -> true") + public static boolean deepIsNull(Object obj) { + if (null == obj) { + return true; + } + Class arrClazz = Object[].class; + Object[] newObj = convertArray(arrClazz, obj); + for (Object it : newObj) { + if (null != it) { + return false; + } + } + return true; + } + + /** + * 与Java自己的toString不同在于,如果是数组,打印数组内容而不再是打印内存地址 + * + * @param obj the obj + * @return the string + * @since 1.0.0 + */ + @Contract("null -> null") + public static String toStr(Object obj) { + if (obj == null) { + return null; + } + if (obj.getClass().isArray()) { + if (isPrimitive(obj)) { + if (obj instanceof byte[]) { + return Arrays.toString((byte[]) obj); + } else if (obj instanceof short[]) { + return Arrays.toString((short[]) obj); + } else if (obj instanceof int[]) { + return Arrays.toString((int[]) obj); + } else if (obj instanceof long[]) { + return Arrays.toString((long[]) obj); + } else if (obj instanceof double[]) { + return Arrays.toString((double[]) obj); + } else if (obj instanceof float[]) { + return Arrays.toString((float[]) obj); + } else if (obj instanceof char[]) { + return Arrays.toString((char[]) obj); + } else if (obj instanceof boolean[]) { + return Arrays.toString((boolean[]) obj); + } + return Arrays.toString((Object[]) obj); + } else { + return Arrays.deepToString((Object[]) obj); + } + } + return obj.toString(); + } + + /** + * 判断输入的数据是否为基本数据类型 基本数据类型和基本数据类型的数组类型都是做是基本数据类型 null视作非基本数据类型 + * + * @param obj the obj + * @return the boolean + * @see #isPrimitive(Class) + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isPrimitive(Object obj) { + if (null == obj) { + return false; + } + return isPrimitive(obj.getClass()); + } + + /** + * 判断 mehtod 的返回值是否为基础类型: + * 1. 8 个非包装类的基础类型; + * 2. 8 个基础类的包装类型; + * 3. String 类型 + * 4. String[] 类型 + * 5. 集合类型 + * 6. 基础类型数组 + * + * @param method method + * @return the boolean + * @since 1.7.1 + */ + public static boolean isExtendPrimitive(Method method) { + Class outClass = getaOutParamClass(method); + // 判断是否为基础数据类型 + return isExtendPrimitive(outClass); + } + + /** + * Is extend primitive + * + * @param clazz clazz + * @return the boolean + * @since 1.7.1 + */ + public static boolean isExtendPrimitive(Class clazz) { + return isPrimitive(clazz) + || isPrimitive(typeUnBoxing(clazz)) + || String.class.isAssignableFrom(clazz) + || String[].class.isAssignableFrom(clazz) + || Collection.class.isAssignableFrom(clazz); + } + + /** + * 获取 method 的返回类型的 class + * + * @param method method + * @return the out param class + * @since 1.7.1 + */ + public static Class getaOutParamClass(Method method) { + Type returnType = method.getGenericReturnType(); + // 请求结果不为空时处理如下逻辑 + Class outClass = null; + //判断是否是参数化类型[例如: List\List>\List] + if (returnType instanceof ParameterizedType) { + //参数话类型如: Result\Result>\Result>> 最多需要判断三层来确定参数化类型的具体元素类型 + ParameterizedType parameterizedType = (ParameterizedType) returnType; + log.trace("返回值类型是参数化类型, 其类型为: {}", parameterizedType); + // 获取第二层参数 + Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0]; + if (actualTypeArgument instanceof ParameterizedType) { + outClass = (Class) ((ParameterizedType) actualTypeArgument).getRawType(); + // 如果参数类型还是集合, 则需要再进一步判断集合里的元素类型 + if (Collection.class.isAssignableFrom(outClass)) { + // 获取第三层参数 + Type subActualTypeArgument = ((ParameterizedType) actualTypeArgument).getActualTypeArguments()[0]; + if (subActualTypeArgument instanceof ParameterizedType) { + outClass = (Class) ((ParameterizedType) subActualTypeArgument).getRawType(); + } else { + outClass = (Class) subActualTypeArgument; + } + } + } else if (actualTypeArgument instanceof WildcardType) { + // 如果是 Result, 则设置为 Object.class, 由 Jackson 序列化返回 + outClass = Object.class; + } else if (actualTypeArgument instanceof TypeVariable) { + // 如果是还是泛型, 则返回原始类型, 由 Jackson 序列化返回 + outClass = method.getReturnType(); + } else { + // 强转 获取实际参数类型数组, 比如List, 则获取的是数组[Dog], Map 则获取的是数组[Dog, Cat] + outClass = (Class) actualTypeArgument; + } + } else { + log.trace("返回值类型不是参数化类型其类型为: {}", returnType); + // 不是参数化类型,直接获取返回值类型: 即当返回值是: int/Integer等或实体时 + outClass = method.getReturnType(); + } + + return outClass; + } + + /** + * 判断输入的数据类型是否为基本数据类型 基本数据类型和基本数据类型的数组类型都是做是基本数据类型 null视作非基本数据类型 void为基本数据类型 + * log.info("{}", isPrimitive(Long[].class)); //false + * log.info("{}", isPrimitive(Long.class)); //false + * log.info("{}", isPrimitive(long[].class)); //true + * log.info("{}", isPrimitive(long.class)); //true + * log.info("{}", isPrimitive(void.class)); //true + * log.info("{}", isPrimitive(Object[].class));//false + * log.info("{}", isPrimitive(Object.class)); //false + * + * @param clazz the clazz + * @return the boolean + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isPrimitive(Class clazz) { + if (null == clazz) { + return false; + } + // 基础类型(非包装类型) + if (clazz.isPrimitive()) { + return true; + } + if (clazz.isArray()) { + return int[].class.isAssignableFrom(clazz) + || long[].class.isAssignableFrom(clazz) + || double[].class.isAssignableFrom(clazz) + || float[].class.isAssignableFrom(clazz) + || short[].class.isAssignableFrom(clazz) + || byte[].class.isAssignableFrom(clazz) + || char[].class.isAssignableFrom(clazz) + || boolean[].class.isAssignableFrom(clazz); + } + return false; + } + + /** + * 判断输入的数据是否是char + * + * @param obj the obj + * @return the boolean + * @see #isChar(Class) + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isChar(Object obj) { + if (obj == null) { + return false; + } + if (isChar(obj.getClass())) { + return true; + } + return obj.toString().length() == 1; + } + + /** + * 判断输入的数据类型是否是char + * + * @param clazz the clazz + * @return the boolean + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isChar(Class clazz) { + if (clazz == null) { + return false; + } + if (char.class.isAssignableFrom(clazz)) { + return true; + } + return Character.class.isAssignableFrom(clazz); + } + + /** + * 判断输入的数据是否是数字 支持基本数据类型和装箱数据类型的判断 支持数组数据类型的判断 + * null视作不是数字 存在容错机制,如果数据类型不是数字,继续判断toString字符串是否为数字,并兼容正负号 + * + * @param obj the obj + * @return the boolean + * @see #isNumber(Class) + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isNumber(Object obj) { + if (null == obj) { + return false; + } + if (isNumber(obj.getClass())) { + return true; + } + // 进行容错,如果数据类型对不上,但依然有可能是字符串数字,那么用正则表达式判断是否是数字,并兼容正负号 + return obj.toString().matches("^([-+])?\\d+(\\.\\d+)?$"); + } + + /** + * 判断输入的数据类型是否是数字 支持基本数据类型和装箱数据类型的判断 支持数组数据类型的判断 null视作不是数字 + * log.info("{}", isNumber(Long[].class));//true + * log.info("{}", isNumber(Long.class)); //true + * log.info("{}", isNumber(long.class)); //true + * log.info("{}", isNumber(long.class)); //true + * log.info("{}", isNumber(void.class)); //false + * log.info("{}", isNumber(Object[].class));//false + * log.info("{}", isNumber(Object.class));//false + * + * @param clazz the clazz + * @return the boolean + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isNumber(Class clazz) { + if (null == clazz) { + return false; + } + if (Number.class.isAssignableFrom(clazz)) { + return true; + } + if (Number[].class.isAssignableFrom(clazz)) { + return true; + } + if (clazz.isArray()) { + return int[].class.isAssignableFrom(clazz) + || long[].class.isAssignableFrom(clazz) + || double[].class.isAssignableFrom(clazz) + || float[].class.isAssignableFrom(clazz) + || short[].class.isAssignableFrom(clazz) + || byte[].class.isAssignableFrom(clazz) + || char[].class.isAssignableFrom(clazz); + } + if (isPrimitive(clazz)) { + return int.class.isAssignableFrom(clazz) + || long.class.isAssignableFrom(clazz) + || double.class.isAssignableFrom(clazz) + || float.class.isAssignableFrom(clazz) + || short.class.isAssignableFrom(clazz) + || byte.class.isAssignableFrom(clazz) + || char.class.isAssignableFrom(clazz); + } + return false; + } + + /** + * 判断对象是否是日期; + * 进行类型判断以及内容判断; + * 内容判断为判断字符串格式是否是时间格式; + * 字符串判断: 日期部分兼容减号、斜杠、点号分隔; 时间部分兼容冒号、点号; 兼容空格; + * 特别提醒: 2011.11这种既是日期又是数字,在这里也返回true,单纯的2011也会返回true + * + * @param obj the obj + * @return the boolean + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isDate(Object obj) { + if (null == obj) { + return false; + } + if (isDate(obj.getClass())) { + return true; + } + // 进行容错,如果数据类型对不上,但依然有可能是字符串日期,那么用正则表达式判断是否日期,日期部分兼容减号、斜杠、点号分隔; 时间部分兼容冒号、点号; 兼容空格; + return obj.toString().matches("^" + + "( *)(\\d{1,4})( *)" //年 + + "(([-/.])?)( *)((\\d{1,2})?)( *)" //月 + + "(([-/.])?)( *)((\\d{1,2})?)( *)" //日 + + "((( +)\\d{1,2})?)( *)" //时 + + "(([:.])?)( *)((\\d{1,2})?)( *)" //分 + + "(([:.])?)( *)((\\d{1,2})?)( *)" //秒 + + "(([:.])?)( *)((\\d{1,3})?)( *)" //毫秒 + + "$"); + } + + /** + * 判断Class是否是日期 + * + * @param clazz the clazz + * @return the boolean + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isDate(Class clazz) { + if (null == clazz) { + return false; + } + return Date.class.isAssignableFrom(clazz); + } + + /** + * 对输入数据进行装箱 如果输入数据是基本数据类型则返回装箱数据类型的数据 可以对基本数据类型的数组进行装箱 + * + * @param srcObj the src obj + * @return the object + * @see #typeBoxing(Class) + * @since 1.0.0 + */ + @Contract("null -> null") + public static Object boxing(Object srcObj) { + if (null == srcObj) { + return null; + } + Class objClazz = srcObj.getClass(); + if (!isPrimitive(objClazz)) { + return srcObj; + } + Object obj; + if (!objClazz.isArray()) { + obj = boxingObject(srcObj, objClazz); + } else { + obj = boxingArray(srcObj, objClazz); + } + return obj; + } + + /** + * Process array object + * + * @param srcObj src obj + * @param objClazz obj clazz + * @return the object + * @since 1.0.0 + */ + @Nullable + private static Object boxingArray(Object srcObj, Class objClazz) { + if (int[].class.isAssignableFrom(objClazz)) { + int[] obj = (int[]) srcObj; + Integer[] ret = new Integer[obj.length]; + for (int i = 0; i < obj.length; i++) { + ret[i] = obj[i]; + } + return ret; + } else if (long[].class.isAssignableFrom(objClazz)) { + long[] obj = (long[]) srcObj; + Long[] ret = new Long[obj.length]; + for (int i = 0; i < obj.length; i++) { + ret[i] = obj[i]; + } + return ret; + } else if (double[].class.isAssignableFrom(objClazz)) { + double[] obj = (double[]) srcObj; + Double[] ret = new Double[obj.length]; + for (int i = 0; i < obj.length; i++) { + ret[i] = obj[i]; + } + return ret; + } else if (float[].class.isAssignableFrom(objClazz)) { + float[] obj = (float[]) srcObj; + Float[] ret = new Float[obj.length]; + for (int i = 0; i < obj.length; i++) { + ret[i] = obj[i]; + } + return ret; + } else if (short[].class.isAssignableFrom(objClazz)) { + short[] obj = (short[]) srcObj; + Short[] ret = new Short[obj.length]; + for (int i = 0; i < obj.length; i++) { + ret[i] = obj[i]; + } + return ret; + } else if (byte[].class.isAssignableFrom(objClazz)) { + byte[] obj = (byte[]) srcObj; + Byte[] ret = new Byte[obj.length]; + for (int i = 0; i < obj.length; i++) { + ret[i] = obj[i]; + } + return ret; + } else if (char[].class.isAssignableFrom(objClazz)) { + char[] obj = (char[]) srcObj; + Character[] ret = new Character[obj.length]; + for (int i = 0; i < obj.length; i++) { + ret[i] = obj[i]; + } + return ret; + } else if (boolean[].class.isAssignableFrom(objClazz)) { + boolean[] obj = (boolean[]) srcObj; + Boolean[] ret = new Boolean[obj.length]; + for (int i = 0; i < obj.length; i++) { + ret[i] = obj[i]; + } + return ret; + } + return null; + } + + /** + * Process object object + * + * @param srcObj src obj + * @param objClazz obj clazz + * @return the object + * @since 1.0.0 + */ + @Nullable + @SuppressWarnings("all") + private static Object boxingObject(Object srcObj, Class objClazz) { + if (int.class.isAssignableFrom(objClazz)) { + return srcObj; + } else if (long.class.isAssignableFrom(objClazz)) { + return Long.valueOf((long) srcObj); + } else if (double.class.isAssignableFrom(objClazz)) { + return Double.valueOf((double) srcObj); + } else if (float.class.isAssignableFrom(objClazz)) { + return Float.valueOf((float) srcObj); + } else if (short.class.isAssignableFrom(objClazz)) { + return Short.valueOf((short) srcObj); + } else if (byte.class.isAssignableFrom(objClazz)) { + return Byte.valueOf((byte) srcObj); + } else if (char.class.isAssignableFrom(objClazz)) { + return Character.valueOf((char) srcObj); + } else if (boolean.class.isAssignableFrom(objClazz)) { + return Boolean.valueOf((boolean) srcObj); + } + return null; + } + + /** + * 对输入数据进行拆箱 如果输入数据是包装数据类型则返回拆箱数据类型的数据 可以对包装数据类型的数组进行拆箱 + * + * @param obj the obj + * @return the object + * @see #typeUnBoxing(Class) + * @since 1.0.0 + */ + @Contract("null -> null") + public static Object unBoxing(Object obj) { + if (null == obj) { + return null; + } + Class clazz = obj.getClass(); + if (isPrimitive(clazz)) { + return obj; + } + if (!clazz.isArray()) { + return unBoxingObject(obj, clazz); + } else { + return unBoxingArray(obj, clazz); + } + } + + /** + * Un boxing object object + * + * @param obj obj + * @param clazz clazz + * @return the object + * @since 1.0.0 + */ + @Contract("_, _ -> param1") + private static Object unBoxingObject(Object obj, Class clazz) { + if (Integer.class.isAssignableFrom(clazz)) { + return obj; + } else if (Long.class.isAssignableFrom(clazz)) { + return obj; + } else if (Double.class.isAssignableFrom(clazz)) { + return obj; + } else if (Float.class.isAssignableFrom(clazz)) { + return obj; + } else if (Short.class.isAssignableFrom(clazz)) { + return obj; + } else if (Byte.class.isAssignableFrom(clazz)) { + return obj; + } else if (Character.class.isAssignableFrom(clazz)) { + return obj; + } else { + // Boolean + return obj; + } + } + + /** + * Un boxing array object + * + * @param obj obj + * @param clazz clazz + * @return the object + * @since 1.0.0 + */ + private static Object unBoxingArray(Object obj, Class clazz) { + if (Integer[].class.isAssignableFrom(clazz)) { + Integer[] tmp = (Integer[]) obj; + int[] newObj = new int[tmp.length]; + for (int i = 0; i < tmp.length; i++) { + newObj[i] = (int) unBoxing(tmp[i]); + } + return newObj; + } else if (Long[].class.isAssignableFrom(clazz)) { + Long[] tmp = (Long[]) obj; + long[] newObj = new long[tmp.length]; + for (int i = 0; i < tmp.length; i++) { + newObj[i] = (long) unBoxing(tmp[i]); + } + return newObj; + } else if (Double[].class.isAssignableFrom(clazz)) { + Double[] tmp = (Double[]) obj; + double[] newObj = new double[tmp.length]; + for (int i = 0; i < tmp.length; i++) { + newObj[i] = (double) unBoxing(tmp[i]); + } + return newObj; + } else if (Float[].class.isAssignableFrom(clazz)) { + Float[] tmp = (Float[]) obj; + float[] newObj = new float[tmp.length]; + for (int i = 0; i < tmp.length; i++) { + newObj[i] = (float) unBoxing(tmp[i]); + } + return newObj; + } else if (Short[].class.isAssignableFrom(clazz)) { + Short[] tmp = (Short[]) obj; + short[] newObj = new short[tmp.length]; + for (int i = 0; i < tmp.length; i++) { + newObj[i] = (short) unBoxing(tmp[i]); + } + return newObj; + } else if (Byte[].class.isAssignableFrom(clazz)) { + Byte[] tmp = (Byte[]) obj; + byte[] newObj = new byte[tmp.length]; + for (int i = 0; i < tmp.length; i++) { + newObj[i] = (byte) unBoxing(tmp[i]); + } + return newObj; + } else if (Character[].class.isAssignableFrom(clazz)) { + Character[] tmp = (Character[]) obj; + char[] newObj = new char[tmp.length]; + for (int i = 0; i < tmp.length; i++) { + newObj[i] = (char) unBoxing(tmp[i]); + } + return newObj; + } else { + Boolean[] tmp = (Boolean[]) obj; + boolean[] newObj = new boolean[tmp.length]; + for (int i = 0; i < tmp.length; i++) { + newObj[i] = (boolean) unBoxing(tmp[i]); + } + return newObj; + } + } + + /** + * 对输入数据类型进行装箱 把八种基本数据类型装箱为包装类型 支持对基本数据类型的数组类型装箱为包装类型 + * void类型以及非基本数据类型不进行装箱原样返回 输入null返回null不作处理 + * log.info("{}", typeBoxing(Long[].class));// 原样返回=>class [Ljava.lang.Long; + * log.info("{}", typeBoxing(Long.class));// 原样返回=>class java.lang.Long + * log.info("{}", typeBoxing(long[].class));// 装箱=>class [Ljava.lang.Long; + * log.info("{}", typeBoxing(long.class));// 装箱=>class java.lang.Long + * log.info("{}", typeBoxing(void.class));// 原样返回=>void + * log.info("{}", typeBoxing(Object[].class));// 原样返回=>class [Ljava.lang.Object; + * log.info("{}", typeBoxing(Object.class));// 原样返回=>class java.lang.Object + * + * @param clazz the clazz + * @return the class + * @see #typeUnBoxing(Class) + * @since 1.0.0 + */ + @Contract("null -> null") + public static Class typeBoxing(Class clazz) { + if (null == clazz) { + return null; + } + if (!isPrimitive(clazz)) { + return clazz; + } + if (!clazz.isArray()) { + if (int.class.isAssignableFrom(clazz)) { + return Integer.class; + } else if (long.class.isAssignableFrom(clazz)) { + return Long.class; + } else if (double.class.isAssignableFrom(clazz)) { + return Double.class; + } else if (float.class.isAssignableFrom(clazz)) { + return Float.class; + } else if (short.class.isAssignableFrom(clazz)) { + return Short.class; + } else if (byte.class.isAssignableFrom(clazz)) { + return Byte.class; + } else if (char.class.isAssignableFrom(clazz)) { + return Character.class; + } else if (boolean.class.isAssignableFrom(clazz)) { + return Boolean.class; + } + } else { + if (int[].class.isAssignableFrom(clazz)) { + return Integer[].class; + } else if (long[].class.isAssignableFrom(clazz)) { + return Long[].class; + } else if (double[].class.isAssignableFrom(clazz)) { + return Double[].class; + } else if (float[].class.isAssignableFrom(clazz)) { + return Float[].class; + } else if (short[].class.isAssignableFrom(clazz)) { + return Short[].class; + } else if (byte[].class.isAssignableFrom(clazz)) { + return Byte[].class; + } else if (char[].class.isAssignableFrom(clazz)) { + return Character[].class; + } else if (boolean[].class.isAssignableFrom(clazz)) { + return Boolean[].class; + } + } + return clazz; + } + + /** + * 对输入数据类型进行拆箱 把八种基本数据类型的包装类拆箱为基本类型 支持对包装数据类型的数组类型拆箱为基本数据类型的数组类型 + * void类型和基本数据类型不进行拆箱原样返回 输入null返回null不作处理 + * log.info("{}", typeUnBoxing(Long[].class));// 拆箱=>class [J + * log.info("{}", typeUnBoxing(Long.class));// 拆箱=>long + * log.info("{}", typeUnBoxing(long[].class));// 原样返回=>class [J + * log.info("{}", typeUnBoxing(long.class));// 原样返回=>long + * log.info("{}", typeUnBoxing(void.class));// 原样返回=>void + * log.info("{}", typeUnBoxing(Object[].class));// 原样返回=>class [Ljava.lang.Object; + * log.info("{}", typeUnBoxing(Object.class));// 原样返回=>class java.lang.Object + * + * @param clazz the clazz + * @return the class + * @see #typeBoxing(Class) + * @since 1.0.0 + */ + @Contract("null -> null") + public static Class typeUnBoxing(Class clazz) { + if (null == clazz) { + return null; + } + if (isPrimitive(clazz)) { + return clazz; + } + if (!clazz.isArray()) { + if (Integer.class.isAssignableFrom(clazz)) { + return int.class; + } else if (Long.class.isAssignableFrom(clazz)) { + return long.class; + } else if (Double.class.isAssignableFrom(clazz)) { + return double.class; + } else if (Float.class.isAssignableFrom(clazz)) { + return float.class; + } else if (Short.class.isAssignableFrom(clazz)) { + return short.class; + } else if (Byte.class.isAssignableFrom(clazz)) { + return byte.class; + } else if (Character.class.isAssignableFrom(clazz)) { + return char.class; + } else if (Boolean.class.isAssignableFrom(clazz)) { + return boolean.class; + } + } else { + if (Integer[].class.isAssignableFrom(clazz)) { + return int[].class; + } else if (Long[].class.isAssignableFrom(clazz)) { + return long[].class; + } else if (Double[].class.isAssignableFrom(clazz)) { + return double[].class; + } else if (Float[].class.isAssignableFrom(clazz)) { + return float[].class; + } else if (Short[].class.isAssignableFrom(clazz)) { + return short[].class; + } else if (Byte[].class.isAssignableFrom(clazz)) { + return byte[].class; + } else if (Character[].class.isAssignableFrom(clazz)) { + return char[].class; + } else if (Boolean[].class.isAssignableFrom(clazz)) { + return boolean[].class; + } + } + return clazz; + } + + /** + * The type Data type exception. + * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:55 + * @since 1.0.0 + */ + static final class DataTypeException extends BasicException { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = 4654330042965245163L; + + /** + * Instantiates a new Data type exception. + * + * @param message the message + * @since 1.0.0 + */ + DataTypeException(String message) { + super(message); + } + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:55 + * @since 1.0.0 + */ + private static final class CatchDataTypeException { + + /** + * LONG_MAX + */ + private static final BigDecimal LONG_MAX = BigDecimal.valueOf(Long.MAX_VALUE); + /** + * LONG_MIN + */ + private static final BigDecimal LONG_MIN = BigDecimal.valueOf(Long.MIN_VALUE); + /** + * INTEGER_MAX + */ + private static final BigDecimal INTEGER_MAX = BigDecimal.valueOf(Integer.MAX_VALUE); + /** + * INTEGER_MIN + */ + private static final BigDecimal INTEGER_MIN = BigDecimal.valueOf(Integer.MIN_VALUE); + /** + * SHORT_MAX + */ + private static final BigDecimal SHORT_MAX = BigDecimal.valueOf(Short.MAX_VALUE); + /** + * SHORT_MIN + */ + private static final BigDecimal SHORT_MIN = BigDecimal.valueOf(Short.MIN_VALUE); + /** + * BYTE_MAX + */ + private static final BigDecimal BYTE_MAX = BigDecimal.valueOf(Byte.MAX_VALUE); + /** + * BYTE_MIN + */ + private static final BigDecimal BYTE_MIN = BigDecimal.valueOf(Byte.MIN_VALUE); + /** + * CHAR_MAX + */ + private static final BigDecimal CHAR_MAX = BigDecimal.valueOf(Character.MAX_VALUE); + /** + * CHAR_MIN + */ + private static final BigDecimal CHAR_MIN = BigDecimal.valueOf(Character.MIN_VALUE); + /** + * DOUBLE_MAX + */ + private static final BigDecimal DOUBLE_MAX = BigDecimal.valueOf(Double.MAX_VALUE); + /** + * DOUBLE_MIN + */ + private static final BigDecimal DOUBLE_MIN = BigDecimal.valueOf(-Double.MAX_VALUE); + /** + * FLOAT_MAX + */ + private static final BigDecimal FLOAT_MAX = BigDecimal.valueOf(Float.MAX_VALUE); + /** + * FLOAT_MIN + */ + private static final BigDecimal FLOAT_MIN = BigDecimal.valueOf(-Float.MAX_VALUE); + + /** + * 如果是小数,则抛出无法转换数据类型的异常 + * + * @param parameter + * @param clazz clazz + * @param targetData target data + * @author hcqt @qq.com + * @since 1.0.0 + */ + private static void notDecimal(Class clazz, @NotNull BigDecimal targetData) { + BigDecimal bigDecimal = targetData.setScale(0, BigDecimal.ROUND_CEILING); + if (targetData.compareTo(bigDecimal) != 0) { + throw new DataTypeException( + StrFormatter.format("类型为 {} 的数据 {} 为小数,无法转换为类型 {}", + targetData.getClass(), + targetData, + clazz.getName())); + } + } + + /** + * Is between span * + * + * @param parameter + * @param destClazz dest clazz + * @param srcObj src obj + * @author hcqt @qq.com + * @see #isBetweenSpan(Class, Object) #isBetweenSpan(Class, Object)#isBetweenSpan(Class, Object) + * @since 1.0.0 + */ + private static void isBetweenSpan(Class destClazz, BigDecimal srcObj) { + isBetweenSpan(destClazz, (Object) srcObj); + } + + /** + * 检查目标数字是否介于最大值与最小值之间,符合指定数据类型的取值范围 + * + * @param parameter + * @param destClazz dest clazz + * @param srcObj src obj + * @author hcqt @qq.com + * @since 1.0.0 + */ + private static void isBetweenSpan(Class destClazz, Object srcObj) { + if (!DataTypeUtils.isNumber(srcObj)) { + throw CatchDataTypeException.returnCouldNotConvertException(destClazz, srcObj); + } + BigDecimal max = null; + BigDecimal min = null; + if (Long.class.isAssignableFrom(destClazz)) { + max = LONG_MAX; + min = LONG_MIN; + } else if (Integer.class.isAssignableFrom(destClazz)) { + max = INTEGER_MAX; + min = INTEGER_MIN; + } else if (Double.class.isAssignableFrom(destClazz)) { + max = DOUBLE_MAX; + min = DOUBLE_MIN; + } else if (Float.class.isAssignableFrom(destClazz)) { + max = FLOAT_MAX; + min = FLOAT_MIN; + } else if (Short.class.isAssignableFrom(destClazz)) { + max = SHORT_MAX; + min = SHORT_MIN; + } else if (Byte.class.isAssignableFrom(destClazz)) { + max = BYTE_MAX; + min = BYTE_MIN; + } else if (Character.class.isAssignableFrom(destClazz)) { + max = CHAR_MAX; + min = CHAR_MIN; + } + BigDecimal src = new BigDecimal(srcObj.toString()); + boolean maxOverflow = max != null && src.compareTo(max) > 0; + boolean minOverflow = min != null && src.compareTo(min) < 0; + if (maxOverflow || minOverflow) { + throw new DataTypeException( + StrFormatter.format("类型为 {} 的数值 {} 不在数据类型 {} 可以容纳的数值范围内", + srcObj.getClass().getName(), + srcObj, + destClazz.getName())); + } + } + + /** + * 用于抛出无法转换数据类型的异常——输入的数据无法转换为预期的类型 该异常总是抛出,使用前你必须确定你是需要抛出此异常 + * + * @param parameter + * @param destClazz 预期的类型 + * @param srcObj 输入的数据 + * @return the data type exception + * @author hcqt @qq.com + * @since 1.0.0 + */ + @NotNull + @Contract("_, _ -> new") + private static DataTypeException returnCouldNotConvertException(Class destClazz, Object srcObj) { + return new DataTypeException( + StrFormatter.format("类型为 {} 的数据 {} 无法转换为类型 {}", + srcObj == null ? "null" : srcObj.getClass(), + srcObj, + destClazz == null ? "null" : destClazz.getName())); + } + + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:55 + * @since 1.0.0 + */ + private static final class Object2Byte { + + /** + * Convert byte + * + * @param targetObject target object + * @return the byte + * @since 1.0.0 + */ + private static Byte convert(@NotNull Object targetObject) { + if (targetObject.getClass().isEnum()) { + return convert(((Enum) targetObject).ordinal()); + } else if (isNumber(targetObject)) { + BigDecimal targetData = new BigDecimal(targetObject.toString()); + CatchDataTypeException.notDecimal(Byte.class, targetData); + CatchDataTypeException.isBetweenSpan(Byte.class, targetData); + if (targetObject instanceof String + || targetObject instanceof Long + || targetObject instanceof Byte + || targetObject instanceof Short + || targetObject instanceof Integer + || targetObject instanceof BigDecimal + || targetObject instanceof Float + || targetObject instanceof Double) { + return targetData.byteValue(); + } else { + throw CatchDataTypeException.returnCouldNotConvertException(Byte.class, targetObject); + } + } else if (isChar(targetObject)) { + return Integer.valueOf(targetObject.toString().charAt(0)).byteValue(); + } else if (isDate(targetObject)) { + Date d = DataTypeUtils.convert(Date.class, targetObject); + return convert(d.getTime()); + } + throw CatchDataTypeException.returnCouldNotConvertException(Byte.class, targetObject); + } + + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:55 + * @since 1.0.0 + */ + private static final class Object2Short { + + /** + * Convert short + * + * @param targetObject target object + * @return the short + * @since 1.0.0 + */ + private static Short convert(@NotNull Object targetObject) { + if (targetObject.getClass().isEnum()) { + return convert(((Enum) targetObject).ordinal()); + } else if (isNumber(targetObject)) { + BigDecimal targetData = BigDecimal.valueOf(Double.parseDouble(targetObject.toString())); + + CatchDataTypeException.notDecimal(Short.class, targetData); + CatchDataTypeException.isBetweenSpan(Short.class, targetData); + if (targetObject instanceof String + || targetObject instanceof Long + || targetObject instanceof Byte + || targetObject instanceof Short + || targetObject instanceof Integer + || targetObject instanceof BigDecimal + || targetObject instanceof Float + || targetObject instanceof Double + ) { + return targetData.shortValue(); + } else { + throw CatchDataTypeException.returnCouldNotConvertException(Short.class, targetObject); + } + } else if (isChar(targetObject)) { + return Integer.valueOf(targetObject.toString().charAt(0)).shortValue(); + } else if (isDate(targetObject)) { + Date d = DataTypeUtils.convert(Date.class, targetObject); + return convert(d.getTime()); + } + throw CatchDataTypeException.returnCouldNotConvertException(Short.class, targetObject); + } + + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:55 + * @since 1.0.0 + */ + private static final class Object2Integer { + + /** + * Convert integer + * + * @param targetObject target object + * @return the integer + * @since 1.0.0 + */ + private static Integer convert(@NotNull Object targetObject) { + if (targetObject.getClass().isEnum()) { + return ((Enum) targetObject).ordinal(); + } else if (isNumber(targetObject)) { + BigDecimal targetData = BigDecimal.valueOf(Double.parseDouble(targetObject.toString())); + + CatchDataTypeException.notDecimal(Integer.class, targetData); + CatchDataTypeException.isBetweenSpan(Integer.class, targetData); + if (targetObject instanceof String + || targetObject instanceof Long + || targetObject instanceof Byte + || targetObject instanceof Short + || targetObject instanceof Integer + || targetObject instanceof BigDecimal + || targetObject instanceof Float + || targetObject instanceof Double) { + return targetData.intValue(); + } else { + throw CatchDataTypeException.returnCouldNotConvertException(Integer.class, targetObject); + } + } else if (isChar(targetObject)) { + return (int) targetObject.toString().charAt(0); + } else if (isDate(targetObject)) { + Date d = DataTypeUtils.convert(Date.class, targetObject); + return convert(d.getTime()); + } + throw CatchDataTypeException.returnCouldNotConvertException(Integer.class, targetObject); + } + + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:55 + * @since 1.0.0 + */ + private static final class Object2Long { + + /** + * Convert long + * + * @param targetObject target object + * @return the long + * @since 1.0.0 + */ + private static Long convert(@NotNull Object targetObject) { + if (targetObject.getClass().isEnum()) { + return (long) ((Enum) targetObject).ordinal(); + } else if (isNumber(targetObject)) { + BigDecimal targetData = BigDecimal.valueOf(Double.parseDouble(targetObject.toString())); + CatchDataTypeException.notDecimal(Long.class, targetData); + CatchDataTypeException.isBetweenSpan(Long.class, targetData); + if (targetObject instanceof String + || targetObject instanceof Long + || targetObject instanceof Byte + || targetObject instanceof Short + || targetObject instanceof Integer + || targetObject instanceof BigDecimal + || targetObject instanceof Float + || targetObject instanceof Double + ) { + return targetData.longValue(); + } else { + throw CatchDataTypeException.returnCouldNotConvertException(Long.class, targetObject); + } + } else if (isChar(targetObject)) { + return (long) (int) targetObject.toString().charAt(0); + } else if (isDate(targetObject)) { + Date d = DataTypeUtils.convert(Date.class, targetObject); + return d.getTime(); + } + throw CatchDataTypeException.returnCouldNotConvertException(Long.class, targetObject); + } + + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:55 + * @since 1.0.0 + */ + private static final class Object2Double { + + /** + * Convert double + * + * @param targetObject target object + * @return the double + * @since 1.0.0 + */ + private static Double convert(@NotNull Object targetObject) { + if (targetObject.getClass().isEnum()) { + return (double) ((Enum) targetObject).ordinal(); + } else if (isNumber(targetObject)) { + BigDecimal targetData = BigDecimal.valueOf(Double.parseDouble(targetObject.toString())); + CatchDataTypeException.isBetweenSpan(Double.class, targetData); + return targetData.doubleValue(); + } else if (isChar(targetObject)) { + return (double) (int) targetObject.toString().charAt(0); + } else if (isDate(targetObject)) { + Date d = DataTypeUtils.convert(Date.class, targetObject); + return convert(d.getTime()); + } + throw CatchDataTypeException.returnCouldNotConvertException(Double.class, targetObject); + } + + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:55 + * @since 1.0.0 + */ + private static final class Object2Float { + + /** + * Convert float + * + * @param targetObject target object + * @return the float + * @since 1.0.0 + */ + private static Float convert(@NotNull Object targetObject) { + if (targetObject.getClass().isEnum()) { + return convert(((Enum) targetObject).ordinal()); + } else if (isNumber(targetObject)) { + BigDecimal targetData = BigDecimal.valueOf(Double.parseDouble(targetObject.toString())); + CatchDataTypeException.isBetweenSpan(Float.class, targetData); + if (targetObject instanceof String + || targetObject instanceof Long + || targetObject instanceof Byte + || targetObject instanceof Short + || targetObject instanceof Integer + || targetObject instanceof BigDecimal + || targetObject instanceof Float + || targetObject instanceof Double) { + return targetData.floatValue(); + } else { + throw CatchDataTypeException.returnCouldNotConvertException(Float.class, targetObject); + } + } else if (isChar(targetObject)) { + return (float) (int) targetObject.toString().charAt(0); + } else if (isDate(targetObject)) { + Date d = DataTypeUtils.convert(Date.class, targetObject); + return convert(d.getTime()); + } + throw CatchDataTypeException.returnCouldNotConvertException(Float.class, targetObject); + } + + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:55 + * @since 1.0.0 + */ + private static final class Object2BigDecimal { + + /** + * Convert big decimal + * + * @param targetObject target object + * @return the big decimal + * @since 1.0.0 + */ + private static BigDecimal convert(@NotNull Object targetObject) { + if (targetObject.getClass().isEnum()) { + return BigDecimal.valueOf(((Enum) targetObject).ordinal()); + } else if (isNumber(targetObject)) { + try { + return BigDecimal.valueOf(Double.parseDouble(targetObject.toString())); + } catch (Exception e) { + throw CatchDataTypeException.returnCouldNotConvertException(BigDecimal.class, targetObject); + } + } else if (isChar(targetObject)) { + return BigDecimal.valueOf((int) targetObject.toString().charAt(0)); + } else if (isDate(targetObject)) { + Date d = DataTypeUtils.convert(Date.class, targetObject); + return BigDecimal.valueOf(d.getTime()); + } + throw CatchDataTypeException.returnCouldNotConvertException(BigDecimal.class, targetObject); + } + + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:55 + * @since 1.0.0 + */ + private static final class Object2Character { + + /** + * Convert character + * + * @param targetObject target object + * @return the character + * @since 1.0.0 + */ + private static Character convert(Object targetObject) { + if (isChar(targetObject)) { + return targetObject.toString().charAt(0); + } else if (isNumber(targetObject)) { + BigDecimal b = DataTypeUtils.convert(BigDecimal.class, targetObject); + CatchDataTypeException.notDecimal(Character.class, b); + CatchDataTypeException.isBetweenSpan(Character.class, b); + return (char) b.intValue(); + } else if (targetObject.getClass().isEnum()) { + Integer i = DataTypeUtils.convert(Integer.class, targetObject); + return convert(i); + } else if (isDate(targetObject)) { + Date d = DataTypeUtils.convert(Date.class, targetObject); + return convert(d.getTime()); + } else if (targetObject instanceof String) { + String s = (String) targetObject; + if (s.length() != 1) { + throw CatchDataTypeException.returnCouldNotConvertException(Character.class, targetObject); + } + return (s).charAt(0); + } + throw CatchDataTypeException.returnCouldNotConvertException(Character.class, targetObject); + } + + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:55 + * @since 1.0.0 + */ + private static final class Object2Boolean { + + /** + * Convert boolean + * + * @param targetObject target object + * @return the boolean + * @since 1.0.0 + */ + @NotNull + private static Boolean convert(Object targetObject) { + if (targetObject instanceof String) { + return Boolean.valueOf((String) targetObject); + } else if (targetObject instanceof Boolean) { + return (Boolean) targetObject; + } + throw CatchDataTypeException.returnCouldNotConvertException(Boolean.class, targetObject); + } + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:55 + * @since 1.0.0 + */ + private static final class Object2Enum { + + /** + * Convert t + * + * @param parameter + * @param clazz clazz + * @param targetObject target object + * @return the t + * @since 1.0.0 + */ + private static T convert(@NotNull Class clazz, Object targetObject) { + T[] enumConstants = clazz.getEnumConstants(); + if (targetObject instanceof String) { + for (T enumElement : enumConstants) { + try { + Method method = enumElement.getClass().getMethod("name"); + Object enumName; + enumName = method.invoke(enumElement); + if (null == enumName) { + continue; + } + if (enumName.toString().equals(targetObject.toString())) { + return enumElement; + } + } catch (Exception ignored) { + } + } + } + // 尝试输入值是否可以转换为整型,如果可以,那么以枚举底层整型下标进行转换 + try { + Integer index = DataTypeUtils.convert(Integer.class, targetObject); + for (T enumElement : enumConstants) { + try { + Method method = enumElement.getClass().getMethod("ordinal"); + Object enumOrdinal; + enumOrdinal = method.invoke(enumElement); + if (null == enumOrdinal) { + continue; + } + if (enumOrdinal.toString().equals(index.toString())) { + return enumElement; + } + } catch (Exception ignored) { + } + } + } catch (BasicException ignored) { + } + throw new DataTypeException( + StrFormatter.format("无法把数据类型为 {} 的数据 {} 转换到枚举类型 {}", targetObject.getClass().getName(), targetObject, clazz)); + } + + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:55 + * @since 1.0.0 + */ + private static final class Object2String { + + /** + * Convert string + * + * @param targetObject target object + * @return the string + * @since 1.0.0 + */ + private static String convert(Object targetObject) { + String ret = null; + if (isDate(targetObject) && !isNumber(targetObject)) { + // 这里需要既是日期,又不是数字才进行转换,存在例如数据2011.11|2011这种既是数字又是时间的数据中抉择的问题,那么此处偏向与认为这是一个数字,不转换为日期显示 + Date d = Object2Date.convert(targetObject); + SimpleDateFormat formatter = new SimpleDateFormat(JsonUtils.PATTERN_DATETIME); + ret = formatter.format(d); + } + + if (null == ret && targetObject != null) { + ret = targetObject.toString(); + } + return ret; + } + + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 15:54 + * @since 1.0.0 + */ + private static final class Object2Date { + + /** + * Convert date + * + * @param targetObject target object + * @return the date + * @since 1.0.0 + */ + @Nullable + private static Date convert(Object targetObject) { + if (!isDate(targetObject)) { + return null; + } else { + if (targetObject instanceof Date) { + return (Date) targetObject; + } else if (targetObject instanceof String) { + try { + return DateUtils.parseDate((String) targetObject, JsonUtils.PATTERN_DATETIME); + } catch (Exception ignored) { + } + } + } + // 注意此处不用else if的原因是由于存在2011.11这种既是日期又是数字的情况,所以不能用else if做互斥代码 + if (isNumber(targetObject)) { + try { + return new Date(DataTypeUtils.convert(Long.class, targetObject)); + } catch (BasicException ignored) { + } + } + + throw CatchDataTypeException.returnCouldNotConvertException(Date.class, targetObject); + } + + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/EnumUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/EnumUtils.java new file mode 100644 index 0000000..489f6dc --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/EnumUtils.java @@ -0,0 +1,79 @@ +package com.aivfo.el.starter.core.util; + +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 枚举工具类 + */ +@Slf4j +@UtilityClass +@SuppressWarnings("all") +public class EnumUtils { + /** + * 枚举类缓存 + */ + private static final Map, Object> ENUM_MAP = new ConcurrentHashMap<>(); + + /** + * 根据条件获取枚举对象 + * + * @param the type parameter + * @param cla 枚举类 + * @param predicate 筛选条件 + * @return enum optional + * @since 1.0.0 + */ + public static Optional of(@NotNull Class cla, Predicate predicate) { + if (!cla.isEnum()) { + log.info("Class 不是枚举类 cla ={}", cla); + return Optional.empty(); + } + Object obj = ENUM_MAP.get(cla); + T[] ts; + if (obj == null) { + ts = cla.getEnumConstants(); + ENUM_MAP.put(cla, ts); + } else { + ts = (T[]) obj; + } + return Arrays.stream(ts).filter(predicate).findAny(); + } + + /** + * 通过枚举的 index 获取枚举 + * + * @param the type parameter + * @param clazz the clazz + * @param ordinal the ordinal 需要的枚举值在设定的枚举类中的顺序, 以 0 开始 + * @return t t + * @author xiehao + * @since 1.0.0 + */ + public static > T indexOf(@NotNull Class clazz, int ordinal) { + return clazz.getEnumConstants()[ordinal]; + } + + /** + * 传入的参数 name 指的是枚举值的名称, 一般是大写加下划线的 + * + * @param the type parameter + * @param clazz the clazz + * @param name the name + * @return Enum T + * @author xiehao + * @since 1.0.0 + */ + @NotNull + public static > T nameOf(Class clazz, String name) { + return Enum.valueOf(clazz, name); + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/INetUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/INetUtils.java new file mode 100644 index 0000000..505f9c1 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/INetUtils.java @@ -0,0 +1,1027 @@ +package com.aivfo.el.starter.core.util; + +import com.aivfo.el.starter.base.utils.TimeoutUtils; +import lombok.Data; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.SystemUtils; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.Closeable; +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.*; +import java.util.regex.Pattern; + +@Slf4j +@SuppressWarnings("all") +abstract class INetUtils { + /** + * returned port range is [30000, 39999] + */ + private static final int RND_PORT_START = 30000; + /** + * RND_PORT_RANGE + */ + private static final int RND_PORT_RANGE = 10000; + /** + * ADDRESS_PATTERN + */ + private static final Pattern ADDRESS_PATTERN = Pattern.compile("^\\d{1,3}(\\.\\d{1,3}){3}\\:\\d{1,5}$"); + /** + * LOCAL_IP_PATTERN + */ + private static final Pattern LOCAL_IP_PATTERN = Pattern.compile("127(\\.\\d{1,3}){3}$"); + /** + * IP_PATTERN + */ + private static final Pattern IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$"); + /** + * HOST_NAME_CACHE + */ + private static final Map HOST_NAME_CACHE = new LRUCache<>(1000); + /** + * LOCAL_ADDRESS + */ + private static volatile InetAddress localAddress = null; + /** + * SPLIT_IPV4_CHARECTER + */ + private static final String SPLIT_IPV4_CHARECTER = "\\."; + /** + * SPLIT_IPV6_CHARECTER + */ + private static final String SPLIT_IPV6_CHARECTER = ":"; + /** + * ANYHOST_VALUE + */ + public static final String ANYHOST_VALUE = "0.0.0.0"; + /** + * DUBBO_IP_TO_BIND + */ + public static final String FKH_IP_TO_BIND = "FKH_IP_TO_BIND"; + /** + * LOCALHOST_KEY + */ + public static final String LOCALHOST_KEY = InetUtilsProperties.LOCALHOST_KEY; + /** + * LOCALHOST_VALUE + */ + public static final String LOCALHOST_VALUE = InetUtilsProperties.LOCALHOST_VALUE; + /** + * MIN_PORT + */ + public static final int MIN_PORT = 0; + /** + * MAX_PORT + */ + public static final int MAX_PORT = 65535; + + /** + * Gets random port * + * + * @return the random port + * @since 1.5.0 + */ + public static int getRandomPort() { + return RND_PORT_START + ThreadLocalRandom.current().nextInt(RND_PORT_RANGE); + } + + /** + * Gets available port * + * + * @return the available port + * @since 1.5.0 + */ + public static int getAvailablePort() { + try (ServerSocket ss = new ServerSocket()) { + ss.bind(null); + return ss.getLocalPort(); + } catch (IOException e) { + return getRandomPort(); + } + } + + /** + * Gets available port * + * + * @param port port + * @return the available port + * @since 1.5.0 + */ + public static int getAvailablePort(int port) { + if (port <= 0) { + return getAvailablePort(); + } + for (int i = port; i < MAX_PORT; i++) { + try (ServerSocket ss = new ServerSocket(i)) { + return i; + } catch (IOException ignore) { + } + } + return port; + } + + /** + * Is invalid port + * + * @param port port + * @return the boolean + * @since 1.5.0 + */ + @Contract(pure = true) + public static boolean isInvalidPort(int port) { + return port <= MIN_PORT || port > MAX_PORT; + } + + /** + * Is valid address + * + * @param address address + * @return the boolean + * @since 1.5.0 + */ + public static boolean isValidAddress(String address) { + return ADDRESS_PATTERN.matcher(address).matches(); + } + + /** + * Is local host + * + * @param host host + * @return the boolean + * @since 1.5.0 + */ + @Contract("null -> false") + public static boolean isLocalHost(String host) { + return host != null + && (LOCAL_IP_PATTERN.matcher(host).matches() + || host.equalsIgnoreCase(LOCALHOST_KEY)); + } + + /** + * Is any host + * + * @param host host + * @return the boolean + * @since 1.5.0 + */ + @Contract(value = "null -> false", pure = true) + public static boolean isAnyHost(String host) { + return ANYHOST_VALUE.equals(host); + } + + /** + * Is invalid local host + * + * @param host host + * @return the boolean + * @since 1.5.0 + */ + @Contract("null -> true") + public static boolean isInvalidLocalHost(String host) { + return host == null + || host.length() == 0 + || host.equalsIgnoreCase(LOCALHOST_KEY) + || host.equals(ANYHOST_VALUE) + || (LOCAL_IP_PATTERN.matcher(host).matches()); + } + + /** + * Is valid local host + * + * @param host host + * @return the boolean + * @since 1.5.0 + */ + @Contract("null -> false") + public static boolean isValidLocalHost(String host) { + return !isInvalidLocalHost(host); + } + + /** + * Gets local socket address * + * + * @param host host + * @param port port + * @return the local socket address + * @since 1.5.0 + */ + public static @NotNull InetSocketAddress getLocalSocketAddress(String host, int port) { + return isInvalidLocalHost(host) + ? new InetSocketAddress(port) + : new InetSocketAddress(host, port); + } + + /** + * Is valid v 4 address + * + * @param address address + * @return the boolean + * @since 1.5.0 + */ + @Contract("null -> false") + static boolean isValidV4Address(InetAddress address) { + if (address == null || address.isLoopbackAddress()) { + return false; + } + String name = address.getHostAddress(); + return (name != null + && IP_PATTERN.matcher(name).matches() + && !ANYHOST_VALUE.equals(name) + && !LOCALHOST_VALUE.equals(name)); + } + + /** + * Check if an ipv6 address + * + * @return true if it is reachable + * @since 1.5.0 + */ + @Contract(pure = true) + static boolean isPreferIPV6Address() { + return Boolean.getBoolean("java.net.preferIPv6Addresses"); + } + + /** + * normalize the ipv6 Address, convert scope name to scope id. + * e.g. + * convert + * fe80:0:0:0:894:aeec:f37d:23e1%en0 + * to + * fe80:0:0:0:894:aeec:f37d:23e1%5 + *

+ * The %5 after ipv6 address is called scope id. + * see java doc of {@link Inet6Address} for more details. + * + * @param address the input address + * @return the normalized address, with scope id converted to int + * @since 1.5.0 + */ + static InetAddress normalizeV6Address(@NotNull Inet6Address address) { + String addr = address.getHostAddress(); + int i = addr.lastIndexOf('%'); + if (i > 0) { + try { + return TimeoutUtils.process(new Callable() { + @Override + public InetAddress call() throws Exception { + return InetAddress.getByName(addr.substring(0, i) + '%' + address.getScopeId()); + } + }, 1000); + } catch (Exception e) { + // ignore + log.debug("Unknown IPV6 address: ", e); + } + } + return address; + } + + /** + * Gets local host * + * + * @return the local host + * @since 1.5.0 + */ + public static String getLocalHost() { + InetAddress address = getLocalAddress(); + return address == null ? LOCALHOST_VALUE : address.getHostAddress(); + } + + /** + * Gets ip by config * + * todo-dong4j : (2021.01.24 02:46) [从配置文件中加载] + * + * @return the ip by config + * @since 1.5.0 + */ + public static String getIpByConfig() { + String configIp = System.getProperty(FKH_IP_TO_BIND); + if (configIp != null) { + return configIp; + } + + return getIpByHost(getLocalAddress().getHostName()); + } + + /** + * Find first valid IP from local network card + * + * @return first valid local IP + * @since 1.5.0 + */ + @SuppressWarnings("all") + public static InetAddress getLocalAddress() { + if (localAddress != null) { + return localAddress; + } + + InetAddress localAddress = getLocalAddress0(); + if (LOCALHOST_VALUE.equals(localAddress.getHostAddress())) { + log.warn("多网卡下获取服务器 IP[127.0.0.1] 错误,使用备用方案重新获取, 可通过添加 -D 直接覆盖 fkh.system.properties 配置"); + InetUtils inetUtils = new InetUtils(new InetUtilsProperties()); + localAddress = inetUtils.findFirstNonLoopbackAddress(); + } + + INetUtils.localAddress = localAddress; + return localAddress; + } + + /** + * To valid address + * + * @param address address + * @return the optional + * @since 1.5.0 + */ + private static Optional toValidAddress(InetAddress address) { + if (address instanceof Inet6Address) { + Inet6Address v6Address = (Inet6Address) address; + if (isPreferIPV6Address()) { + return Optional.ofNullable(normalizeV6Address(v6Address)); + } + } + if (isValidV4Address(address)) { + return Optional.of(address); + } + return Optional.empty(); + } + + /** + * https://stackoverflow.com/questions/9481865/getting-the-ip-address-of-the-current-machine-using-java + * 返回一个 InetAddress 对象, 该对象封装了最有可能是计算机的 LAN IP 地址. + * 此方法用于替代 JDK 方法 InetAddress.getLocalHost, 因为该方法在 Linux 系统上不明确. + * Linux 系统枚举环回网络接口的方式与常规 LAN 网络接口相同, 但 JDKInetAddress.getLocalHost 方法不指定用于选择在这种情况下返回的地址的算法, + * 并且通常会返回环回地址, 这对网络通信无效. 详情请看这里. 此方法将扫描主机上所有网络接口上的所有 IP 地址, 以确定最有可能是计算机的 LAN 地址的 IP 地址. + * 如果计算机有多个 IP 地址, 则如果计算机有一个站点本地 IP 地址 (例如 192.168.x.x 或 10.10.x.x, 通常是 IPv4) , + * 则此方法将首选该站点本地 IP 地址 (如果计算机有多个站点本地地址, 则返回第一个站点本地地址) , 但如果计算机没有保存站点本地地址, + * 则此方法将只返回第一个非环回找到地址 (IPv4 或 IPv6) . + * 如果此方法使用此选择算法找不到非环回地址, 则返回调用并返回 JDK 方法的结果 InetAddress + *

+ * 127.xxx.xxx.xxx 属于 loopback 地址, 即只能你自己的本机可见, 就是本机地址, 比较常见的有 127.0.0.1; + * 192.168.xxx.xxx(10.xxx.xxx.xxx、从172.16.xxx.xxx 到 172.31.xxx) 属于 private 私有地址(site local address), 只能在本地局域网可见; + * 169.254.xxx.xxx 属于连接本地地址 (link local IP), 在单独网段可用 + * 224.xxx.xxx.xxx 到 239.xxx.xxx.xxx 属于组播地址 + * 比较特殊的 255.255.255.255 属于广播地址 + * 除此之外的地址就是点对点的可用的公开 IPv4 地址 + * + * @return the local host lan address + * @throws UnknownHostException If the LAN address of the machine cannot be found. + * @since 1.0.0 + */ + private static @Nullable InetAddress getLocalAddress0() { + InetAddress localAddress = null; + + try { + // 遍历所有的网络接口 + Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + if (null == interfaces) { + return localAddress; + } + while (interfaces.hasMoreElements()) { + try { + // 过滤掉 loopback 设备、虚拟网卡和不可用的网卡 + NetworkInterface network = interfaces.nextElement(); + if (network.isLoopback() || network.isVirtual() || !network.isUp() + // 如果是 mac 系统, 不使用 wifi + || (network.getDisplayName().equals("en1") && SystemUtils.IS_OS_MAC_OSX)) { + continue; + } + + // 在所有的接口下再遍历 IP + Enumeration addresses = network.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress inetAddress = addresses.nextElement(); + // 使用局域网 ip + if (inetAddress.isSiteLocalAddress()) { + try { + Optional addressOp = toValidAddress(inetAddress); + if (addressOp.isPresent()) { + try { + if (addressOp.get().isReachable(100)) { + return addressOp.get(); + } + } catch (IOException ignored) { + } + } + } catch (Throwable e) { + log.warn("", e); + } + } + } + } catch (Throwable e) { + log.warn("", e); + } + } + } catch (Throwable e) { + log.warn("", e); + } + return localAddress; + } + + /** + * Gets host name * + * + * @param address address + * @return the host name + * @since 1.5.0 + */ + public static String getHostName(String address) { + int i = address.indexOf(':'); + if (i > -1) { + address = address.substring(0, i); + } + String hostname = HOST_NAME_CACHE.get(address); + if (hostname != null && hostname.length() > 0) { + return hostname; + } + String finalAddress = address; + InetAddress inetAddress = getInetAddressWithTimeout(finalAddress); + + if (inetAddress != null) { + hostname = inetAddress.getHostName(); + HOST_NAME_CACHE.put(address, hostname); + return hostname; + } + + return address; + } + + /** + * Gets ip by host * + * + * @param hostName host name + * @return ip address or hostName if UnknownHostException + * @since 1.5.0 + */ + public static String getIpByHost(String hostName) { + InetAddress inetAddress = getInetAddressWithTimeout(hostName); + return inetAddress != null ? inetAddress.getHostAddress() : hostName; + } + + /** + * {@link InetAddress#getByName(String)} 有可能会走 DNS (java.net.InetAddress#getAddressesFromNameService) 查询, + * 如果 DNS 服务挂掉或长时间未返回数据, 将导致应用阻塞, 因此添加一个超时逻辑(1 秒). + * + * @param hostName host name + * @return the inet address with timeout + * @since 1.7.1 + */ + public static InetAddress getInetAddressWithTimeout(String hostName) { + return getInetAddressWithTimeout(hostName, 1000); + } + + /** + * Gets inet address with timeout * + * + * @param hostName host name + * @param timeout timeout + * @return the inet address with timeout + * @since 1.7.1 + */ + public static InetAddress getInetAddressWithTimeout(String hostName, long timeout) { + try { + return TimeoutUtils.process(new Callable() { + @Override + public InetAddress call() throws Exception { + return InetAddress.getByName(hostName); + } + }, timeout); + } catch (Exception e) { + } + return null; + } + + /** + * To address string + * + * @param address address + * @return the string + * @since 1.5.0 + */ + public static @NotNull String toAddressString(@NotNull InetSocketAddress address) { + return address.getAddress().getHostAddress() + ":" + address.getPort(); + } + + /** + * To address + * + * @param address address + * @return the inet socket address + * @since 1.5.0 + */ + @Contract("_ -> new") + public static @NotNull InetSocketAddress toAddress(@NotNull String address) { + int i = address.indexOf(':'); + String host; + int port; + if (i > -1) { + host = address.substring(0, i); + port = Integer.parseInt(address.substring(i + 1)); + } else { + host = address; + port = 0; + } + return new InetSocketAddress(host, port); + } + + /** + * To url + * + * @param protocol protocol + * @param host host + * @param port port + * @param path path + * @return the string + * @since 1.5.0 + */ + public static @NotNull String toURL(String protocol, String host, int port, @NotNull String path) { + StringBuilder sb = new StringBuilder(); + sb.append(protocol).append("://"); + sb.append(host).append(':').append(port); + if (path.charAt(0) != '/') { + sb.append('/'); + } + sb.append(path); + return sb.toString(); + } + + /** + * Join multicast group + * + * @param multicastSocket multicast socket + * @param multicastAddress multicast address + * @throws IOException io exception + * @since 1.5.0 + */ + public static void joinMulticastGroup(MulticastSocket multicastSocket, InetAddress multicastAddress) throws IOException { + setInterface(multicastSocket, multicastAddress instanceof Inet6Address); + multicastSocket.setLoopbackMode(false); + multicastSocket.joinGroup(multicastAddress); + } + + /** + * Sets interface * + * + * @param multicastSocket multicast socket + * @param preferIpv6 prefer ipv 6 + * @throws IOException io exception + * @since 1.5.0 + */ + public static void setInterface(MulticastSocket multicastSocket, boolean preferIpv6) throws IOException { + boolean interfaceSet = false; + Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + NetworkInterface i = interfaces.nextElement(); + Enumeration addresses = i.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress address = addresses.nextElement(); + if (preferIpv6 && address instanceof Inet6Address) { + try { + if (address.isReachable(100)) { + multicastSocket.setInterface(address); + interfaceSet = true; + break; + } + } catch (IOException ignored) { + } + } else if (!preferIpv6 && address instanceof Inet4Address) { + try { + if (address.isReachable(100)) { + multicastSocket.setInterface(address); + interfaceSet = true; + break; + } + } catch (IOException ignored) { + } + } + } + if (interfaceSet) { + break; + } + } + } + + /** + * Match ip expression + * + * @param pattern pattern + * @param host host + * @param port port + * @return the boolean + * @throws UnknownHostException unknown host exception + * @since 1.5.0 + */ + public static boolean matchIpExpression(@NotNull String pattern, String host, int port) throws UnknownHostException { + + // if the pattern is subnet format, it will not be allowed to config port param in pattern. + if (pattern.contains("/")) { + CIDRUtils utils = new CIDRUtils(pattern); + return utils.isInRange(host); + } + + return matchIpRange(pattern, host, port); + } + + /** + * Match ip range + * + * @param pattern pattern + * @param host host + * @param port port + * @return boolean boolean + * @throws UnknownHostException unknown host exception + * @since 1.5.0 + */ + @SneakyThrows + @Contract("null, _, _ -> fail; !null, null, _ -> fail") + public static boolean matchIpRange(String pattern, String host, int port) throws UnknownHostException { + if (pattern == null || host == null) { + throw new IllegalArgumentException("Illegal Argument pattern or hostName. Pattern:" + pattern + ", Host:" + host); + } + pattern = pattern.trim(); + if ("*.*.*.*".equals(pattern) || "*".equals(pattern)) { + return true; + } + + String finalHost = host; + InetAddress inetAddress = getInetAddressWithTimeout(finalHost); + + if (inetAddress == null) { + return false; + } + + boolean isIpv4 = isValidV4Address(inetAddress); + String[] hostAndPort = getPatternHostAndPort(pattern, isIpv4); + if (hostAndPort[1] != null && !hostAndPort[1].equals(String.valueOf(port))) { + return false; + } + pattern = hostAndPort[0]; + + String splitCharacter = SPLIT_IPV4_CHARECTER; + if (!isIpv4) { + splitCharacter = SPLIT_IPV6_CHARECTER; + } + String[] mask = pattern.split(splitCharacter); + //check format of pattern + checkHostPattern(pattern, mask, isIpv4); + + host = inetAddress.getHostAddress(); + + String[] ipAddress = host.split(splitCharacter); + if (pattern.equals(host)) { + return true; + } + // short name condition + if (!ipPatternContainExpression(pattern)) { + String finalPattern = pattern; + InetAddress patternAddress = getInetAddressWithTimeout(finalPattern); + return inetAddress == null ? false : patternAddress.getHostAddress().equals(host); + } + for (int i = 0; i < mask.length; i++) { + if ("*".equals(mask[i]) || mask[i].equals(ipAddress[i])) { + continue; + } else if (mask[i].contains("-")) { + String[] rangeNumStrs = mask[i].split("-"); + if (rangeNumStrs.length != 2) { + throw new IllegalArgumentException("There is wrong format of ip Address: " + mask[i]); + } + Integer min = getNumOfIpSegment(rangeNumStrs[0], isIpv4); + Integer max = getNumOfIpSegment(rangeNumStrs[1], isIpv4); + Integer ip = getNumOfIpSegment(ipAddress[i], isIpv4); + if (ip < min || ip > max) { + return false; + } + } else if ("0".equals(ipAddress[i]) + && ("0".equals(mask[i]) || "00".equals(mask[i]) || "000".equals(mask[i]) || "0000".equals(mask[i]))) { + continue; + } else if (!mask[i].equals(ipAddress[i])) { + return false; + } + } + return true; + } + + /** + * Ip pattern contain expression + * + * @param pattern pattern + * @return the boolean + * @since 1.5.0 + */ + private static boolean ipPatternContainExpression(@NotNull String pattern) { + return pattern.contains("*") || pattern.contains("-"); + } + + /** + * Check host pattern + * + * @param pattern pattern + * @param mask mask + * @param isIpv4 is ipv 4 + * @since 1.5.0 + */ + private static void checkHostPattern(String pattern, String[] mask, boolean isIpv4) { + if (!isIpv4) { + if (mask.length != 8 && ipPatternContainExpression(pattern)) { + throw new IllegalArgumentException("If you config ip expression that contains '*' or '-', " + + "please fill qulified ip pattern like 234e:0:4567:0:0:0:3d:*. "); + } + if (mask.length != 8 && !pattern.contains("::")) { + throw new IllegalArgumentException("The host is ipv6, but the pattern is not ipv6 pattern : " + pattern); + } + } else { + if (mask.length != 4) { + throw new IllegalArgumentException("The host is ipv4, but the pattern is not ipv4 pattern : " + pattern); + } + } + } + + /** + * Get pattern host and port + * + * @param pattern pattern + * @param isIpv4 is ipv 4 + * @return the string [ ] + * @since 1.5.0 + */ + private static String @NotNull [] getPatternHostAndPort(@NotNull String pattern, boolean isIpv4) { + String[] result = new String[2]; + if (pattern.startsWith("[") && pattern.contains("]:")) { + int end = pattern.indexOf("]:"); + result[0] = pattern.substring(1, end); + result[1] = pattern.substring(end + 2); + return result; + } else if (pattern.startsWith("[") && pattern.endsWith("]")) { + result[0] = pattern.substring(1, pattern.length() - 1); + result[1] = null; + return result; + } else if (isIpv4 && pattern.contains(":")) { + int end = pattern.indexOf(":"); + result[0] = pattern.substring(0, end); + result[1] = pattern.substring(end + 1); + return result; + } else { + result[0] = pattern; + return result; + } + } + + /** + * Gets num of ip segment * + * + * @param ipSegment ip segment + * @param isIpv4 is ipv 4 + * @return the num of ip segment + * @since 1.5.0 + */ + private static @NotNull Integer getNumOfIpSegment(String ipSegment, boolean isIpv4) { + if (isIpv4) { + return Integer.parseInt(ipSegment); + } + return Integer.parseInt(ipSegment, 16); + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.0.0 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2022.02.11 15:27 + * @since 2022.1.1 + */ + private static class InetUtils implements Closeable { + /** + * Executor service + */ + private final ExecutorService executorService; + /** + * Properties + */ + private final InetUtilsProperties properties; + + /** + * Inet utils + * + * @param properties properties + * @since 2022.1.1 + */ + InetUtils(final InetUtilsProperties properties) { + this.properties = properties; + this.executorService = Executors.newSingleThreadExecutor(r -> { + Thread thread = new Thread(r); + thread.setName("INetUtils-Thread"); + thread.setDaemon(true); + return thread; + }); + } + + /** + * Close + * + * @since 2022.1.1 + */ + @Override + public void close() { + this.executorService.shutdown(); + } + + /** + * Find first non loopback host info + * + * @return the host info + * @since 2022.1.1 + */ + HostInfo findFirstNonLoopbackHostInfo() { + InetAddress address = findFirstNonLoopbackAddress(); + if (address != null) { + return convertAddress(address); + } + HostInfo hostInfo = new HostInfo(); + hostInfo.setHostname(this.properties.getDefaultHostname()); + hostInfo.setIpAddress(this.properties.getDefaultIpAddress()); + return hostInfo; + } + + /** + * Find first non loopback address + * + * @return the inet address + * @since 2022.1.1 + */ + InetAddress findFirstNonLoopbackAddress() { + InetAddress result = null; + try { + int lowest = Integer.MAX_VALUE; + for (Enumeration nics = NetworkInterface + .getNetworkInterfaces(); nics.hasMoreElements(); ) { + NetworkInterface ifc = nics.nextElement(); + if (ifc.isUp()) { + log.trace("Testing interface: " + ifc.getDisplayName()); + if (ifc.getIndex() < lowest || result == null) { + lowest = ifc.getIndex(); + } else if (result != null) { + continue; + } + + // @formatter:off + if (!ignoreInterface(ifc.getDisplayName())) { + for (Enumeration addrs = ifc + .getInetAddresses(); addrs.hasMoreElements(); ) { + InetAddress address = addrs.nextElement(); + if (address instanceof Inet4Address + && !address.isLoopbackAddress() + && isPreferredAddress(address)) { + log.trace("Found non-loopback interface: " + + ifc.getDisplayName()); + result = address; + } + } + } + // @formatter:on + } + } + } catch (IOException ex) { + log.error("Cannot get first non-loopback address", ex); + } + + if (result != null) { + return result; + } + + try { + return InetAddress.getLocalHost(); + } catch (UnknownHostException e) { + log.warn("Unable to retrieve localhost"); + } + + return null; + } + + /** + * Is preferred address + * + * @param address address + * @return the boolean + * @since 2022.1.1 + */ + boolean isPreferredAddress(InetAddress address) { + + if (this.properties.isUseOnlySiteLocalInterfaces()) { + final boolean siteLocalAddress = address.isSiteLocalAddress(); + if (!siteLocalAddress) { + log.trace("Ignoring address: " + address.getHostAddress()); + } + return siteLocalAddress; + } + final List preferredNetworks = this.properties.getPreferredNetworks(); + if (preferredNetworks.isEmpty()) { + return true; + } + for (String regex : preferredNetworks) { + final String hostAddress = address.getHostAddress(); + if (hostAddress.matches(regex) || hostAddress.startsWith(regex)) { + return true; + } + } + log.trace("Ignoring address: " + address.getHostAddress()); + return false; + } + + /** + * Ignore interface + * + * @param interfaceName interface name + * @return the boolean + * @since 2022.1.1 + */ + boolean ignoreInterface(String interfaceName) { + for (String regex : this.properties.getIgnoredInterfaces()) { + if (interfaceName.matches(regex)) { + log.trace("Ignoring interface: " + interfaceName); + return true; + } + } + return false; + } + + /** + * Convert address + * + * @param address address + * @return the host info + * @since 2022.1.1 + */ + HostInfo convertAddress(final InetAddress address) { + HostInfo hostInfo = new HostInfo(); + Future result = this.executorService.submit(address::getHostName); + + String hostname; + try { + hostname = result.get(this.properties.getTimeoutSeconds(), TimeUnit.SECONDS); + } catch (Exception e) { + log.info("Cannot determine local hostname"); + hostname = "localhost"; + } + hostInfo.setHostname(hostname); + hostInfo.setIpAddress(address.getHostAddress()); + return hostInfo; + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.0.0 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2022.02.11 15:27 + * @since 2022.1.1 + */ + @Data + private static class HostInfo { + /** + * Override + */ + public boolean override; + /** + * Ip address + */ + private String ipAddress; + /** + * Hostname + */ + private String hostname; + + /** + * Gets ip address as int * + * + * @return the ip address as int + * @since 2022.1.1 + */ + public int getIpAddressAsInt() { + InetAddress inetAddress = null; + String host = this.ipAddress; + if (host == null) { + host = this.hostname; + } + try { + inetAddress = InetAddress.getByName(host); + } catch (final UnknownHostException e) { + throw new IllegalArgumentException(e); + } + return ByteBuffer.wrap(inetAddress.getAddress()).getInt(); + } + + } + } +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/InetUtilsProperties.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/InetUtilsProperties.java new file mode 100644 index 0000000..1f00eb0 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/InetUtilsProperties.java @@ -0,0 +1,72 @@ +package com.aivfo.el.starter.core.util; + +import com.aivfo.el.starter.base.utils.StringUtils; +import lombok.Data; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 针对多网卡获取 IP 错误的问题, 使用此类可配置获取 IP 的方式 + */ +@Data +class InetUtilsProperties { + + /** + * LOCALHOST_KEY + */ + public static final String LOCALHOST_KEY = "localhost"; + /** + * LOCALHOST_VALUE + */ + public static final String LOCALHOST_VALUE = "127.0.0.1"; + + /** + * Default hostname + */ + private String defaultHostname = LOCALHOST_KEY; + /** + * Default ip address + */ + private String defaultIpAddress = LOCALHOST_VALUE; + /** + * Timeout seconds + */ + private int timeoutSeconds; + /** + * 是否只使用带有站点本地地址的接口, {@link InetAddress#isSiteLocalAddress()} + */ + private boolean useOnlySiteLocalInterfaces; + /** + * 将被忽略的网络接口的Java正则表达式列表. + */ + private List ignoredInterfaces = new ArrayList<>(); + /** + * 首选网络地址的Java正则表达式列表 + */ + private List preferredNetworks = new ArrayList<>(); + + /** + * Inet utils properties + * + * @since 2022.1.1 + */ + InetUtilsProperties() { + this.timeoutSeconds = 1; + this.useOnlySiteLocalInterfaces = false; + + String ignoredInterfacesStr = ""; + if (StringUtils.isNotBlank(ignoredInterfacesStr)) { + this.ignoredInterfaces = StringUtils.splitTrim(ignoredInterfacesStr, ","); + } + + String preferredNetworksStr = ""; + if (StringUtils.isNotBlank(preferredNetworksStr)) { + this.preferredNetworks = StringUtils.splitTrim(preferredNetworksStr, ","); + } + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/LRUCache.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/LRUCache.java new file mode 100644 index 0000000..3a7fd6d --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/LRUCache.java @@ -0,0 +1,183 @@ +package com.aivfo.el.starter.core.util; + +import java.util.LinkedHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +@SuppressWarnings("all") +public class LRUCache extends LinkedHashMap { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = -5167631809472116969L; + /** + * DEFAULT_LOAD_FACTOR + */ + private static final float DEFAULT_LOAD_FACTOR = 0.75f; + /** + * DEFAULT_MAX_CAPACITY + */ + private static final int DEFAULT_MAX_CAPACITY = 1000; + /** + * Lock + */ + private final Lock lock = new ReentrantLock(); + /** + * Max capacity + */ + private volatile int maxCapacity; + + /** + * Lru cache + * + * @since 1.5.0 + */ + public LRUCache() { + this(DEFAULT_MAX_CAPACITY); + } + + /** + * Lru cache + * + * @param maxCapacity max capacity + * @since 1.5.0 + */ + public LRUCache(int maxCapacity) { + super(16, DEFAULT_LOAD_FACTOR, true); + this.maxCapacity = maxCapacity; + } + + /** + * Remove eldest entry + * + * @param eldest eldest + * @return the boolean + * @since 1.5.0 + */ + @Override + protected boolean removeEldestEntry(java.util.Map.Entry eldest) { + return this.size() > this.maxCapacity; + } + + /** + * Contains key + * + * @param key key + * @return the boolean + * @since 1.5.0 + */ + @Override + public boolean containsKey(Object key) { + this.lock.lock(); + try { + return super.containsKey(key); + } finally { + this.lock.unlock(); + } + } + + /** + * Get + * + * @param key key + * @return the v + * @since 1.5.0 + */ + @Override + public V get(Object key) { + this.lock.lock(); + try { + return super.get(key); + } finally { + this.lock.unlock(); + } + } + + /** + * Put + * + * @param key key + * @param value value + * @return the v + * @since 1.5.0 + */ + @Override + public V put(K key, V value) { + this.lock.lock(); + try { + return super.put(key, value); + } finally { + this.lock.unlock(); + } + } + + /** + * Remove + * + * @param key key + * @return the v + * @since 1.5.0 + */ + @Override + public V remove(Object key) { + this.lock.lock(); + try { + return super.remove(key); + } finally { + this.lock.unlock(); + } + } + + /** + * Size + * + * @return the int + * @since 1.5.0 + */ + @Override + public int size() { + this.lock.lock(); + try { + return super.size(); + } finally { + this.lock.unlock(); + } + } + + /** + * Clear + * + * @since 1.5.0 + */ + @Override + public void clear() { + this.lock.lock(); + try { + super.clear(); + } finally { + this.lock.unlock(); + } + } + + /** + * Gets max capacity * + * + * @return the max capacity + * @since 1.5.0 + */ + public int getMaxCapacity() { + return this.maxCapacity; + } + + /** + * Sets max capacity * + * + * @param maxCapacity max capacity + * @since 1.5.0 + */ + public void setMaxCapacity(int maxCapacity) { + this.maxCapacity = maxCapacity; + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/NetUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/NetUtils.java new file mode 100644 index 0000000..783077d --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/NetUtils.java @@ -0,0 +1,139 @@ +package com.aivfo.el.starter.core.util; + +import com.aivfo.el.starter.base.utils.StringPool; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.util.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.UnknownHostException; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: INet 相关工具 + */ +@Slf4j +@UtilityClass +public class NetUtils extends INetUtils { + /** + * LOCAL_HOST + */ + public static final String LOCAL_HOST = INetUtils.LOCALHOST_VALUE; + /** + * MIN_PORT_NUMBER + */ + public static final int MIN_PORT_NUMBER = INetUtils.MIN_PORT; + /** + * MAX_PORT_NUMBER + */ + public static final int MAX_PORT_NUMBER = INetUtils.MAX_PORT; + + /** + * Gets local ip addr. + * + * @return the local ip addr + * @since 1.0.0 + * @deprecated use {@link NetUtils#getLocalHost()} + */ + @Nullable + @Deprecated + public static String getLocalIpAddr() { + return getLocalHost(); + } + + /** + * 获取 服务器 hostname + * + * @return hostname host name + * @since 1.0.0 + * @deprecated use {@link NetUtils#getLocalAddress()} + */ + @Deprecated + public static String getHostName() { + InetAddress localAddress = getLocalAddress(); + if (localAddress == null) { + return LOCALHOST_KEY; + } + return localAddress.getHostName(); + } + + /** + * 尝试端口时候被占用 + * + * @param port 端口号 + * @return boolean 没有被占用: true, 被占用: false + * @since 1.0.0 + * @deprecated use {@link NetUtils#available(int)} + */ + @Deprecated + public static boolean tryPort(int port) { + return available(port); + } + + /** + * 检查本机 TCP/UDP 端口是否可用, 可用返回 true, 否则返回 false + * + * @param port port + * @return the boolean + * @since 1.5.0 + */ + public static boolean available(int port) { + if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) { + throw new IllegalArgumentException("Invalid start port: " + port); + } + try (ServerSocket ss = new ServerSocket(port); + DatagramSocket ds = new DatagramSocket(port)) { + ss.setReuseAddress(true); + ds.setReuseAddress(true); + return true; + } catch (IOException ignored) { + return false; + } + } + + /** + * 获取请求方 ip + * + * @param request HttpServletRequest + * @return ip string + * @since 1.0.0 + */ + @SuppressWarnings("PMD.UndefineMagicConstantRule") + public static String ip(@NotNull HttpServletRequest request) { + String ipAddress; + ipAddress = request.getHeader("x-forwarded-for"); + if (StringUtils.isEmpty(ipAddress)) { + ipAddress = request.getHeader("Proxy-Client-IP"); + } + if (StringUtils.isEmpty(ipAddress)) { + ipAddress = request.getRemoteAddr(); + if (LOCAL_HOST.equals(ipAddress)) { + // 根据网卡取本机配置的IP + InetAddress inetAddress = null; + try { + inetAddress = InetAddress.getLocalHost(); + } catch (UnknownHostException ignored) { + } + ipAddress = inetAddress != null ? inetAddress.getHostAddress() : null; + } + } + // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 + if (ipAddress != null && ipAddress.length() > 15) { + if (ipAddress.indexOf(StringPool.COMMA) > 0) { + ipAddress = ipAddress.substring(0, ipAddress.indexOf(StringPool.COMMA)); + } + } + if (StringUtils.isEmpty(ipAddress)) { + return "NONE"; + } + return ipAddress; + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/NumberUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/NumberUtils.java new file mode 100644 index 0000000..0c2f155 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/NumberUtils.java @@ -0,0 +1,215 @@ +package com.aivfo.el.starter.core.util; + +import cn.hutool.core.util.NumberUtil; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.lang.Nullable; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 数字类型工具类 + */ +@UtilityClass +public class NumberUtils extends org.springframework.util.NumberUtils { + + /** + * All possible chars for representing a number as a String + */ + public static final char[] DIGITS = { + '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z' + }; + + /** + *

Convert a String to an int, returning + * zero if the conversion fails.

+ *

If the string is null, zero is returned.

+ *
+     *   NumberUtil.toInt(null) = 0
+     *   NumberUtil.toInt("")   = 0
+     *   NumberUtil.toInt("1")  = 1
+     * 
+ * + * @param str the string to convert, may be null + * @return the int represented by the string, or zero if conversion fails + * @since 1.0.0 + */ + public static int toInt(String str) { + return toInt(str, -1); + } + + /** + * Is numer boolean + * + * @param str str + * @return the boolean + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isNumer(String str) { + return NumberUtil.isNumber(str); + } + + /** + *

Convert a String to an int, returning a + * default value if the conversion fails.

+ *

If the string is null, the default value is returned.

+ *
+     *   NumberUtil.toInt(null, 1) = 1
+     *   NumberUtil.toInt("", 1)   = 1
+     *   NumberUtil.toInt("1", 0)  = 1
+     * 
+ * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the int represented by the string, or the default if conversion fails + * @since 1.0.0 + */ + @Contract("null, _ -> param2") + public static int toInt(@Nullable String str, int defaultValue) { + if (str == null) { + return defaultValue; + } + try { + return Integer.parseInt(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + /** + *

Convert a String to a long, returning + * zero if the conversion fails.

+ *

If the string is null, zero is returned.

+ *
+     *   NumberUtil.toLong(null) = 0L
+     *   NumberUtil.toLong("")   = 0L
+     *   NumberUtil.toLong("1")  = 1L
+     * 
+ * + * @param str the string to convert, may be null + * @return the long represented by the string, or 0 if conversion fails + * @since 1.0.0 + */ + public static long toLong(String str) { + return toLong(str, 0L); + } + + /** + *

Convert a String to a long, returning a + * default value if the conversion fails.

+ *

If the string is null, the default value is returned.

+ *
+     *   NumberUtil.toLong(null, 1L) = 1L
+     *   NumberUtil.toLong("", 1L)   = 1L
+     *   NumberUtil.toLong("1", 0L)  = 1L
+     * 
+ * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the long represented by the string, or the default if conversion fails + * @since 1.0.0 + */ + @Contract("null, _ -> param2") + public static long toLong(@Nullable String str, long defaultValue) { + if (str == null) { + return defaultValue; + } + try { + return Long.parseLong(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + /** + *

Convert a String to a Double + * + * @param value value + * @return double value + * @since 1.0.0 + */ + @Contract("!null -> !null") + public static Double toDouble(String value) { + return toDouble(value, null); + } + + /** + *

Convert a String to a Double + * + * @param value value + * @param defaultValue 默认值 + * @return double value + * @since 1.0.0 + */ + @Contract("!null, _ -> !null; null, _ -> param2") + public static Double toDouble(@Nullable String value, Double defaultValue) { + if (value != null) { + return Double.valueOf(value.trim()); + } + return defaultValue; + } + + /** + *

Convert a String to a Double + * + * @param value value + * @return double value + * @since 1.0.0 + */ + @Contract("!null -> !null") + public static Float toFloat(String value) { + return toFloat(value, null); + } + + /** + *

Convert a String to a Double + * + * @param value value + * @param defaultValue 默认值 + * @return double value + * @since 1.0.0 + */ + @Contract("!null, _ -> !null; null, _ -> param2") + public static Float toFloat(@Nullable String value, Float defaultValue) { + if (value != null) { + return Float.valueOf(value.trim()); + } + return defaultValue; + } + + /** + * 将 long 转短字符串 为 62 进制 + * + * @param i 数字 + * @return 短字符串 string + * @since 1.0.0 + */ + @NotNull + @Contract(value = "_ -> new", pure = true) + public static String to62String(long i) { + int radix = DIGITS.length; + char[] buf = new char[65]; + int charPos = 64; + i = -i; + while (i <= -radix) { + buf[charPos--] = DIGITS[(int) (-(i % radix))]; + i = i / radix; + } + buf[charPos] = DIGITS[(int) (-i)]; + + return new String(buf, charPos, (65 - charPos)); + } + +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/UrlUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/UrlUtils.java new file mode 100644 index 0000000..bb3ffc8 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/UrlUtils.java @@ -0,0 +1,153 @@ +package com.aivfo.el.starter.core.util; + +import com.aivfo.el.starter.base.utils.Charsets; +import com.aivfo.el.starter.base.utils.StringPool; +import com.aivfo.el.starter.base.utils.StringUtils; +import com.google.common.base.Joiner; +import com.google.common.collect.Maps; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.util.Map; + +@Slf4j +@UtilityClass +public class UrlUtils extends org.springframework.web.util.UriUtils { + + /** + * Build url params by map + * + * @param map map + * @return the string + * @since 1.7.0 + */ + public static String buildUrlParamsByMap(Map map) { + if (map == null) { + return ""; + } + StringBuilder sb = new StringBuilder(); + for (Map.Entry entry : map.entrySet()) { + sb.append(entry.getKey()).append(StringPool.EQUALS).append(entry.getValue()).append(StringPool.AMPERSAND); + } + String s = sb.toString(); + if (s.endsWith(StringPool.AMPERSAND)) { + s = StringUtils.subBefore(s, StringPool.AMPERSAND); + } + return s; + } + + /** + * Build map by url params + * + * @param param param + * @return the map + * @since 1.7.0 + */ + public static @NotNull Map buildMapByUrlParams(String param) { + Map map = Maps.newHashMap(); + if (StringUtils.isBlank(param)) { + return map; + } + if (param.startsWith(StringPool.QUESTION_MARK)) { + param = StringUtils.subAfter(param, StringPool.QUESTION_MARK); + } + String[] params = param.split(StringPool.AMPERSAND); + for (String s : params) { + String[] p = s.split(StringPool.EQUALS); + if (p.length == 2) { + map.put(p[0], p[1]); + } + } + return map; + } + + /** + * url 编码,同js decodeURIComponent + * + * @param source url + * @param charset 字符集 + * @return 编码后的url string + * @since 1.0.0 + */ + @NotNull + public static String encodeUrl(String source, @NotNull Charset charset) { + return UrlUtils.encode(source, charset.name()); + } + + /** + * url 解码 + * + * @param source url + * @param charset 字符集 + * @return 解码url string + * @since 1.0.0 + */ + @NotNull + public static String decodeUrl(String source, @NotNull Charset charset) { + return UrlUtils.decode(source, charset.name()); + } + + /** + * 获取 url 路径 + * + * @param uriStr 路径 + * @return url路径 path + * @since 1.0.0 + */ + public static String getPath(String uriStr) { + URI uri; + + try { + uri = new URI(uriStr); + } catch (URISyntaxException var3) { + throw new RuntimeException(var3); + } + + return uri.getPath(); + } + + /** + * map 转为 url 参数, 默认需要参数编码 + * + * @param source source + * @return the string + * @since 1.0.0 + */ + @NotNull + public static String asUrlParams(@NotNull Map source) { + return asUrlParams(source, true); + } + + /** + * map 转为 url 参数 + * + * @param source source + * @param urlEncoder url encoder + * @return the string + * @since 1.0.0 + */ + @NotNull + public static String asUrlParams(@NotNull Map source, boolean urlEncoder) { + Map tmp = Maps.newHashMap(); + source.forEach((k, v) -> { + if (k != null) { + try { + if (urlEncoder) { + tmp.put(k, URLEncoder.encode(v, Charsets.UTF_8_NAME)); + } else { + tmp.put(k, v); + } + } catch (UnsupportedEncodingException e) { + log.error("url encode error", e); + } + } + }); + return Joiner.on("&").useForNull("").withKeyValueSeparator("=").join(tmp); + } +} diff --git a/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/WebUtils.java b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/WebUtils.java new file mode 100644 index 0000000..733c687 --- /dev/null +++ b/band/aivfo-element/aivfo-element-base/src/main/java/com/aivfo/el/starter/core/util/WebUtils.java @@ -0,0 +1,840 @@ +package com.aivfo.el.starter.core.util; + +import com.aivfo.el.starter.base.asserts.Assertions; +import com.aivfo.el.starter.base.contex.AgentRequestContextHolder; +import com.aivfo.el.starter.base.utils.*; +import com.google.common.collect.Maps; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.util.ContentCachingRequestWrapper; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 用于处理HTTP请求的工具类 + */ +@Slf4j +@UtilityClass +@SuppressWarnings("checkstyle:MethodLimit") +public class WebUtils extends org.springframework.web.util.WebUtils { + + /** + * The constant USER_AGENT_HEADER. + */ + public static final String USER_AGENT_HEADER = "user-agent"; + + /** + * The constant UN_KNOWN. + */ + public static final String UN_KNOWN = "unknown"; + + /** + * 判断是否ajax请求 + * spring ajax 返回含有 ResponseBody 或者 RestController注解 + * + * @param handlerMethod HandlerMethod + * @return 是否ajax请求 boolean + * @since 1.0.0 + */ + public static boolean isBody(HandlerMethod handlerMethod) { + ResponseBody responseBody = ClassUtils.getAnnotation(handlerMethod, ResponseBody.class); + return responseBody != null; + } + + /** + * 读取cookie + * + * @param name cookie name + * @return cookie value + * @since 1.0.0 + */ + public static String getCookieVal(String name) { + HttpServletRequest request = WebUtils.getRequest(); + return getCookieVal(request, name); + } + + /** + * 获取 HttpServletRequest + * + * @return {HttpServletRequest} + * @since 1.0.0 + */ + @NotNull + public static HttpServletRequest getRequest() { + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if (requestAttributes == null) { + requestAttributes = AgentRequestContextHolder.getRequestAttributes(); + } + Assertions.notNull(requestAttributes, "当前线程中不存在 RequestAttributes"); + return ((ServletRequestAttributes) Objects.requireNonNull(requestAttributes)).getRequest(); + } + + /** + * 读取cookie + * + * @param request HttpServletRequest + * @param name cookie name + * @return cookie value + * @since 1.0.0 + */ + @Nullable + public static String getCookieVal(HttpServletRequest request, String name) { + Cookie cookie = getCookie(request, name); + return cookie != null ? cookie.getValue() : null; + } + + /** + * 获取COOKIE + * + * @param request the request + * @param name the name + * @return the cookie + * @since 1.0.0 + */ + public static @Nullable Cookie getCookie(@NotNull HttpServletRequest request, @NotNull String name) { + Cookie[] cookies = request.getCookies(); + if (cookies == null) { + return null; + } + for (Cookie ck : cookies) { + if (StringUtils.equalsIgnoreCase(name, ck.getName())) { + return ck; + } + } + return null; + } + + /** + * 清除 某个指定的cookie + * + * @param response HttpServletResponse + * @param key cookie key + * @since 1.0.0 + */ + public static void removeCookie(HttpServletResponse response, String key) { + setCookie(response, key, null, 0); + } + + /** + * 设置cookie + * + * @param response HttpServletResponse + * @param name cookie name + * @param value cookie value + * @param maxAgeInSeconds maxage + * @since 1.0.0 + */ + public static void setCookie(@NotNull HttpServletResponse response, String name, @Nullable String value, int maxAgeInSeconds) { + Cookie cookie = new Cookie(name, value); + cookie.setPath(StringPool.SLASH); + cookie.setMaxAge(maxAgeInSeconds); + cookie.setHttpOnly(true); + response.addCookie(cookie); + } + + /** + * 返回json + * + * @param response HttpServletResponse + * @param result 结果对象 + * @since 1.0.0 + */ + public static void renderJson(HttpServletResponse response, Object result) { + renderJson(response, result, MediaType.APPLICATION_JSON_VALUE); + } + + /** + * 返回json + * + * @param response HttpServletResponse + * @param result 结果对象 + * @param contentType contentType + * @since 1.0.0 + */ + public static void renderJson(@NotNull HttpServletResponse response, Object result, String contentType) { + response.setCharacterEncoding(StringPool.UTF_8); + response.setContentType(contentType); + try (PrintWriter out = response.getWriter()) { + out.append(JsonUtils.toJson(result)); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + + /** + * 获取ip + * + * @return {String} + * @since 1.0.0 + */ + public static String getIp() { + return getIp(WebUtils.getRequest()); + } + + /** + * 获取ip + * + * @param request HttpServletRequest + * @return {String} + * @since 1.0.0 + */ + @Nullable + public static String getIp(HttpServletRequest request) { + Assertions.notNull(request, "HttpServletRequest is null"); + String ip = request.getHeader("X-Requested-For"); + if (StringUtils.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Forwarded-For"); + } + if (StringUtils.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (StringUtils.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (StringUtils.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + if (StringUtils.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (StringUtils.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + + return StringUtils.isBlank(ip) ? NetUtils.ip(request) : ip.split(",")[0]; + } + + /** + * 获取请求的 url: http[s]://www.xxx.xx[:port] + * + * @return the string + * @since 1.5.0 + */ + public static @NotNull String getUrl() { + return getUrl(WebUtils.getRequest()); + } + + /** + * Gets url * + * + * @param request request + * @return the url + * @since 1.5.0 + */ + public static @NotNull String getUrl(@NotNull HttpServletRequest request) { + String schemeAndHost = request.getScheme() + "://" + request.getServerName(); + // 默认端口输出 + int defaultPort = 80; + if (request.getServerPort() != defaultPort) { + schemeAndHost += ":" + request.getServerPort(); + } + return schemeAndHost; + } + + /*** + * 获取 request 中 json 字符串的内容 + * + * @param request request + * @return 字符串内容 request param string + * @since 1.0.0 + */ + public static String getRequestParamString(HttpServletRequest request) { + try { + return getRequestStr(request); + } catch (Exception ex) { + return StringPool.EMPTY; + } + } + + /** + * 获取 request 请求内容 + * + * @param request request + * @return String request str + * @throws IOException IOException + * @since 1.0.0 + */ + public static String getRequestStr(@NotNull HttpServletRequest request) { + String queryString = request.getQueryString(); + if (StringUtils.isNotBlank(queryString)) { + return new String(queryString.getBytes(Charsets.ISO_8859_1), Charsets.UTF_8) + .replaceAll("&", StringPool.AMPERSAND) + .replaceAll("%22", "\""); + } + return getRequestStr(request, getRequestBytes(request)); + } + + /** + * 获取 request 请求内容 + * + * @param request request + * @param buffer buffer + * @return String request str + * @throws IOException IOException + * @since 1.0.0 + */ + @SneakyThrows + @NotNull + public static String getRequestStr(@NotNull HttpServletRequest request, byte[] buffer) { + String str = new String(buffer, request.getCharacterEncoding()).trim(); + if (StringUtils.isBlank(str)) { + StringBuilder sb = new StringBuilder(); + Enumeration parameterNames = request.getParameterNames(); + while (parameterNames.hasMoreElements()) { + String key = parameterNames.nextElement(); + String value = request.getParameter(key); + StringUtils.appendBuilder(sb, key, StringPool.EQUALS, value, StringPool.AMPERSAND); + } + str = StringUtils.removeSuffix(sb.toString(), StringPool.AMPERSAND); + } + return str.replaceAll("&", StringPool.AMPERSAND); + } + + /** + * 获取 request 请求的 byte[] 数组 + * + * @param request request + * @return byte[] byte [ ] + * @throws IOException IOException + * @since 1.0.0 + */ + @SneakyThrows + public static byte[] getRequestBytes(@NotNull HttpServletRequest request) { + int contentLength = request.getContentLength(); + if (contentLength < 0) { + return null; + } + byte[] buffer = new byte[contentLength]; + for (int i = 0; i < contentLength; ) { + + int readlen = request.getInputStream().read(buffer, i, contentLength - i); + if (readlen == -1) { + break; + } + i += readlen; + } + return buffer; + } + + /** + * 获取客户端IP地址,此方法用在proxy环境中 + * + * @param req the req + * @return remote addr + * @since 1.0.0 + */ + public static String getRemoteAddr(@NotNull HttpServletRequest req) { + String ip = req.getHeader("X-Forwarded-For"); + if (StringUtils.isNotBlank(ip)) { + String[] ips = StringUtils.split(ip, StringPool.COMMA); + for (String tmpip : Objects.requireNonNull(ips)) { + if (StringUtils.isBlank(tmpip)) { + continue; + } + tmpip = tmpip.trim(); + if (isIpAddr(tmpip) && !tmpip.startsWith("10.") && !tmpip.startsWith("192.168.") + && !NetUtils.LOCAL_HOST.equals(tmpip)) { + return tmpip.trim(); + } + } + } + ip = req.getHeader("x-real-ip"); + if (isIpAddr(ip)) { + return ip; + } + ip = req.getRemoteAddr(); + if (ip.indexOf(CharPool.DOT) == -1) { + ip = NetUtils.LOCAL_HOST; + } + return ip; + } + + /** + * 判断字符串是否是一个IP地址 + * + * @param addr the addr + * @return boolean boolean + * @since 1.0.0 + */ + @SuppressWarnings(value = {"PMD.UndefineMagicConstantRule", "checkstyle:ReturnCount"}) + public static boolean isIpAddr(String addr) { + if (StringUtils.isEmpty(addr)) { + return false; + } + String[] ips = StringUtils.split(addr, StringPool.DOT); + if (Objects.requireNonNull(ips).length != 4) { + return false; + } + try { + int ipa = Integer.parseInt(ips[0]); + int ipb = Integer.parseInt(ips[1]); + int ipc = Integer.parseInt(ips[2]); + int ipd = Integer.parseInt(ips[3]); + return ipa >= 0 && ipa <= 255 && ipb >= 0 && ipb <= 255 && ipc >= 0 + && ipc <= 255 && ipd >= 0 && ipd <= 255; + } catch (Exception e) { + log.error("transformation error", e); + } + return false; + } + + /** + * 判断是否为搜索引擎 + * + * @param req the req + * @return boolean boolean + * @since 1.0.0 + */ + public static boolean isRobot(@NotNull HttpServletRequest req) { + String ua = req.getHeader("user-agent"); + if (StringUtils.isBlank(ua)) { + return false; + } + return ua.contains("Baiduspider") + || ua.contains("Googlebot") + || ua.contains("sogou") + || ua.contains("sina") + || ua.contains("iaskspider") + || ua.contains("ia_archiver") + || ua.contains("Sosospider") + || ua.contains("YoudaoBot") + || ua.contains("yahoo") + || ua.contains("yodao") + || ua.contains("MSNBot") + || ua.contains("Twiceler") + || ua.contains("Sosoimagespider") + || ua.contains("naver.com/robots") + || ua.contains("Nutch") + || ua.contains("spider"); + } + + /** + * 获取COOKIE + * + * @param request the request + * @param name the name + * @return the cookie value + * @since 1.0.0 + */ + @Nullable + public static String getCookieValue(@NotNull HttpServletRequest request, String name) { + Cookie[] cookies = request.getCookies(); + if (cookies == null) { + return null; + } + for (Cookie ck : cookies) { + if (StringUtils.equalsIgnoreCase(name, ck.getName())) { + return ck.getValue(); + } + } + return null; + } + + /** + * 设置COOKIE + * + * @param request the request + * @param response the response + * @param name the name + * @param value the value + * @param maxAge the max age + * @since 1.0.0 + */ + public static void setCookie(HttpServletRequest request, HttpServletResponse response, String name, + String value, int maxAge) { + setCookie(request, response, name, value, maxAge, true); + } + + /** + * 设置COOKIE + * + * @param request the request + * @param response the response + * @param name the name + * @param value the value + * @param maxAge the max age + * @param allSubDomain the all sub domain + * @since 1.0.0 + */ + @SuppressWarnings("checkstyle:ParameterNumber") + public static void setCookie(HttpServletRequest request, + HttpServletResponse response, + String name, + String value, + int maxAge, + boolean allSubDomain) { + Cookie cookie = new Cookie(name, value); + cookie.setMaxAge(maxAge); + if (allSubDomain) { + String serverName = request.getServerName(); + String domain = getDomainOfServerName(serverName); + if (domain != null && domain.indexOf(CharPool.DOT) != -1) { + cookie.setDomain(CharPool.DOT + domain); + } + } + cookie.setPath("/"); + response.addCookie(cookie); + } + + /** + * 获取用户访问URL中的根域名 + * 例如: www.dlog.cn -> dlog.cn + * + * @param host the host + * @return domain of server name + * @since 1.0.0 + */ + @Nullable + @SuppressWarnings(value = {"PMD.UndefineMagicConstantRule", "checkstyle:ReturnCount"}) + public static String getDomainOfServerName(String host) { + if (isIpAddr(host)) { + return null; + } + String[] names = StringUtils.split(host, StringPool.DOT); + int len = Objects.requireNonNull(names).length; + if (len == 1) { + return null; + } + int www = 3; + if (len == www) { + return makeup(names[len - 2], names[len - 1]); + } + if (len > www) { + String dp = names[len - 2]; + if ("com".equalsIgnoreCase(dp) + || "gov".equalsIgnoreCase(dp) + || "net".equalsIgnoreCase(dp) + || "edu".equalsIgnoreCase(dp) + || "org".equalsIgnoreCase(dp)) { + return makeup(names[len - 3], names[len - 2], names[len - 1]); + } else { + return makeup(names[len - 2], names[len - 1]); + } + } + return host; + } + + /** + * Makeup string + * + * @param ps ps + * @return the string + * @since 1.0.0 + */ + @NotNull + private static String makeup(@NotNull String... ps) { + StringBuilder s = new StringBuilder(); + for (int idx = 0; idx < ps.length; idx++) { + if (idx > 0) { + s.append('.'); + } + s.append(ps[idx]); + } + return s.toString(); + } + + /** + * Delete cookie. + * + * @param request the request + * @param response the response + * @param name the name + * @param allSubDomain the all sub domain + * @since 1.0.0 + */ + public static void deleteCookie(HttpServletRequest request, + HttpServletResponse response, String name, boolean allSubDomain) { + setCookie(request, response, name, "", 0, allSubDomain); + } + + /** + * 获取HTTP端口 + * + * @param req the req + * @return http port + * @since 1.0.0 + */ + public static int getHttpPort(@NotNull HttpServletRequest req) { + try { + return new URL(req.getRequestURL().toString()).getPort(); + } catch (MalformedURLException excp) { + return 80; + } + } + + /** + * 获取浏览器提交的整形参数 + * + * @param req the req + * @param param the param + * @param defaultValue the default value + * @return param param + * @since 1.0.0 + */ + public static int getParam(@NotNull HttpServletRequest req, String param, int defaultValue) { + return NumberUtils.toInt(req.getParameter(param), defaultValue); + } + + /** + * 获取浏览器提交的整形参数 + * + * @param req the req + * @param param the param + * @param defaultValue the default value + * @return param param + * @since 1.0.0 + */ + public static long getParam(@NotNull HttpServletRequest req, String param, long defaultValue) { + return NumberUtils.toLong(req.getParameter(param), defaultValue); + } + + /** + * 获取浏览器提交的字符串参数? + * + * @param req the req + * @param param the param + * @param defaultValue the default value + * @return param param + * @since 1.0.0 + */ + public static String getParam(@NotNull HttpServletRequest req, String param, String defaultValue) { + String value = req.getParameter(param); + return (StringUtils.isEmpty(value)) ? defaultValue : value; + } + + /** + * 获取请求参数 + * + * @param request the request + * @return parameters map + * @since 1.0.0 + */ + public static @NotNull Map getParameterMap(@NotNull HttpServletRequest request) { + ContentCachingRequestWrapper cachingRequestWrapper = new ContentCachingRequestWrapper(request); + + Map returnMap = Maps.newHashMapWithExpectedSize(16); + //request.getParameterMap() 返回的是一个Map类型的值,该返回值记录着前端 (如jsp页面) 所提交请求中的请求参数和请求参数值的映射关系. 这个返回值有个特别之处——只能读. + Map parameterMap = cachingRequestWrapper.getParameterMap(); + // Map.Entry是Map声明的一个内部接口,此接口为泛型,定义为Entry. 它表示Map中的一个实体 (一个key-value对) . 接口中有getKey(),getValue方法. + for (Map.Entry en : parameterMap.entrySet()) { + String value = cachingRequestWrapper.getParameter(en.getKey()); + try { + returnMap.put(en.getKey(), URLDecoder.decode(value, Charsets.UTF_8_NAME)); + } catch (Exception e) { + log.error("url decode error", e); + } + } + return returnMap; + } + + /** + * Get header map. + * + * @param request the request + * @return the map + * @since 1.0.0 + */ + public static @NotNull Map getHeader(@NotNull HttpServletRequest request) { + // 获取所有的消息头名称 + Enumeration headerNames = request.getHeaderNames(); + if (headerNames == null) { + return Collections.emptyMap(); + } + Map headers = Maps.newHashMapWithExpectedSize(16); + // 获取获取的消息头名称,获取对应的值,并输出 + while (headerNames.hasMoreElements()) { + String nextElement = headerNames.nextElement(); + headers.put(nextElement, request.getHeader(nextElement)); + } + return headers; + } + + /** + * Get header string + * + * @param headerName header name + * @return the string + * @since 1.0.0 + */ + public static String getHeader(String headerName) { + Map headers = getHeader(getRequest()); + return headers.get(headerName); + } + + /** + * 是否multipart/form-data or application/octet-stream表单提交方式 + * + * @param request the request + * @return boolean boolean + * @since 1.0.0 + */ + public static boolean isMultipartContent(@NotNull HttpServletRequest request) { + if (!HttpMethod.POST.name().equalsIgnoreCase(request.getMethod())) { + return false; + } + String contentType = request.getContentType(); + if (contentType == null) { + return false; + } + contentType = contentType.toLowerCase(Locale.ENGLISH); + return contentType.startsWith("multipart/") || MediaType.APPLICATION_OCTET_STREAM_VALUE.equals(contentType); + } + + /** + * Build query params map map. + * + * @param request the request + * @return the map + * @since 1.0.0 + */ + public static @NotNull Map buildQueryParamsMap(@NotNull HttpServletRequest request) { + ContentCachingRequestWrapper cachingRequestWrapper = new ContentCachingRequestWrapper(request); + Map params = Maps.newHashMap(); + Enumeration e = cachingRequestWrapper.getParameterNames(); + + StringBuilder tmpbuff = new StringBuilder(); + if (e.hasMoreElements()) { + while (e.hasMoreElements()) { + String name = e.nextElement(); + String[] values = cachingRequestWrapper.getParameterValues(name); + if (values.length == 1) { + if (StringUtils.isNotBlank(values[0])) { + params.put(name, values[0]); + } + } else { + tmpbuff.setLength(0); + for (String value : values) { + if (StringUtils.isNotBlank(value)) { + tmpbuff.append(value.trim()).append(","); + } + } + if (tmpbuff.length() > 0) { + tmpbuff.deleteCharAt(tmpbuff.length() - 1); + params.put(name, tmpbuff.toString()); + } + } + } + } + return params; + } + + /** + * Gets typesafe request map * + * + * @param request request + * @return the typesafe request map + * @since 1.0.0 + */ + private @NotNull Map getTypesafeRequestMap(@NotNull HttpServletRequest request) { + ContentCachingRequestWrapper cachingRequestWrapper = new ContentCachingRequestWrapper(request); + Enumeration requestParamNames = cachingRequestWrapper.getParameterNames(); + Map typesafeRequestMap = Maps.newHashMap(); + + while (requestParamNames.hasMoreElements()) { + String requestParamName = (String) requestParamNames.nextElement(); + String requestParamValue = cachingRequestWrapper.getParameter(requestParamName); + typesafeRequestMap.put(requestParamName, requestParamValue); + } + + return typesafeRequestMap; + } + + /** + * 获取请求 Body, 注意: 调用此方法后还需要二次读取 request时, HttpServletRequest 必须为 CacheRequestWrapper + * + * @param request the request + * @return the string + * @since 1.0.0 + */ + public static String getBody(@NotNull HttpServletRequest request) { + ContentCachingRequestWrapper cachingRequestWrapper = new ContentCachingRequestWrapper(request); + String bodyInfo = ""; + try (InputStream is = cachingRequestWrapper.getInputStream()) { + bodyInfo = IoUtils.toString(is, StandardCharsets.UTF_8); + } catch (IOException ignored) { + } + return StringUtils.replaceBlank(bodyInfo); + } + + /** + * Gets cache input stream * + * + * @param body body + * @return the cache input stream + * @since 1.0.0 + */ + @NotNull + public static ServletInputStream getCacheInputStream(byte[] body) { + ByteArrayInputStream bais = new ByteArrayInputStream(body); + + return new ServletInputStream() { + + @Override + public int read() { + return bais.read(); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + } + }; + } + + /** + * 将 url 参数解析为 map + * + * @param url the url + * @return the query params + * @since 1.5.0 + */ + public static @NotNull Map converterToMap(@NotNull String url) { + try { + Map params = new HashMap<>(16); + for (String param : url.split(StringPool.AMPERSAND)) { + String[] pair = param.split(StringPool.EQUALS); + String key = URLDecoder.decode(pair[0], Charsets.UTF_8_NAME); + String value = StringPool.EMPTY; + if (pair.length > 1) { + value = URLDecoder.decode(pair[1], Charsets.UTF_8_NAME); + } + params.put(key, value); + } + return params; + } catch (UnsupportedEncodingException ex) { + log.error("{}", ex.getMessage()); + } + return Collections.emptyMap(); + } +} diff --git a/band/aivfo-element/aivfo-element-common/pom.xml b/band/aivfo-element/aivfo-element-common/pom.xml new file mode 100644 index 0000000..6660497 --- /dev/null +++ b/band/aivfo-element/aivfo-element-common/pom.xml @@ -0,0 +1,25 @@ + + + + aivfo-element + com.aivfo + 1.0.0-SNAPSHOT + + 4.0.0 + + aivfo-element-common + + + com.aivfo + aivfo-element-core + 1.0.0-SNAPSHOT + + + org.springframework + spring-webmvc + + + + \ No newline at end of file diff --git a/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/enums/ServletCodeEnum.java b/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/enums/ServletCodeEnum.java new file mode 100644 index 0000000..bc20012 --- /dev/null +++ b/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/enums/ServletCodeEnum.java @@ -0,0 +1,172 @@ +package com.aivfo.start.common.enums; + +import com.aivfo.el.start.core.api.IResultCode; +import com.aivfo.el.starter.base.annotation.BusinessLevel; +import com.aivfo.el.starter.base.annotation.ModelSerial; +import com.aivfo.el.starter.base.annotation.SystemLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import javax.servlet.http.HttpServletResponse; + +@Getter +@AllArgsConstructor +@ModelSerial(modelName = "R") +public enum ServletCodeEnum implements IResultCode { + /** + * 方法级的参数验证时候后的异常 + */ + @BusinessLevel + METHOD_ARGUMENT_NOT_VALID(4400, + "请求参数验证失败: [{}]", + HttpServletResponse.SC_BAD_REQUEST, "MethodArgumentNotValidException"), + /** + * Method argument type mismatch exception servlet code enum + */ + @SystemLevel + METHOD_ARGUMENT_TYPE_MISMATCH(4400, + "请求参数格式错误: [{}]", + HttpServletResponse.SC_BAD_REQUEST, "MethodArgumentTypeMismatchException"), + /** + * Missing servlet request part exception servlet code enum + */ + @SystemLevel + MISSING_SERVLET_REQUEST_PART(4400, + "[{}]", + HttpServletResponse.SC_BAD_REQUEST, "MissingServletRequestPartException"), + /** + * Missing path variable exception servlet code enum + */ + @SystemLevel + MISSING_PATH_VARIABLE(4400, + "[{}]", + HttpServletResponse.SC_BAD_REQUEST, "MissingPathVariableException"), + /** + * 参数绑定异常 + */ + @BusinessLevel + BIND_ERROR(4400, + "请求参数绑定错误: [{}]", + HttpServletResponse.SC_BAD_REQUEST, "BindException"), + /** + * Missing servlet request parameter exception servlet code enum + */ + @SystemLevel + MISSING_SERVLET_REQUEST_PARAMETER(4400, + "缺少请求参数: [{}]", + HttpServletResponse.SC_BAD_REQUEST, "MissingServletRequestParameterException"), + /** + * Type mismatch exception servlet code enum + */ + @SystemLevel + TYPE_MISMATCH(4400, "[{}]", + HttpServletResponse.SC_BAD_REQUEST, "TypeMismatchException"), + /** + * Servlet request binding exception servlet code enum + */ + @SystemLevel + SERVLET_REQUEST_BINDING(4400, + "[{}]", + HttpServletResponse.SC_BAD_REQUEST, "ServletRequestBindingException"), + /** + * Missing request header exception servlet code enum + */ + @SystemLevel + MISSING_REQUEST_HEADER(4400, + "缺少必要的 header 参数: [{}]", + HttpServletResponse.SC_BAD_REQUEST, "MissingServletRequestParameterException"), + /** + * Http message not readable exception servlet code enum + */ + @SystemLevel + HTTP_MESSAGE_NOT_READABLE(4400, + "消息不能读取: [{}]", + HttpServletResponse.SC_BAD_REQUEST, "HttpMessageNotReadableException"), + /** + * No handler found exception servlet code enum + */ + @SystemLevel + NO_HANDLER_FOUND(4404, + "404 没找到请求!: [{}]", + HttpServletResponse.SC_NOT_FOUND, "NoHandlerFoundException"), + /** + * No such request handling method exception servlet code enum + */ + @SystemLevel + NO_SUCH_REQUEST_HANDLING_METHOD(4404, + "[{}]", + HttpServletResponse.SC_NOT_FOUND, "NoSuchRequestHandlingMethodException"), + /** + * Http request method not supported exception servlet code enum + */ + @SystemLevel + HTTP_REQUEST_METHOD_NOT_SUPPORTED(4405, + "不支持当前请求方法: [{}]", + HttpServletResponse.SC_METHOD_NOT_ALLOWED, "HttpRequestMethodNotSupportedException"), + /** + * Http media type not acceptable exception servlet code enum + */ + @SystemLevel + HTTP_MEDIA_TYPE_NOT_ACCEPTABLE(4406, + "[{}]", + HttpServletResponse.SC_NOT_ACCEPTABLE, "HttpMediaTypeNotAcceptableException"), + /** + * Http media type not supported exception servlet code enum + */ + @SystemLevel + HTTP_MEDIA_TYPE_NOT_SUPPORTED(4415, + "不支持当前媒体类型: [{}]", + HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, "HttpMediaTypeNotSupportedException"), + /** + * Conversion not supported exception servlet code enum + */ + @SystemLevel + CONVERSION_NOT_SUPPORTED(4500, + "服务器异常: [{}]", + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "ConversionNotSupportedException"), + /** + * Http message not writable exception servlet code enum + */ + @SystemLevel + HTTP_MESSAGE_NOT_WRITABLE(4500, + "服务器异常: [{}]", + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "HttpMessageNotWritableException"), + /** + * Async request timeout exception servlet code enum + */ + @SystemLevel + ASYNC_REQUEST_TIMEOUT(4503, + "异步请求超时: [{}]", + HttpServletResponse.SC_SERVICE_UNAVAILABLE, "AsyncRequestTimeoutException"), + /** + * Missing requestheader servlet code enum + */ + @SystemLevel + MISSING_REQUESTHEADER(4504, + "缺少必要的 header 参数: [{}]", + HttpServletResponse.SC_BAD_REQUEST, "MissingRequestHeaderException"), + + /** + * Inner error servlet code enum + */ + @SystemLevel + INNER_ERROR(5000, "内部错误", HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "InnerErrorException"); + + /** + * 返回码,目前与 {@link #statusCode} 相同 + */ + public final Integer code; + /** + * 返回信息,直接读取异常的 message + */ + public final String message; + /** + * HTTP 状态码 + */ + public final int statusCode; + /** + * 异常名 + */ + public final String exceptionName; +} diff --git a/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/ExceptionInfo.java b/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/ExceptionInfo.java new file mode 100644 index 0000000..521135c --- /dev/null +++ b/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/ExceptionInfo.java @@ -0,0 +1,61 @@ +package com.aivfo.start.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 开发环境时输出更多的异常信息 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ExceptionInfo implements Serializable { + /** + * serialVersionUID + */ + private static final long serialVersionUID = -5072425562316472427L; + /** + * 请求路径 + */ + private String path; + /** + * 请求参数 + */ + private Object params; + /** + * 请求方式 + */ + private String method; + /** + * 请求方地址 + */ + private String remoteAddr; + /** + * header + */ + private Object headers; + /** + * 追踪 id + */ + private String traceId; + /** + * 异常类 + */ + private String exceptionClass; + /** + * 错误信息 + */ + private String errorMessage; + /** + * 异常堆栈 + */ + private String stackTrace; + +} diff --git a/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/GlobalExceptionHandler.java b/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..eb718bf --- /dev/null +++ b/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/GlobalExceptionHandler.java @@ -0,0 +1,274 @@ +package com.aivfo.start.common.exception; + +import com.aivfo.el.start.core.api.BaseCodes; +import com.aivfo.el.start.core.api.R; +import com.aivfo.el.start.core.util.ResultCodeUtils; +import com.aivfo.el.starter.base.Result; +import com.aivfo.el.starter.base.constant.BasicConstant; +import com.aivfo.el.starter.base.contex.Trace; +import com.aivfo.el.starter.base.exception.BasicException; +import com.aivfo.el.starter.base.utils.Exceptions; +import com.aivfo.el.starter.base.utils.StringUtils; +import com.aivfo.el.starter.core.util.EnumUtils; +import com.aivfo.el.starter.core.util.WebUtils; +import com.aivfo.start.common.enums.ServletCodeEnum; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.ConversionNotSupportedException; +import org.springframework.http.HttpStatus; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; +import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; +import org.springframework.web.HttpMediaTypeNotAcceptableException; +import org.springframework.web.HttpMediaTypeNotSupportedException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.*; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.client.RestClientException; +import org.springframework.web.context.request.async.AsyncRequestTimeoutException; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.springframework.web.multipart.support.MissingServletRequestPartException; +import org.springframework.web.servlet.NoHandlerFoundException; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.ConstraintViolationException; + + +@Slf4j +public class GlobalExceptionHandler { + + /** + * Build result result + * + * @param e e + * @param request request + * @return the result + * @since 1.0.0 + */ + @NotNull + private Result buildResult(BasicException e, @NotNull HttpServletRequest request) { + Result failed = R.failed(e.getCode(), e.getMessage()); + failed.setExtend(this.buildExceptionData(e, request)); + return failed; + } + + /** + * Build exception data exception info + * + * @param throwable throwable + * @param request request + * @return the exception info + * @since 1.0.0 + */ + @NotNull + @SuppressWarnings("checkstyle:Regexp") + private ExceptionInfo buildExceptionData(@NotNull Throwable throwable, @NotNull HttpServletRequest request) { + ExceptionInfo exceptionEntity = new ExceptionInfo(); + exceptionEntity.setPath(request.getRequestURI()); + exceptionEntity.setParams(WebUtils.getRequestParamString(request)); + exceptionEntity.setMethod(request.getMethod()); + exceptionEntity.setRemoteAddr(request.getRemoteAddr()); + exceptionEntity.setHeaders(WebUtils.getHeader(request)); + exceptionEntity.setExceptionClass(throwable.getClass().getName()); + exceptionEntity.setTraceId(Trace.context().get()); + exceptionEntity.setErrorMessage(throwable.getMessage()); + exceptionEntity.setStackTrace(Exceptions.getStackTraceAsString(throwable)); + return exceptionEntity; + } + + /** + * 自定义异常 + * + * @param e 异常 + * @param request request + * @return 异常结果 result + * @since 1.0.0 + */ + @ExceptionHandler(value = { + BasicException.class + }) + public Result handleBasicException(@NotNull BasicException e, @NotNull HttpServletRequest request) { + log.error("{} path: {}", e.getMessage(), request.getRequestURI()); + return this.buildResult(e, request); + } + + /** + * 如果是正式环境, 将服务器异常转换为对用户友好信息, 不暴露具体异常信息, 而是通过日志输出 + * 非正式则输出正确异常信息 + * + * @param e 异常 + * @param request request + * @return 异常结果 result + * @since 1.0.0 + */ + @ExceptionHandler(value = { + NoHandlerFoundException.class, + HttpRequestMethodNotSupportedException.class, + HttpMediaTypeNotSupportedException.class, + HttpMediaTypeNotAcceptableException.class, + MissingPathVariableException.class, + MissingRequestHeaderException.class, + MissingServletRequestParameterException.class, + HttpMessageNotReadableException.class, + HttpMessageNotWritableException.class, + MethodArgumentTypeMismatchException.class, + ServletRequestBindingException.class, + ConversionNotSupportedException.class, + MissingServletRequestPartException.class, + AsyncRequestTimeoutException.class + }) + @SuppressWarnings("all") + public Result handleServletException(@NotNull Exception e, HttpServletRequest request) { + log.error(e.getMessage()); + ExceptionInfo exceptionEntity = this.buildExceptionData(e, request); + try { + String message = e.getMessage(); + if (e instanceof MissingServletRequestParameterException) { + MissingServletRequestParameterException exception = (MissingServletRequestParameterException) e; + message = exception.getParameterName() + + " (" + + exception.getParameterType() + + ")"; + } + ServletCodeEnum servletExceptionEnum = EnumUtils.of(ServletCodeEnum.class, + servletCodeEnum -> e.getClass().getSimpleName() + .equals(servletCodeEnum.exceptionName)) + .orElse(ServletCodeEnum.INNER_ERROR); + if (servletExceptionEnum == ServletCodeEnum.INNER_ERROR) { + log.error(e.getMessage(), e); + } + final Result result = R.failed(ResultCodeUtils.generateCode(servletExceptionEnum), + StringUtils.format(servletExceptionEnum.getMessage(), message)); + result.setExtend(exceptionEntity); + return result; + } catch (IllegalArgumentException ignored) { + } + + final Result failed = R.failed(ResultCodeUtils.generateCode(BaseCodes.FAILURE), e.getMessage()); + failed.setExtend(exceptionEntity); + return failed; + } + + /** + * 参数绑定异常 + * + * @param e 异常 + * @return 异常结果 result + * @since 1.0.0 + */ + @ExceptionHandler(value = { + BindException.class, + }) + public Result handleBindException(@NotNull BindException e) { + log.debug("exception from BindException"); + return this.wrapperBindingResult(e.getBindingResult()); + } + + /** + * 包装绑定异常结果, 输出全部绑定异常信息 + * + * @param bindingResult 绑定结果 + * @return 异常结果 result + * @since 1.0.0 + */ + @NotNull + private Result wrapperBindingResult(@NotNull BindingResult bindingResult) { + StringBuilder warnMessage = new StringBuilder(); + String message = ""; + for (ObjectError error : bindingResult.getAllErrors()) { + if (StringUtils.isBlank(message)) { + message = bindingResult.getAllErrors().get(0).getDefaultMessage(); + } + warnMessage.append(", "); + if (error instanceof FieldError) { + warnMessage.append(((FieldError) error).getField()).append(": "); + } + warnMessage.append(error.getDefaultMessage() == null ? "" : error.getDefaultMessage()); + + } + log.warn("参数绑定校验异常: {} params: {}", warnMessage.substring(2), bindingResult.getTarget()); + return R.failed(ResultCodeUtils.generateCode(BaseCodes.PARAM_VERIFY_ERROR), message); + } + + /** + * 处理 @Validated (spring) 验证异常,将校验失败的所有异常组合成一条错误信息 + * + * @param e 异常 + * @return 异常结果 result + * @since 1.0.0 + */ + @ExceptionHandler(value = { + MethodArgumentNotValidException.class + }) + public Result handleValidException(@NotNull MethodArgumentNotValidException e) { + log.debug("exception from MethodArgumentNotValidException"); + return this.wrapperBindingResult(e.getBindingResult()); + } + + /** + * 处理 @Valid (javax.validation [api], hibernate-validator [impl]) 验证异常 + * + * @param e the e + * @return the result + * @since 1.0.0 + */ + @ExceptionHandler(value = { + ConstraintViolationException.class + }) + public Result handleValidException(@NotNull ConstraintViolationException e) { + log.warn("参数校验失败: [{}]", e.getMessage()); + return R.failed(ResultCodeUtils.generateCode(BaseCodes.PARAM_VERIFY_ERROR), e.getMessage()); + } + + /** + * Handle resource access exception + * + * @param e e + * @return the result + * @since 1.5.0 + */ + @ExceptionHandler(value = { + RestClientException.class + }) + public Result handleResourceAccessException(@NotNull RestClientException e) { + log.error("Agent Service 不可用", e); + return R.failed(ResultCodeUtils.generateCode(BaseCodes.AGENT_DISABLE_EXCEPTION), BaseCodes.AGENT_DISABLE_EXCEPTION.getMessage()); + } + + /** + * 未定义异常 + * + * @param e 异常 + * @param request request + * @return 异常结果 result + * @since 1.0.0 + */ + @ExceptionHandler(value = { + Exception.class + }) + public Result handleException(Exception e, HttpServletRequest request) { + return this.handleError(e, request); + } + + /** + * 捕获 error 级异常 + * + * @param e the e + * @param request request + * @return the result + * @since 1.0.0 + */ + @ExceptionHandler(Throwable.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public Result handleError(Throwable e, HttpServletRequest request) { + Result failed = R.failed(ResultCodeUtils.generateCode(BaseCodes.SERVER_INNER_ERROR), e.getMessage()); + failed.setExtend(this.buildExceptionData(e, request)); + request.setAttribute(BasicConstant.REQUEST_EXCEPTION_INFO_ATTR, e); + return failed; + } + +} diff --git a/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/InstanceException.java b/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/InstanceException.java new file mode 100644 index 0000000..f284469 --- /dev/null +++ b/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/InstanceException.java @@ -0,0 +1,62 @@ +package com.aivfo.start.common.exception; + +import com.aivfo.el.start.core.exception.BaseException; + +public class InstanceException extends BaseException { + /** + * serialVersionUID + */ + private static final long serialVersionUID = -5426025146601282119L; + + /** + * Instantiates a new Instance exception. + * + * @since 1.0.0 + */ + public InstanceException() { + } + + /** + * Instantiates a new Instance exception. + * + * @param message the message + * @since 1.0.0 + */ + public InstanceException(String message) { + super(message); + } + + /** + * Instantiates a new Instance exception. + * + * @param cause the cause + * @since 1.0.0 + */ + public InstanceException(Throwable cause) { + super(cause); + } + + /** + * Instantiates a new Instance exception. + * + * @param message the message + * @param cause the cause + * @since 1.0.0 + */ + public InstanceException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new Instance exception. + * + * @param message the message + * @param cause the cause + * @param enableSuppression the enable suppression + * @param writableStackTrace the writable stack traceId + * @since 1.0.0 + */ + public InstanceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/PropertiesException.java b/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/PropertiesException.java new file mode 100644 index 0000000..27c9623 --- /dev/null +++ b/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/PropertiesException.java @@ -0,0 +1,31 @@ +package com.aivfo.start.common.exception; + +import com.aivfo.el.start.core.exception.BaseException; + +public class PropertiesException extends BaseException { + /** + * serialVersionUID + */ + private static final long serialVersionUID = -6498727260647427447L; + + /** + * Properties exception + * + * @param msg msg + * @param args args + * @since 1.0.0 + */ + public PropertiesException(String msg, Object... args) { + super(msg, args); + } + + /** + * Properties exception + * + * @param cause cause + * @since 1.0.0 + */ + public PropertiesException(Throwable cause) { + super(cause); + } +} diff --git a/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/StarterException.java b/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/StarterException.java new file mode 100644 index 0000000..b89498b --- /dev/null +++ b/band/aivfo-element/aivfo-element-common/src/main/java/com/aivfo/start/common/exception/StarterException.java @@ -0,0 +1,76 @@ +package com.aivfo.start.common.exception; + +import com.aivfo.el.start.core.api.BaseCodes; +import com.aivfo.el.start.core.exception.BaseException; + +public class StarterException extends BaseException { + /** + * serialVersionUID + */ + private static final long serialVersionUID = 4076461843028836262L; + + /** + * Instantiates a new Json exception. + * + * @since 1.0.0 + */ + public StarterException() { + super(); + } + + /** + * Instantiates a new Json exception. + * + * @param message the message + * @since 1.0.0 + */ + public StarterException(String message) { + super(message); + } + + /** + * Instantiates a new Json exception. + * + * @param message the message + * @param cause the cause + * @since 1.0.0 + */ + public StarterException(String message, Throwable cause) { + super(message, cause); + } + + /** + * msg 占位符替换 + * + * @param msg msg + * @param args args + * @since 1.0.0 + */ + public StarterException(String msg, Object... args) { + super(msg, args); + this.resultCode = BaseCodes.FAILURE; + } + + /** + * Instantiates a new Json exception. + * + * @param cause the cause + * @since 1.0.0 + */ + public StarterException(Throwable cause) { + super(cause); + } + + /** + * Instantiates a new Json exception. + * + * @param message the message + * @param cause the cause + * @param enableSuppression the enable suppression + * @param writableStackTrace the writable stack traceId + * @since 1.0.0 + */ + protected StarterException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/band/aivfo-element/aivfo-element-core/pom.xml b/band/aivfo-element/aivfo-element-core/pom.xml new file mode 100644 index 0000000..ae2834e --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/pom.xml @@ -0,0 +1,40 @@ + + + + aivfo-element + com.aivfo + 1.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + aivfo-element-core + + + + com.aivfo + aivfo-element-base + 1.0.0-SNAPSHOT + + + org.springframework.boot + spring-boot + + + com.fasterxml.jackson.datatype + jackson-datatype-jdk8 + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + org.apache.commons + commons-text + 1.8 + + + + \ No newline at end of file diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/api/BaseCodes.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/api/BaseCodes.java new file mode 100644 index 0000000..49951d4 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/api/BaseCodes.java @@ -0,0 +1,111 @@ +package com.aivfo.el.start.core.api; + +import com.aivfo.el.start.core.assertion.BaseExceptionAssert; +import com.aivfo.el.starter.base.Result; +import com.aivfo.el.starter.base.annotation.BusinessLevel; +import com.aivfo.el.starter.base.annotation.ModelSerial; +import com.aivfo.el.starter.base.annotation.SystemLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 通用返回结果 + */ +@Getter +@AllArgsConstructor +@ModelSerial +public enum BaseCodes implements BaseExceptionAssert { + /** + * 成功 + */ + SUCCESS(Integer.parseInt(Result.SUCCESS_CODE), Result.SUCCESS_MESSAGE), + /** + * 默认没有数据响应 + */ + @SystemLevel + DEFAULT_NULL_DATA(2222, "暂无数据"), + /** + * 默认的失败响应 + */ + @SystemLevel + FAILURE(Integer.parseInt(Result.FAILURE_CODE), Result.FAILURE_MESSAGE), + /** + * 参数校验失败 + */ + @BusinessLevel + PARAM_VERIFY_ERROR(4100, "参数校验失败: [{}]"), + /** + * 数据不存在 + */ + @SystemLevel + DATA_ERROR(4101, "数据不存在"), + /** + * 操作失败 + */ + @SystemLevel + OPTION_FAILURE(4102, "操作失败"), + /** + * Config error base codes + */ + @SystemLevel + CONFIG_ERROR(7000, "配置错误"), + /** + * Server inner error base codes. + */ + @SystemLevel + SERVER_INNER_ERROR(5000, "服务内部错误"), + /** + * Service invoke error base codes + */ + @SystemLevel + SERVICE_INVOKE_ERROR(5001, "服务不可用"), + /** + * Agent exception + */ + @SystemLevel + AGENT_INVOKE_EXCEPTION(5002, "Rest Client 调用失败"), + /** + * Agent enable exception base codes + */ + @SystemLevel + AGENT_DISABLE_EXCEPTION(5003, "Agent Service 不可用"), + /** + * Rpc error base codes + */ + @SystemLevel + RPC_ERROR(5004, "远程服务不可用"), + /** + * Gateway not fund instances error base codes + */ + GATEWAY_NOT_FUND_INSTANCES_ERROR(5005, "网关路由失败, 未找到指定服务"), + /** + * 路由配置错误 + */ + GATEWAY_ROUTER_ERROR(5006, "网关路由配置错误: 1.请检查调用端配置 ; 2.检查路由配置是否正确."), + /** + * Agent service not found error base codes + */ + @BusinessLevel + AGENT_SERVICE_NOT_FOUND_ERROR(5007, "未找到指定服务"), + /** + * 服务器繁忙,请稍后重试 + */ + @SystemLevel + SERVER_BUSY(9998, "服务暂时不可用,请稍后重试!"), + /** + * 服务器异常,无法识别的异常,尽可能对通过判断减少未定义异常抛出 + */ + @SystemLevel + SERVER_ERROR(9999, "网络异常"); + + /** + * 返回码 + */ + private final Integer code; + /** + * 返回消息 + */ + private final String message; +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/api/GeneralResult.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/api/GeneralResult.java new file mode 100644 index 0000000..81bca1e --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/api/GeneralResult.java @@ -0,0 +1,94 @@ +package com.aivfo.el.start.core.api; + + +import com.aivfo.el.starter.base.Result; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: + */ +public interface GeneralResult { + + /** + * 请求成功 + * + * @param parameter + * @return the result + * @since 1.0.0 + */ + default Result ok() { + return this.ok(null); + } + + /** + * 请求成功 + * + * @param 对象泛型 + * @param data 数据内容 + * @return the result + * @since 1.0.0 + */ + default Result ok(T data) { + return R.succeed(data); + } + + /** + * 请求失败 + * + * @param parameter + * @param msg 提示内容 + * @return the result + * @since 1.0.0 + */ + default Result fail(String msg) { + return R.failed(msg); + } + + /** + * 请求失败 + * + * @param parameter + * @param errorCode 请求错误码 + * @return the result + * @since 1.0.0 + */ + default Result fail(IResultCode errorCode) { + return R.failed(errorCode); + } + + /** + * Status result. + * + * @param flag the flag + * @return the result + * @since 1.0.0 + */ + default Result status(boolean flag) { + return this.status(flag, BaseCodes.FAILURE); + } + + /** + * Status result + * + * @param flag flag + * @param resultCode result code + * @return the result + * @since 1.0.0 + */ + default Result status(boolean flag, IResultCode resultCode) { + return R.status(flag, resultCode); + } + + /** + * Status result + * + * @param flag flag + * @param message message + * @return the result + * @since 1.0.0 + */ + default Result status(boolean flag, String message) { + return R.status(flag, message); + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/api/IResultCode.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/api/IResultCode.java new file mode 100644 index 0000000..eb8cc96 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/api/IResultCode.java @@ -0,0 +1,38 @@ +package com.aivfo.el.start.core.api; + +import java.io.Serializable; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 请求响应代码接口 + */ +public interface IResultCode extends Serializable { + + /** + * 获取返回消息, 可使用占位符 + * + * @return String message + * @since 1.0.0 + */ + String getMessage(); + + /** + * 获取返回状态码 + * + * @return String code + * @since 1.0.0 + */ + Integer getCode(); + + /** + * Name + * + * @return the string + * @since 1.5.0 + */ + default String name() { + return "FAILURE"; + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/api/R.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/api/R.java new file mode 100644 index 0000000..1ed28f3 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/api/R.java @@ -0,0 +1,343 @@ +package com.aivfo.el.start.core.api; + +import com.aivfo.el.start.core.support.ChainMap; +import com.aivfo.el.start.core.util.CollectionUtils; +import com.aivfo.el.start.core.util.ResultCodeUtils; +import com.aivfo.el.starter.base.Result; +import com.aivfo.el.starter.base.contex.Trace; +import com.aivfo.el.starter.base.utils.StringUtils; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.Map; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 将标准返回的字段全部放在了 + */ +@SuppressWarnings({"PMD.ClassNamingShouldBeCamelRule"}) +public final class R extends Result { + /** + * serialVersionUID + */ + private static final long serialVersionUID = 3077918845714343375L; + + /** + * R + * + * @param code code + * @param message message + * @param data data + * @since 1.0.0 + */ + @Contract(pure = true) + private R(String code, String message, T data) { + super(code, message, data, Trace.context().get()); + } + + /** + * Succeed result + * + * @param parameter + * @return the result + * @since 1.0.0 + */ + @Contract(pure = true) + @NotNull + @SuppressWarnings("unchecked") + public static Result succeed() { + return succeed((T) Collections.emptyMap()); + } + + /** + * Succeed result + * + * @param parameter + * @param data data + * @return the result + * @since 1.0.0 + */ + @Contract(pure = true) + @NotNull + public static Result succeed(T data) { + return succeed(SUCCESS_CODE, SUCCESS_MESSAGE, data); + } + + /** + * Succeed result + * + * @param parameter + * @param code code + * @param msg msg + * @param data data + * @return the result + * @since 1.0.0 + */ + @Contract(pure = true) + @NotNull + public static Result succeed(String code, String msg, T data) { + return build(code, msg, data); + } + + /** + * Failed result + * + * @param parameter + * @return the result + * @since 1.0.0 + */ + @Contract(pure = true) + @NotNull + public static Result failed() { + return failed(FAILURE_MESSAGE); + } + + /** + * Failed result + * + * @param parameter + * @param msg msg + * @return the result + * @since 1.0.0 + */ + @Contract(pure = true) + @NotNull + public static Result failed(String msg) { + return failed(FAILURE_CODE, msg); + } + + /** + * Failed result + * + * @param parameter + * @param code code + * @param msg msg + * @return the result + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + @SuppressWarnings("unchecked") + public static Result failed(String code, String msg) { + return failed(code, msg, (T) Collections.emptyMap()); + } + + /** + * Failed result + * + * @param parameter + * @param resultCode result code + * @return the result + * @since 1.0.0 + */ + @NotNull + public static Result failed(@NotNull IResultCode resultCode) { + return failed(ResultCodeUtils.generateCode(resultCode), resultCode.getMessage()); + } + + /** + * Failed result + * + * @param parameter + * @param resultCode result code + * @param msg msg + * @return the result + * @since 1.0.0 + */ + @NotNull + public static Result failed(@NotNull IResultCode resultCode, String msg) { + return failed(ResultCodeUtils.generateCode(resultCode), StringUtils.format(resultCode.getMessage(), msg)); + } + + /** + * Failed result + * + * @param parameter + * @param resultCode result code + * @param msg msg + * @param data data + * @return the result + * @since 1.0.0 + */ + @NotNull + @Contract(value = "_, _, _ -> new", pure = true) + public static Result failed(@NotNull IResultCode resultCode, String msg, T data) { + return failed(ResultCodeUtils.generateCode(resultCode), StringUtils.format(resultCode.getMessage(), msg), data); + } + + /** + * Failed result + * + * @param parameter + * @param code code + * @param msg msg + * @param data data + * @return the result + * @since 1.0.0 + */ + @NotNull + @Contract(value = "_, _, _ -> new", pure = true) + public static Result failed(String code, String msg, T data) { + return build(code, msg, data); + } + + /** + * Build result + * + * @param parameter + * @param code code + * @param msg msg + * @return the result + * @since 1.7.0 + */ + @Contract("_, _ -> new") + @NotNull + @SuppressWarnings("unchecked") + public static Result build(String code, String msg) { + return build(code, msg, (T) Collections.emptyMap()); + } + + /** + * Build result + * + * @param parameter + * @param code code + * @param msg msg + * @param data data + * @return the result + * @since 1.0.0 + */ + @Contract(value = "_, _, _ -> new", pure = true) + @NotNull + public static Result build(String code, String msg, T data) { + return new R<>(code, msg, data); + } + + /** + * Status result + * + * @param parameter + * @param expression expression + * @return the result + * @since 1.0.0 + */ + @Contract("_ -> !null") + public static Result status(boolean expression) { + return status(expression, ""); + } + + /** + * Status result + * + * @param parameter + * @param expression expression + * @param resultCode result code + * @return the result + * @since 1.0.0 + */ + @Contract("_, _ -> !null") + public static Result status(boolean expression, @NotNull IResultCode resultCode) { + return status(expression, resultCode.getMessage()); + } + + /** + * Status result + * + * @param parameter + * @param expression expression + * @param message message + * @return the result + * @since 1.0.0 + */ + @Contract("_, _ -> !null") + public static Result status(boolean expression, String message) { + return expression ? succeed() : failed(message); + } + + /** + * Values result + * + * @param args 键值对一一对应 + * @return the result + * @since 1.0.0 + */ + @NotNull + public static Result> values(@NotNull Object... args) { + return succeed(CollectionUtils.toMap(args)); + } + + /** + * Map map + * + * @param parameter + * @param data data + * @return the map + * @since 1.0.0 + */ + @NotNull + public static Map map(T data) { + return map(ResultCodeUtils.generateCode(BaseCodes.SUCCESS), + true, + data, + BaseCodes.SUCCESS.getMessage(), + Trace.context().get()); + } + + /** + * Map map + * + * @param parameter + * @param code code + * @param success success + * @param data data + * @param message message + * @param traceId trace id + * @return the map + * @since 1.0.0 + */ + @NotNull + public static Map map(String code, + boolean success, + T data, + String message, + String traceId) { + + return ChainMap.build(5) + .put(CODE, code) + .put(SUCCESS, success) + .put(DATA, data) + .put(MESSAGE, message) + .put(TRACE_ID, traceId); + + } + + /** + * Fail map map + * + * @param resultCode result code + * @return the map + * @since 1.0.0 + */ + @NotNull + public static Map failMap(@NotNull IResultCode resultCode) { + return map(ResultCodeUtils.generateCode(resultCode), false, null, resultCode.getMessage(), Trace.context().get()); + } + + /** + * Map map + * + * @param parameter + * @param resultCode result code + * @param data data + * @return the map + * @since 1.0.0 + */ + @NotNull + public static Map map(@NotNull IResultCode resultCode, T data) { + return map(ResultCodeUtils.generateCode(resultCode), + BaseCodes.SUCCESS.getCode().equals(resultCode.getCode()), + data, resultCode.getMessage(), Trace.context().get()); + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/assertion/BaseExceptionAssert.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/assertion/BaseExceptionAssert.java new file mode 100644 index 0000000..2f9ca02 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/assertion/BaseExceptionAssert.java @@ -0,0 +1,42 @@ +package com.aivfo.el.start.core.assertion; + +import com.aivfo.el.start.core.api.IResultCode; +import com.aivfo.el.start.core.exception.BaseException; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: 全局错误异常断言 + */ +public interface BaseExceptionAssert extends IResultCode, IAssert { + /** + * serialVersionUID + */ + long serialVersionUID = 3077918845714343375L; + + /** + * New exceptions base exception. + * + * @param args the args + * @return the base exception + * @since 1.0.0 + */ + @Override + default BaseException newException(Object... args) { + return new BaseException(this, args, this.getMessage()); + } + + /** + * New exceptions base exception. + * + * @param t the t + * @param args the args + * @return the base exception + * @since 1.0.0 + */ + @Override + default BaseException newException(Throwable t, Object... args) { + return new BaseException(this, args, this.getMessage(), t); + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/assertion/IAssert.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/assertion/IAssert.java new file mode 100644 index 0000000..846ab11 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/assertion/IAssert.java @@ -0,0 +1,224 @@ +package com.aivfo.el.start.core.assertion; + + +import com.aivfo.el.start.core.exception.BaseException; +import com.aivfo.el.starter.base.asserts.Assertions; +import com.aivfo.el.starter.core.function.CheckedCallable; +import com.aivfo.el.starter.core.function.CheckedRunnable; + +import java.util.Collection; +import java.util.Map; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 枚举类异常断言,提供简便的方式判断条件,并在条件满足时抛出异常 + * 错误码和错误信息定义在枚举类中,在本断言方法中,传递错误信息需要的参数 + * 底层会使用 {@link Assertions} + *

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.26 20:06 + * @since 1.0.0 + */ +public interface IAssert { + + /** + * New exception + * + * @param args args + * @return the base exception + * @since 1.7.0 + */ + BaseException newException(Object... args); + + /** + * New exception + * + * @param t t + * @param args args + * @return the base exception + * @since 1.7.0 + */ + BaseException newException(Throwable t, Object... args); + + /** + * Not blank + * + * @param str str + * @param args args + * @since 1.7.0 + */ + default void notBlank(String str, Object... args) { + Assertions.notBlank(str, () -> this.newException(args)); + } + + /** + * Not empty + * + * @param arrays arrays + * @param args args + * @since 1.7.0 + */ + default void notEmpty(Object[] arrays, Object... args) { + Assertions.notEmpty(arrays, () -> this.newException(args)); + } + + /** + * Not empty + * + * @param c c + * @param args args + * @since 1.7.0 + */ + default void notEmpty(Collection c, Object... args) { + Assertions.notEmpty(c, () -> this.newException(args)); + } + + /** + * Not empty + * + * @param map map + * @param args args + * @since 1.7.0 + */ + default void notEmpty(Map map, Object... args) { + Assertions.notEmpty(map, () -> this.newException(args)); + } + + /** + * Is false + * + * @param expression expression + * @param args args + * @since 1.7.0 + */ + default void isFalse(boolean expression, Object... args) { + Assertions.isFalse(expression, () -> this.newException(args)); + } + + /** + * Is true + * + * @param expression expression + * @param args args + * @since 1.7.0 + */ + default void isTrue(boolean expression, Object... args) { + Assertions.isTrue(expression, () -> this.newException(args)); + } + + /** + * Is null + * + * @param obj obj + * @param args args + * @since 1.7.0 + */ + default void isNull(Object obj, Object... args) { + Assertions.isNull(obj, () -> this.newException(args)); + } + + /** + * Not null + * + * @param obj obj + * @param args args + * @since 1.7.0 + */ + default void notNull(Object obj, Object... args) { + Assertions.notNull(obj, () -> this.newException(args)); + } + + /** + * 适用于没有占位符的错误消息 + * + * @param obj obj + * @param runnable runnable + * @since 1.7.0 + */ + default void notNull(Object obj, CheckedRunnable runnable) { + Assertions.notNull(obj, this::newException, runnable); + } + + /** + * Equals + * + * @param o1 o 1 + * @param o2 o 2 + * @param args args + * @since 1.7.0 + */ + default void equals(Object o1, Object o2, Object... args) { + Assertions.equals(o1, o2, () -> this.newException(args)); + } + + /** + * Not equals + * + * @param o1 o 1 + * @param o2 o 2 + * @param args args + * @since 1.7.0 + */ + default void notEquals(Object o1, Object o2, Object... args) { + Assertions.notEquals(o1, o2, () -> this.newException(args)); + } + + /** + * Wrapper + * + * @param runnable runnable + * @param args args + * @since 1.7.0 + */ + default void wrapper(CheckedRunnable runnable, Object... args) { + try { + runnable.run(); + } catch (Throwable throwable) { + this.fail(throwable, args); + } + } + + /** + * Wrapper + * + * @param parameter + * @param callable callable + * @param args args + * @return the t + * @since 1.8.0 + */ + default T wrapper(CheckedCallable callable, Object... args) { + try { + return callable.call(); + } catch (Throwable throwable) { + this.fail(throwable, args); + } + return null; + } + + /** + * Fail + * + * @param args args + * @since 1.7.0 + */ + default void fail(Object... args) { + this.fail(this.newException(args)); + } + + /** + * Fail + * + * @param t t + * @param args args + * @since 1.7.0 + */ + default void fail(Throwable t, Object... args) { + Assertions.fail(() -> this.newException(t, args)); + } + + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/convert/CustomConversionService.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/convert/CustomConversionService.java new file mode 100644 index 0000000..4825d0c --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/convert/CustomConversionService.java @@ -0,0 +1,71 @@ +package com.aivfo.el.start.core.convert; + +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.springframework.boot.convert.ApplicationConversionService; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.lang.Nullable; +import org.springframework.util.StringValueResolver; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 类型 转换 服务,添加了 IEnum 转换 + */ +public final class CustomConversionService extends ApplicationConversionService { + + /** + * Custom conversion service + * + * @since 1.0.0 + */ + private CustomConversionService() { + this(null); + } + + /** + * Custom conversion service + * + * @param embeddedValueResolver the embedded value resolver + * @since 1.0.0 + */ + private CustomConversionService(@Nullable StringValueResolver embeddedValueResolver) { + super(embeddedValueResolver); + super.addConverter(new EnumToStringConverter()); + super.addConverter(new StringToEnumConverter()); + } + + /** + * Gets instance. + * + * @return the instance + * @since 1.0.0 + */ + @Contract(pure = true) + public static GenericConversionService getInstance() { + return SingletonHolder.INSTANCE; + } + + /** + * @author: wangyl + * @date: 2023/5/16 + * @description: 静态内部类实现单例 + */ + private static final class SingletonHolder { + /** + * INSTANCE + */ + private static final CustomConversionService INSTANCE = new CustomConversionService(); + + /** + * Singleton holder + * + * @since 1.0.0 + */ + @Contract(pure = true) + private SingletonHolder() { + + } + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/convert/CustomConverter.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/convert/CustomConverter.java new file mode 100644 index 0000000..a911c94 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/convert/CustomConverter.java @@ -0,0 +1,97 @@ +package com.aivfo.el.start.core.convert; + +import com.aivfo.el.start.core.util.ConvertUtils; +import com.aivfo.el.start.core.util.ReflectionUtils; +import com.aivfo.el.start.core.util.Unchecked; +import com.aivfo.el.starter.base.utils.ClassUtils; +import com.aivfo.el.starter.core.function.CheckedFunction; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.cglib.core.Converter; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.lang.Nullable; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 组合 spring cglib Converter 和 spring ConversionService + */ +@Slf4j +@AllArgsConstructor +public class CustomConverter implements Converter { + /** + * TYPE_CACHE + */ + private static final ConcurrentMap TYPE_CACHE = new ConcurrentHashMap<>(); + /** + * Source clazz + */ + private final Class sourceClazz; + /** + * Target clazz + */ + private final Class targetClazz; + + /** + * cglib convert + * + * @param value 源对象属性 + * @param target 目标对象属性类 + * @param fieldName 目标的field名,原为 set 方法名,MicaBeanCopier 里做了更改 + * @return {Object} + * @since 1.0.0 + */ + @Override + @Nullable + @SuppressWarnings("checkstyle:ReturnCount") + public Object convert(Object value, Class target, Object fieldName) { + if (value == null) { + return null; + } + // 类型一样,不需要转换 + if (ClassUtils.isAssignableValue(target, value)) { + return value; + } + try { + TypeDescriptor targetDescriptor = CustomConverter.getTypeDescriptor(this.targetClazz, (String) fieldName); + // 1. 判断 sourceClazz 为 Map + if (Map.class.isAssignableFrom(this.sourceClazz)) { + return ConvertUtils.convert(value, targetDescriptor); + } else { + TypeDescriptor sourceDescriptor = CustomConverter.getTypeDescriptor(this.sourceClazz, (String) fieldName); + return ConvertUtils.convert(value, sourceDescriptor, targetDescriptor); + } + } catch (Exception e) { + log.warn("Converter error", e); + } + return null; + } + + /** + * Gets type descriptor * + * + * @param clazz clazz + * @param fieldName field name + * @return the type descriptor + * @since 1.0.0 + */ + private static TypeDescriptor getTypeDescriptor(@NotNull Class clazz, String fieldName) { + String srcCacheKey = clazz.getName() + fieldName; + // 忽略抛出异常的函数,定义完整泛型,避免编译问题 + CheckedFunction uncheckedFunction = key -> { + // 这里 property 理论上不会为 null + Field field = ReflectionUtils.getField(clazz, fieldName); + if (field == null) { + throw new NoSuchFieldException(fieldName); + } + return new TypeDescriptor(field); + }; + return TYPE_CACHE.computeIfAbsent(srcCacheKey, Unchecked.function(uncheckedFunction)); + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/convert/EnumToStringConverter.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/convert/EnumToStringConverter.java new file mode 100644 index 0000000..b8c7f5d --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/convert/EnumToStringConverter.java @@ -0,0 +1,154 @@ +package com.aivfo.el.start.core.convert; + +import com.aivfo.el.start.core.util.ConvertUtils; +import com.fasterxml.jackson.annotation.JsonValue; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.ConditionalGenericConverter; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 接收参数 同 jackson Enum -> String 转换 + */ +@Slf4j +public class EnumToStringConverter implements ConditionalGenericConverter { + /** + * 缓存 Enum 类信息,提供性能 + */ + private static final ConcurrentMap, AccessibleObject> ENUM_CACHE_MAP = Maps.newConcurrentMap(); + + /** + * Matches boolean + * + * @param sourceType source type + * @param targetType target type + * @return the boolean + * @since 1.0.0 + */ + @Override + public boolean matches(@NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType) { + return true; + } + + /** + * Gets convertible types * + * + * @return the convertible types + * @since 1.0.0 + */ + @Override + public Set getConvertibleTypes() { + Set pairSet = new HashSet<>(4); + pairSet.add(new ConvertiblePair(Enum.class, String.class)); + pairSet.add(new ConvertiblePair(Enum.class, Integer.class)); + pairSet.add(new ConvertiblePair(Enum.class, Long.class)); + return Collections.unmodifiableSet(pairSet); + } + + /** + * Convert object + * + * @param source source + * @param sourceType source type + * @param targetType target type + * @return the object + * @since 1.0.0 + */ + @Override + @SuppressWarnings("all") + public Object convert(@Nullable Object source, @NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType) { + if (source == null) { + return null; + } + Class sourceClazz = sourceType.getType(); + AccessibleObject accessibleObject = ENUM_CACHE_MAP.computeIfAbsent(sourceClazz, EnumToStringConverter::getAnnotation); + Class targetClazz = targetType.getType(); + // 如果为null,走默认的转换 + if (accessibleObject == null) { + if (String.class == targetClazz) { + return ((Enum) source).name(); + } + int ordinal = ((Enum) source).ordinal(); + return ConvertUtils.convert(ordinal, targetClazz); + } + try { + return EnumToStringConverter.invoke(sourceClazz, accessibleObject, source, targetClazz); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + /** + * Gets annotation * + * + * @param clazz clazz + * @return the annotation + * @since 1.0.0 + */ + @Nullable + @SuppressWarnings("java:S3011") + private static AccessibleObject getAnnotation(@NotNull Class clazz) { + Set accessibleObjects = new HashSet<>(); + // JsonValue METHOD, FIELD + Field[] fields = clazz.getDeclaredFields(); + Collections.addAll(accessibleObjects, fields); + // methods + Method[] methods = clazz.getDeclaredMethods(); + Collections.addAll(accessibleObjects, methods); + for (AccessibleObject accessibleObject : accessibleObjects) { + // 复用 jackson 的 JsonValue 注解 + JsonValue jsonValue = accessibleObject.getAnnotation(JsonValue.class); + if (jsonValue != null && jsonValue.value()) { + accessibleObject.setAccessible(true); + return accessibleObject; + } + } + return null; + } + + /** + * Invoke object + * + * @param clazz clazz + * @param accessibleObject accessible object + * @param source source + * @param targetClazz target clazz + * @return the object + * @throws IllegalAccessException illegal access exception + * @throws InvocationTargetException invocation target exception + * @since 1.0.0 + */ + @Nullable + private static Object invoke(Class clazz, AccessibleObject accessibleObject, Object source, Class targetClazz) + throws IllegalAccessException, InvocationTargetException { + Object value = null; + if (accessibleObject instanceof Field) { + Field field = (Field) accessibleObject; + value = field.get(source); + } else if (accessibleObject instanceof Method) { + Method method = (Method) accessibleObject; + Class paramType = method.getParameterTypes()[0]; + // 类型转换 + Object object = ConvertUtils.convert(source, paramType); + value = method.invoke(clazz, object); + } + if (value == null) { + return null; + } + return ConvertUtils.convert(value, targetClazz); + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/convert/StringToEnumConverter.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/convert/StringToEnumConverter.java new file mode 100644 index 0000000..cfc0871 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/convert/StringToEnumConverter.java @@ -0,0 +1,169 @@ +package com.aivfo.el.start.core.convert; + +import com.aivfo.el.start.core.util.ConvertUtils; +import com.aivfo.el.starter.base.utils.StringUtils; +import com.fasterxml.jackson.annotation.JsonCreator; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.ConditionalGenericConverter; +import org.springframework.lang.Nullable; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 接收参数 同 jackson String -> Enum 转换 + */ +@Slf4j +public class StringToEnumConverter implements ConditionalGenericConverter { + /** + * 缓存 Enum 类信息,提升性能 + */ + private static final ConcurrentMap, AccessibleObject> ENUM_CACHE_MAP = new ConcurrentHashMap<>(8); + + /** + * Matches boolean + * + * @param sourceType source type + * @param targetType target type + * @return the boolean + * @since 1.0.0 + */ + @Override + public boolean matches(@NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType) { + return true; + } + + /** + * Gets convertible types * + * + * @return the convertible types + * @since 1.0.0 + */ + @Override + public Set getConvertibleTypes() { + return Collections.singleton(new ConvertiblePair(String.class, Enum.class)); + } + + /** + * Convert object + * + * @param source source + * @param sourceType source type + * @param targetType target type + * @return the object + * @since 1.0.0 + */ + @Nullable + @Override + @SuppressWarnings("checkstyle:ReturnCount") + public Object convert(@Nullable Object source, @NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType) { + if (source == null) { + return null; + } + if (StringUtils.isBlank((String) source)) { + return null; + } + Class clazz = targetType.getType(); + AccessibleObject accessibleObject = ENUM_CACHE_MAP.computeIfAbsent(clazz, StringToEnumConverter::getAnnotation); + String value = ((String) source).trim(); + // 如果为null,走默认的转换 + if (accessibleObject == null) { + return valueOf(clazz, value); + } + try { + return StringToEnumConverter.invoke(clazz, accessibleObject, value); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + /** + * Gets annotation * + * + * @param clazz clazz + * @return the annotation + * @since 1.0.0 + */ + @org.jetbrains.annotations.Nullable + @Nullable + private static AccessibleObject getAnnotation(@NotNull Class clazz) { + Set accessibleObjects = new HashSet<>(); + // JsonCreator METHOD, CONSTRUCTOR + Constructor[] constructors = clazz.getConstructors(); + Collections.addAll(accessibleObjects, constructors); + // methods + Method[] methods = clazz.getDeclaredMethods(); + Collections.addAll(accessibleObjects, methods); + for (AccessibleObject accessibleObject : accessibleObjects) { + // 复用 jackson 的 JsonCreator注解 + JsonCreator jsonCreator = accessibleObject.getAnnotation(JsonCreator.class); + if (jsonCreator != null && JsonCreator.Mode.DISABLED != jsonCreator.mode()) { + accessibleObject.setAccessible(true); + return accessibleObject; + } + } + return null; + } + + /** + * Value of t + * + * @param parameter + * @param clazz clazz + * @param value value + * @return the t + * @since 1.0.0 + */ + @NotNull + @SuppressWarnings("unchecked") + private static > T valueOf(Class clazz, String value) { + return Enum.valueOf((Class) clazz, value); + } + + /** + * Invoke object + * + * @param clazz clazz + * @param accessibleObject accessible object + * @param value value + * @return the object + * @throws IllegalAccessException illegal access exception + * @throws InvocationTargetException invocation target exception + * @throws InstantiationException instantiation exception + * @since 1.0.0 + */ + @Contract("_, null, _ -> null") + @Nullable + private static Object invoke(Class clazz, AccessibleObject accessibleObject, String value) + throws IllegalAccessException, InvocationTargetException, InstantiationException { + if (accessibleObject instanceof Constructor) { + @SuppressWarnings("rawtypes") Constructor constructor = (Constructor) accessibleObject; + Class paramType = constructor.getParameterTypes()[0]; + // 类型转换 + Object object = ConvertUtils.convert(value, paramType); + return constructor.newInstance(object); + } + if (accessibleObject instanceof Method) { + Method method = (Method) accessibleObject; + Class paramType = method.getParameterTypes()[0]; + // 类型转换 + Object object = ConvertUtils.convert(value, paramType); + return method.invoke(clazz, object); + } + return null; + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/enums/RandomType.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/enums/RandomType.java new file mode 100644 index 0000000..d4ba353 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/enums/RandomType.java @@ -0,0 +1,21 @@ +package com.aivfo.el.start.core.enums; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 生成的随机数类型 + */ +public enum RandomType { + /** + * INT STRING ALL + */ + INT, + /** + * String random type + */ + STRING, + /** + * All random type + */ + ALL +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/exception/BaseException.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/exception/BaseException.java new file mode 100644 index 0000000..336d6f2 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/exception/BaseException.java @@ -0,0 +1,227 @@ +package com.aivfo.el.start.core.exception; + +import com.aivfo.el.start.core.util.ResultCodeUtils; +import com.aivfo.el.start.core.api.BaseCodes; +import com.aivfo.el.start.core.api.IResultCode; +import com.aivfo.el.starter.base.exception.BasicException; +import com.aivfo.el.starter.base.support.StrFormatter; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: 基础异常类, 所有自定义异常类都需要继承本类 + */ +@Slf4j +@Getter +public class BaseException extends BasicException { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = 1L; + + /** + * 返回码 + */ + protected IResultCode resultCode; + /** + * 异常消息参数 + */ + protected Object[] args; + + /** + * Instantiates a new Base exception. + * + * @since 1.0.0 + */ + public BaseException() { + super(BaseCodes.FAILURE.getMessage()); + this.resultCode = BaseCodes.FAILURE; + } + + /** + * Instantiates a new Base exception. + * + * @param msg the msg + * @since 1.0.0 + */ + public BaseException(String msg) { + super(msg); + this.resultCode = BaseCodes.FAILURE; + } + + /** + * Instantiates a new Base exception. + * + * @param code the code + * @param msg the msg + * @since 1.0.0 + */ + public BaseException(int code, String msg) { + super(msg); + this.resultCode = new IResultCode() { + private static final long serialVersionUID = 2590640370242410124L; + + @Override + public String getMessage() { + return msg; + } + + @Override + public Integer getCode() { + return code; + } + }; + } + + public BaseException(String code, String msg) { + super(code, msg); + this.resultCode = new IResultCode() { + private static final long serialVersionUID = 2590640370242410124L; + + /** + * Gets message * + * + * @return the message + * @since 1.6.0 + */ + @Override + public String getMessage() { + return msg; + } + + @Override + public Integer getCode() { + return ResultCodeUtils.convert(code); + } + }; + } + + /** + * msg 占位符替换 + * + * @param msg msg + * @param args args + * @since 1.0.0 + */ + public BaseException(String msg, Object... args) { + super(msg, args); + this.resultCode = BaseCodes.FAILURE; + } + + /** + * Base exception + * + * @param cause cause + * @since 1.0.0 + */ + public BaseException(Throwable cause) { + super(cause); + this.resultCode = BaseCodes.FAILURE; + } + + /** + * Base exception + * + * @param msg msg + * @param cause cause + * @since 1.0.0 + */ + public BaseException(String msg, Throwable cause) { + super(msg, cause); + this.resultCode = BaseCodes.FAILURE; + } + + /** + * Instantiates a new Base exception. + * + * @param resultCode the response enum + * @since 1.0.0 + */ + public BaseException(@NotNull IResultCode resultCode) { + super(resultCode.getMessage()); + this.resultCode = resultCode; + } + + /** + * Base exception + * + * @param resultCode result code + * @param cause cause + * @since 1.0.0 + */ + public BaseException(@NotNull IResultCode resultCode, Throwable cause) { + super(resultCode.getMessage(), cause); + this.resultCode = resultCode; + } + + /** + * Instantiates a new Base exception. + * + * @param code the code + * @param msg the msg + * @param cause cause + * @since 1.0.0 + */ + public BaseException(int code, String msg, Throwable cause) { + super(msg, cause); + this.resultCode = new IResultCode() { + private static final long serialVersionUID = 2590640370242410124L; + + @Override + public String getMessage() { + return msg; + } + + @Override + public Integer getCode() { + return code; + } + + }; + } + + /** + * Instantiates a new Base exception. + * + * @param resultCode the response enum + * @param args the args + * @param msg msg 替换占位符后的消息 + * @since 1.0.0 + */ + public BaseException(IResultCode resultCode, Object[] args, String msg) { + super(StrFormatter.mergeFormat(msg, args)); + this.resultCode = resultCode; + this.args = args; + } + + /** + * Instantiates a new Base exception. + * + * @param resultCode the response enum + * @param args the args + * @param msg msg + * @param cause the cause + * @since 1.0.0 + */ + public BaseException(IResultCode resultCode, Object[] args, String msg, Throwable cause) { + super(StrFormatter.mergeFormat(msg, args), cause); + this.resultCode = resultCode; + this.args = args; + } + + /** + * Get code + * + * @return the string + * @since 1.6.0 + */ + @Override + public String getCode() { + return ResultCodeUtils.generateCode(this.resultCode); + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/BaseNode.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/BaseNode.java new file mode 100644 index 0000000..9a1391e --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/BaseNode.java @@ -0,0 +1,33 @@ +package com.aivfo.el.start.core.node; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 节点基类 + */ +@Data +public class BaseNode implements INode { + + /** + * 主键ID + */ + protected Integer id; + + /** + * 父节点ID + */ + protected Integer parentId; + + /** + * 子孙节点 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + protected List children = new ArrayList<>(); + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/ForestNode.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/ForestNode.java new file mode 100644 index 0000000..70f9168 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/ForestNode.java @@ -0,0 +1,39 @@ +package com.aivfo.el.start.core.node; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: 森林节点类

+ * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.26 20:42 + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class ForestNode extends BaseNode { + + /** + * 节点内容 + */ + private Object content; + + /** + * Instantiates a new Forest node. + * + * @param id the id + * @param parentId the parent id + * @param content the content + * @since 1.0.0 + */ + public ForestNode(Integer id, Integer parentId, Object content) { + this.id = id; + this.parentId = parentId; + this.content = content; + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/ForestNodeManager.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/ForestNodeManager.java new file mode 100644 index 0000000..38f2cb1 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/ForestNodeManager.java @@ -0,0 +1,75 @@ +package com.aivfo.el.start.core.node; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 森林管理类 + */ +public class ForestNodeManager { + + /** + * 森林的所有节点 + */ + private final List list; + + /** + * 森林的父节点ID + */ + private final List parentIds = new ArrayList<>(); + + /** + * Instantiates a new Forest node manager. + * + * @param items the items + * @since 1.0.0 + */ + public ForestNodeManager(List items) { + list = items; + } + + /** + * 根据节点ID获取一个节点 + * + * @param id 节点ID + * @return 对应的节点对象 tree node at + * @since 1.0.0 + */ + public INode getTreeNodeAt(int id) { + for (INode forestNode : list) { + if (forestNode.getId() == id) { + return forestNode; + } + } + return null; + } + + /** + * 增加父节点ID + * + * @param parentId 父节点ID + * @since 1.0.0 + */ + public void addParentId(Integer parentId) { + parentIds.add(parentId); + } + + /** + * 获取树的根节点(一个森林对应多颗树) + * + * @return 树的根节点集合 root + * @since 1.0.0 + */ + public List getRoot() { + List roots = new ArrayList<>(); + for (T forestNode : list) { + if (forestNode.getParentId() == 0 || parentIds.contains(forestNode.getId())) { + roots.add(forestNode); + } + } + return roots; + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/ForestNodeMerger.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/ForestNodeMerger.java new file mode 100644 index 0000000..ca87451 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/ForestNodeMerger.java @@ -0,0 +1,36 @@ +package com.aivfo.el.start.core.node; + +import java.util.List; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 森林节点归并类 + */ +public class ForestNodeMerger { + + /** + * 将节点数组归并为一个森林 (多棵树) (填充节点的children域) + * 时间复杂度为O(n^2) + * + * @param T 泛型标记 + * @param items 节点域 + * @return 多棵树的根节点集合 list + * @since 1.0.0 + */ + public static List merge(List items) { + ForestNodeManager forestNodeManager = new ForestNodeManager<>(items); + items.forEach(forestNode -> { + if (forestNode.getParentId() != 0) { + INode node = forestNodeManager.getTreeNodeAt(forestNode.getParentId()); + if (node != null) { + node.getChildren().add(forestNode); + } else { + forestNodeManager.addParentId(forestNode.getId()); + } + } + }); + return forestNodeManager.getRoot(); + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/INode.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/INode.java new file mode 100644 index 0000000..521fb28 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/INode.java @@ -0,0 +1,31 @@ +package com.aivfo.el.start.core.node; + +import java.util.List; + +public interface INode { + + /** + * 主键 + * + * @return Integer id + * @since 1.0.0 + */ + Integer getId(); + + /** + * 父主键 + * + * @return Integer parent id + * @since 1.0.0 + */ + Integer getParentId(); + + /** + * 子孙节点 + * + * @return List children + * @since 1.0.0 + */ + List getChildren(); + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/TreeNode.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/TreeNode.java new file mode 100644 index 0000000..4cf706b --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/node/TreeNode.java @@ -0,0 +1,28 @@ +package com.aivfo.el.start.core.node; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 树型节点类 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class TreeNode extends BaseNode { + + /** + * Title + */ + private String title; + /** + * Key + */ + private Integer key; + /** + * Value + */ + private Integer value; + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/support/BaseBeanCopier.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/support/BaseBeanCopier.java new file mode 100644 index 0000000..ec37c15 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/support/BaseBeanCopier.java @@ -0,0 +1,340 @@ +package com.aivfo.el.start.core.support; + +import com.aivfo.el.start.core.util.BeanUtils; +import com.google.common.collect.Maps; +import org.jetbrains.annotations.NotNull; +import org.springframework.asm.ClassVisitor; +import org.springframework.asm.Type; +import org.springframework.cglib.core.*; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Modifier; +import java.security.ProtectionDomain; +import java.util.Map; + +/** + * spring cglib + * 1. 支持链式 bean + * 2. 自定义的 BeanCopier 解决 spring boot 和 cglib ClassLoader classLoader 不一致的问题 + */ +@SuppressWarnings("all") +public abstract class BaseBeanCopier { + /** + * KEY_FACTORY + */ + private static final BeanCopierKey KEY_FACTORY = (BeanCopierKey) KeyFactory.create(BeanCopierKey.class); + /** + * CONVERTER + */ + private static final Type CONVERTER = TypeUtils.parseType("org.springframework.cglib.core.Converter"); + /** + * BEAN_COPIER + */ + private static final Type BEAN_COPIER = TypeUtils.parseType(BaseBeanCopier.class.getName()); + /** + * COPY + */ + private static final Signature COPY = new Signature("copy", + Type.VOID_TYPE, + new Type[]{Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER}); + /** + * CONVERT + */ + private static final Signature CONVERT = TypeUtils.parseSignature("Object convert(Object, Class, Object)"); + + /** + * Create base bean copier. + * + * @param source the source + * @param target the target + * @param useConverter the use converter + * @return the base bean copier + * @since 1.0.0 + */ + public static BaseBeanCopier create(Class source, Class target, boolean useConverter) { + return BaseBeanCopier.create(source, target, null, useConverter); + } + + /** + * Create base bean copier. + * + * @param source the source + * @param target the target + * @param classLoader the class loader + * @param useConverter the use converter + * @return the base bean copier + * @since 1.0.0 + */ + public static BaseBeanCopier create(Class source, Class target, ClassLoader classLoader, boolean useConverter) { + Generator gen; + if (classLoader == null) { + gen = new Generator(); + } else { + gen = new Generator(classLoader); + } + gen.setSource(source); + gen.setTarget(target); + gen.setUseConverter(useConverter); + return gen.create(); + } + + /** + * 拷贝 + * + * @param from 源 + * @param to 目标 + * @param converter 转换器 + * @since 1.0.0 + */ + public abstract void copy(Object from, Object to, Converter converter); + + /** + * The interface Bean copier key. + * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.26 21:46 + * @since 1.0.0 + */ + interface BeanCopierKey { + /** + * 实例化 + * + * @param source 源 + * @param target 目标 + * @param useConverter 是否使用转换 + * @return object object + * @since 1.0.0 + */ + Object newInstance(String source, String target, boolean useConverter); + } + + /** + * The type Generator. + * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.26 21:46 + * @since 1.0.0 + */ + public static class Generator extends AbstractClassGenerator { + /** + * SOURCE + */ + private static final Source SOURCE = new Source(BaseBeanCopier.class.getName()); + /** + * Class loader + */ + private final ClassLoader classLoader; + /** + * Source + */ + private Class source; + /** + * Target + */ + private Class target; + /** + * Use converter + */ + private boolean useConverter; + + /** + * Instantiates a new Generator. + * + * @since 1.0.0 + */ + Generator() { + super(SOURCE); + this.classLoader = null; + } + + /** + * Instantiates a new Generator. + * + * @param classLoader the class loader + * @since 1.0.0 + */ + Generator(ClassLoader classLoader) { + super(SOURCE); + this.classLoader = classLoader; + } + + /** + * Sets source. + * + * @param source the source + * @since 1.0.0 + */ + public void setSource(@NotNull Class source) { + if (!Modifier.isPublic(source.getModifiers())) { + this.setNamePrefix(source.getName()); + } + this.source = source; + } + + /** + * Sets target. + * + * @param target the target + * @since 1.0.0 + */ + public void setTarget(@NotNull Class target) { + if (!Modifier.isPublic(target.getModifiers())) { + this.setNamePrefix(target.getName()); + } + + this.target = target; + } + + /** + * Sets use converter. + * + * @param useConverter the use converter + * @since 1.0.0 + */ + public void setUseConverter(boolean useConverter) { + this.useConverter = useConverter; + } + + /** + * Gets default class loader * + * + * @return the default class loader + * @since 1.0.0 + */ + @Override + protected ClassLoader getDefaultClassLoader() { + return this.target.getClassLoader(); + } + + /** + * Gets protection domain * + * + * @return the protection domain + * @since 1.0.0 + */ + @Override + protected ProtectionDomain getProtectionDomain() { + return ReflectUtils.getProtectionDomain(this.source); + } + + /** + * First instance object + * + * @param type type + * @return the object + * @since 1.0.0 + */ + @Override + protected Object firstInstance(Class type) { + return ReflectUtils.newInstance(type); + } + + /** + * Next instance object + * + * @param instance instance + * @return the object + * @since 1.0.0 + */ + @Override + protected Object nextInstance(Object instance) { + return instance; + } + + /** + * Create base bean copier. + * + * @return the base bean copier + * @since 1.0.0 + */ + public BaseBeanCopier create() { + Object key = KEY_FACTORY.newInstance(this.source.getName(), this.target.getName(), this.useConverter); + return (BaseBeanCopier) super.create(key); + } + + /** + * Generate class * + * + * @param v v + * @since 1.0.0 + */ + @Override + public void generateClass(ClassVisitor v) { + Type sourceType = Type.getType(this.source); + Type targetType = Type.getType(this.target); + ClassEmitter ce = new ClassEmitter(v); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + this.getClassName(), + BEAN_COPIER, + null, + Constants.SOURCE_FILE); + + EmitUtils.null_constructor(ce); + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null); + + // 支持链式 bean + PropertyDescriptor[] getters = BeanUtils.getBeanGetters(this.source); + PropertyDescriptor[] setters = BeanUtils.getBeanSetters(this.target); + Map names = Maps.newHashMapWithExpectedSize(16); + for (PropertyDescriptor getter : getters) { + names.put(getter.getName(), getter); + } + + Local targetLocal = e.make_local(); + Local sourceLocal = e.make_local(); + e.load_arg(1); + e.checkcast(targetType); + e.store_local(targetLocal); + e.load_arg(0); + e.checkcast(sourceType); + e.store_local(sourceLocal); + + for (PropertyDescriptor setter : setters) { + PropertyDescriptor getter = (PropertyDescriptor) names.get(setter.getName()); + if (getter != null) { + MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod()); + MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod()); + if (this.useConverter) { + Type setterType = write.getSignature().getArgumentTypes()[0]; + e.load_local(targetLocal); + e.load_arg(2); + e.load_local(sourceLocal); + e.invoke(read); + e.box(read.getSignature().getReturnType()); + EmitUtils.load_class(e, setterType); + e.push(write.getSignature().getName()); + e.invoke_interface(CONVERTER, CONVERT); + e.unbox_or_zero(setterType); + e.invoke(write); + } else if (compatible(getter, setter)) { + // 支持链式 bean + e.load_local(targetLocal); + e.load_local(sourceLocal); + e.invoke(read); + e.invoke(write); + } + } + } + e.return_value(); + e.end_method(); + ce.end_class(); + } + + /** + * Compatible boolean + * + * @param getter getter + * @param setter setter + * @return the boolean + * @since 1.0.0 + */ + private static boolean compatible(@NotNull PropertyDescriptor getter, @NotNull PropertyDescriptor setter) { + return setter.getPropertyType().isAssignableFrom(getter.getPropertyType()); + } + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/support/BeanProperty.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/support/BeanProperty.java new file mode 100644 index 0000000..f4a4eea --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/support/BeanProperty.java @@ -0,0 +1,22 @@ +package com.aivfo.el.start.core.support; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: Bean属性 + */ +@Getter +@AllArgsConstructor +public class BeanProperty { + /** + * Name + */ + private final String name; + /** + * Type + */ + private final Class type; +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/support/ChainMap.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/support/ChainMap.java new file mode 100644 index 0000000..9e0334e --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/support/ChainMap.java @@ -0,0 +1,260 @@ +package com.aivfo.el.start.core.support; + +import com.aivfo.el.start.core.util.Tools; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.util.LinkedCaseInsensitiveMap; + +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Date; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 链式 map (linkedmap) key 必须为 string + */ +@SuppressWarnings("all") +public final class ChainMap extends LinkedCaseInsensitiveMap { + /** + * serialVersionUID + */ + private static final long serialVersionUID = 3992810369797036508L; + + /** + * Chain map + * + * @since 1.0.0 + */ + private ChainMap() { + super(); + } + + /** + * Chain map + * + * @param size size + * @since 1.0.0 + */ + private ChainMap(int size) { + super(Tools.capacity(size)); + } + + /** + * Build chain map. + * + * @return the chain map + * @since 1.0.0 + */ + @NotNull + @Contract(" -> new") + public static ChainMap build() { + return new ChainMap(); + } + + /** + * Build chain map. + * + * @param size the size + * @return the chain map + * @since 1.0.0 + */ + @NotNull + @Contract("_ -> new") + public static ChainMap build(int size) { + return new ChainMap(size); + } + + /** + * 设置列,当键或值为null时忽略 + * + * @param attr 属性 + * @param value 值 + * @return 本身 ignore null + * @since 1.0.0 + */ + @Contract("null, _ -> this; !null, null -> this") + public ChainMap setIgnoreNull(String attr, Object value) { + if (null != attr && null != value) { + put(attr, value); + } + return this; + } + + /** + * 存入 kv + * + * @param attr 属性 + * @param value 值 + * @return 本身 kv + * @since 1.0.0 + */ + @Contract("_, _ -> this") + @NotNull + @Override + public ChainMap put(@NotNull String attr, Object value) { + super.put(attr, value); + return this; + } + + /** + * Clone chain map + * + * @return the chain map + * @since 1.0.0 + */ + @NotNull + @Override + public ChainMap clone() { + return (ChainMap) super.clone(); + } + + /** + * Gets obj. + * + * @param key the key + * @return the obj + * @since 1.0.0 + */ + public Object getObj(String key) { + return super.get(key); + } + + /** + * 获得特定类型值 + * + * @param attr 字段名 + * @return 字段值 str + * @since 1.0.0 + */ + public String getStr(String attr) { + return Tools.toStr(get(attr), null); + } + + /** + * 获得特定类型值 + * + * @param attr 字段名 + * @return 字段值 int + * @since 1.0.0 + */ + @NotNull + public Integer getInt(String attr) { + return Tools.toInt(get(attr), -1); + } + + /** + * 获得特定类型值 + * + * @param attr 字段名 + * @return 字段值 long + * @since 1.0.0 + */ + @NotNull + public Long getLong(String attr) { + return Tools.toLong(get(attr), -1L); + } + + /** + * 获得特定类型值 + * + * @param attr 字段名 + * @return 字段值 float + * @since 1.0.0 + */ + public Float getFloat(String attr) { + return Tools.toFloat(get(attr), null); + } + + /** + * Gets double. + * + * @param attr the attr + * @return the double + * @since 1.0.0 + */ + public Double getDouble(String attr) { + return Tools.toDouble(get(attr), null); + } + + /** + * 获得特定类型值 + * + * @param attr 字段名 + * @return 字段值 bool + * @since 1.0.0 + */ + public Boolean getBool(String attr) { + return Tools.toBoolean(get(attr), null); + } + + /** + * 获得特定类型值 + * + * @param attr 字段名 + * @return 字段值 byte [ ] + * @since 1.0.0 + */ + public byte[] getBytes(String attr) { + return get(attr, null); + } + + /** + * 获得特定类型值 + * + * @param 值类型 + * @param attr 字段名 + * @param defaultValue 默认值 + * @return 字段值 t + * @since 1.0.0 + */ + @SuppressWarnings("unchecked") + public T get(String attr, T defaultValue) { + Object result = get(attr); + return (T) (result != null ? result : defaultValue); + } + + /** + * 获得特定类型值 + * + * @param attr 字段名 + * @return 字段值 date + * @since 1.0.0 + */ + public Date getDate(String attr) { + return get(attr, null); + } + + /** + * 获得特定类型值 + * + * @param attr 字段名 + * @return 字段值 time + * @since 1.0.0 + */ + public Time getTime(String attr) { + return get(attr, null); + } + + /** + * 获得特定类型值 + * + * @param attr 字段名 + * @return 字段值 timestamp + * @since 1.0.0 + */ + public Timestamp getTimestamp(String attr) { + return get(attr, null); + } + + /** + * 获得特定类型值 + * + * @param attr 字段名 + * @return 字段值 number + * @since 1.0.0 + */ + public Number getNumber(String attr) { + return get(attr, null); + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/AesUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/AesUtils.java new file mode 100644 index 0000000..4693d7f --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/AesUtils.java @@ -0,0 +1,304 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.starter.base.utils.Charsets; +import com.aivfo.el.starter.base.utils.Exceptions; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.util.Assert; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.util.Arrays; + +/** + * 完全兼容微信所使用的AES加密方式. + * aes的key必须是256byte长 (比如32个字符) ,可以使用AesKit.genAesKey()来生成一组key + */ +@UtilityClass +@SuppressWarnings("all") +public class AesUtils { + + /** + * 默认 16 长度 + */ + private static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; + /** + * MAXIMUM_CAPACITY + */ + private static final int MAXIMUM_CAPACITY = 1 << 30; + + /** + * 返回 2 的整数倍 + * + * @param cap cap + * @return the int + * @since 1.9.0 + */ + private static final int relengh(int cap) { + int n = cap - 1; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; + } + + /** + * Check power of 2 + * + * @param n n + * @return the boolean + * @since 1.9.0 + */ + private boolean checkPowerOf2(int n) { + if (n <= 0) { + return false; + } + int t = n & (n - 1); + return t == 0 ? true : false; + } + + + /** + * Gen aes key string. + * + * @return the string + * @since 1.0.0 + */ + @NotNull + public static String genAesKey() { + return RandomUtils.random(DEFAULT_INITIAL_CAPACITY); + } + + /** + * Gen aes key + * + * @param length length + * @return the string + * @since 1.9.0 + */ + public static String genAesKey(int length) { + return RandomUtils.random(relengh(length)); + } + + /** + * Encrypt byte [ ]. + * + * @param content the content + * @param aesTextKey the aes text key + * @return the byte [ ] + * @since 1.0.0 + */ + public static byte[] encrypt(byte[] content, @NotNull String aesTextKey) { + return encrypt(content, aesTextKey.getBytes(Charsets.UTF_8)); + } + + /** + * Encrypt byte [ ]. + * + * @param content the content + * @param aesKey the aes key + * @return the byte [ ] + * @since 1.0.0 + */ + public static byte[] encrypt(byte[] content, byte[] aesKey) { + Assert.isTrue(aesKey.length >= DEFAULT_INITIAL_CAPACITY && checkPowerOf2(aesKey.length), + "密钥必须为 2 的幂次方且大于等于 16 位"); + try { + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(aesKey, 0, DEFAULT_INITIAL_CAPACITY); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); + return cipher.doFinal(Pkcs7Encoder.encode(content)); + } catch (Exception e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Encrypt byte [ ]. + * + * @param content the content + * @param aesTextKey the aes text key + * @return the byte [ ] + * @since 1.0.0 + */ + public static byte[] encrypt(@NotNull String content, @NotNull String aesTextKey) { + return encrypt(content.getBytes(Charsets.UTF_8), aesTextKey.getBytes(Charsets.UTF_8)); + } + + /** + * Encrypt to str + * + * @param content content + * @param aesTextKey aes text key + * @return the string + * @since 1.9.0 + */ + public static String encryptToStr(@NotNull String content, @NotNull String aesTextKey) { + return Base64Utils.encodeToString(encrypt(content, aesTextKey)); + } + + /** + * Encrypt byte [ ]. + * + * @param content the content + * @param charset the charset + * @param aesTextKey the aes text key + * @return the byte [ ] + * @since 1.0.0 + */ + public static byte[] encrypt(@NotNull String content, java.nio.charset.Charset charset, @NotNull String aesTextKey) { + return encrypt(content.getBytes(charset), aesTextKey.getBytes(Charsets.UTF_8)); + } + + /** + * Decrypt to str + * + * @param content content + * @param aesTextKey aes text key + * @return the string + * @since 1.9.0 + */ + public static String decryptToStr(@NotNull String content, @NotNull String aesTextKey) { + return new String(decrypt(content, aesTextKey)); + } + + /** + * Decrypt + * + * @param content content + * @param aesTextKey aes text key + * @return the byte [ ] + * @since 1.9.0 + */ + public static byte[] decrypt(@NotNull String content, @NotNull String aesTextKey) { + return decrypt(Base64Utils.decodeFromString(content), aesTextKey); + } + + /** + * Decrypt byte [ ]. + * + * @param content the content + * @param aesTextKey the aes text key + * @return the byte [ ] + * @since 1.0.0 + */ + public static byte[] decrypt(byte[] content, @NotNull String aesTextKey) { + return decrypt(content, aesTextKey.getBytes(Charsets.UTF_8)); + } + + /** + * Decrypt byte [ ]. + * + * @param encrypted the encrypted + * @param aesKey the aes key + * @return the byte [ ] + * @since 1.0.0 + */ + public static byte[] decrypt(byte[] encrypted, byte[] aesKey) { + Assert.isTrue(aesKey.length >= DEFAULT_INITIAL_CAPACITY && checkPowerOf2(aesKey.length), + "密钥必须为 2 的幂次方且大于等于 16 位"); + try { + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, DEFAULT_INITIAL_CAPACITY)); + cipher.init(Cipher.DECRYPT_MODE, keySpec, iv); + return Pkcs7Encoder.decode(cipher.doFinal(encrypted)); + } catch (Exception e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Decrypt to str string. + * + * @param content the content + * @param aesTextKey the aes text key + * @return the string + * @since 1.0.0 + */ + @NotNull + @Contract("_, _ -> new") + public static String decryptToStr(byte[] content, @NotNull String aesTextKey) { + return new String(decrypt(content, aesTextKey.getBytes(Charsets.UTF_8)), Charsets.UTF_8); + } + + /** + * Decrypt to str string. + * + * @param content the content + * @param aesTextKey the aes text key + * @param charset the charset + * @return the string + * @since 1.0.0 + */ + @NotNull + @Contract("_, _, _ -> new") + public static String decryptToStr(byte[] content, @NotNull String aesTextKey, java.nio.charset.Charset charset) { + return new String(decrypt(content, aesTextKey.getBytes(Charsets.UTF_8)), charset); + } + + /** + * 提供基于PKCS7算法的加解密接口. + * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.26 21:39 + * @since 1.0.0 + */ + @UtilityClass + static class Pkcs7Encoder { + /** + * The Block size. + */ + static final int BLOCK_SIZE = 32; + + /** + * Encode byte [ ]. + * + * @param src the src + * @return the byte [ ] + * @since 1.0.0 + */ + static byte[] encode(byte[] src) { + int count = src.length; + // 计算需要填充的位数 + int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); + // 获得补位所用的字符 + byte pad = (byte) (amountToPad & 0xFF); + byte[] pads = new byte[amountToPad]; + for (int index = 0; index < amountToPad; index++) { + pads[index] = pad; + } + int length = count + amountToPad; + byte[] dest = new byte[length]; + System.arraycopy(src, 0, dest, 0, count); + System.arraycopy(pads, 0, dest, count, amountToPad); + return dest; + } + + /** + * Decode byte [ ]. + * + * @param decrypted the decrypted + * @return the byte [ ] + * @since 1.0.0 + */ + @Contract(pure = true) + static byte[] decode(byte[] decrypted) { + int pad = (int) decrypted[decrypted.length - 1]; + if (pad < 1 || pad > BLOCK_SIZE) { + pad = 0; + } + if (pad > 0) { + return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); + } + return decrypted; + } + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/AntPathFilter.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/AntPathFilter.java new file mode 100644 index 0000000..cbb2923 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/AntPathFilter.java @@ -0,0 +1,45 @@ +package com.aivfo.el.start.core.util; + +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +import java.io.File; +import java.io.FileFilter; +import java.io.Serializable; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: spring AntPath 规则文件过滤 + */ +@RequiredArgsConstructor +public class AntPathFilter implements FileFilter, Serializable { + /** + * serialVersionUID + */ + private static final long serialVersionUID = 812598009067554612L; + /** + * PATH_MATCHER + */ + private static final PathMatcher PATH_MATCHER = new AntPathMatcher(); + + /** + * Pattern + */ + private final String pattern; + + /** + * Accept boolean + * + * @param pathname pathname + * @return the boolean + * @since 1.0.0 + */ + @Override + public boolean accept(@NotNull File pathname) { + String filePath = pathname.getAbsolutePath(); + return PATH_MATCHER.match(pattern, filePath); + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/Base64Utils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/Base64Utils.java new file mode 100644 index 0000000..3126060 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/Base64Utils.java @@ -0,0 +1,124 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.starter.base.utils.Charsets; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: Base64工具 + */ +@UtilityClass +@SuppressWarnings("all") +public class Base64Utils extends org.springframework.util.Base64Utils { + + /** + * 编码 + * + * @param value 字符串 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String encode(String value) { + return Base64Utils.encode(value, Charsets.UTF_8); + } + + /** + * 编码 + * + * @param value 字符串 + * @param charset 字符集 + * @return {String} + * @since 1.0.0 + */ + @NotNull + @Contract("_, _ -> new") + public static String encode(@NotNull String value, java.nio.charset.Charset charset) { + byte[] val = value.getBytes(charset); + return new String(Base64Utils.encode(val), charset); + } + + /** + * 编码URL安全 + * + * @param value 字符串 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String encodeUrlSafe(String value) { + return Base64Utils.encodeUrlSafe(value, Charsets.UTF_8); + } + + /** + * 编码URL安全 + * + * @param value 字符串 + * @param charset 字符集 + * @return {String} + * @since 1.0.0 + */ + @NotNull + @Contract("_, _ -> new") + public static String encodeUrlSafe(@NotNull String value, java.nio.charset.Charset charset) { + byte[] val = value.getBytes(charset); + return new String(Base64Utils.encodeUrlSafe(val), charset); + } + + /** + * 解码 + * + * @param value 字符串 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String decode(String value) { + return Base64Utils.decode(value, Charsets.UTF_8); + } + + /** + * 解码 + * + * @param value 字符串 + * @param charset 字符集 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String decode(@NotNull String value, java.nio.charset.Charset charset) { + byte[] val = value.getBytes(charset); + byte[] decodedValue = Base64Utils.decode(val); + return new String(decodedValue, charset); + } + + /** + * 解码URL安全 + * + * @param value 字符串 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String decodeUrlSafe(String value) { + return Base64Utils.decodeUrlSafe(value, Charsets.UTF_8); + } + + /** + * 解码URL安全 + * + * @param value 字符串 + * @param charset 字符集 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String decodeUrlSafe(@NotNull String value, java.nio.charset.Charset charset) { + byte[] val = value.getBytes(charset); + byte[] decodedValue = Base64Utils.decodeUrlSafe(val); + return new String(decodedValue, charset); + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/BeanUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/BeanUtils.java new file mode 100644 index 0000000..f51f038 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/BeanUtils.java @@ -0,0 +1,460 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.start.core.convert.CustomConverter; +import com.aivfo.el.start.core.exception.BaseException; +import com.aivfo.el.start.core.support.BaseBeanCopier; +import com.aivfo.el.start.core.support.BeanProperty; +import com.google.common.collect.Maps; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.BeansException; +import org.springframework.cglib.beans.BeanGenerator; +import org.springframework.cglib.beans.BeanMap; +import org.springframework.cglib.core.CodeGenerationException; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +import java.beans.PropertyDescriptor; +import java.util.*; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 实体工具类 + */ +@SuppressWarnings("all") +@UtilityClass +public class BeanUtils extends org.springframework.beans.BeanUtils { + + /** + * 实例化对象 + * + * @param 泛型标记 + * @param clazzStr 类名 + * @return 对象 t + * @since 1.0.0 + */ + @NotNull + public static T newInstance(String clazzStr) { + try { + Class clazz = Class.forName(clazzStr); + return newInstance(clazz); + } catch (ClassNotFoundException e) { + throw new BaseException(e); + } + } + + /** + * 实例化对象 + * + * @param 泛型标记 + * @param clazz 类 + * @return 对象 t + * @since 1.0.0 + */ + @NotNull + public static T newInstance(Class clazz) { + return (T) instantiateClass(clazz); + } + + /** + * 获取Bean的属性 + * + * @param bean bean + * @param propertyName 属性名 + * @return 属性值 property + * @since 1.0.0 + */ + public static Object getProperty(Object bean, String propertyName) { + Assert.notNull(bean, "bean Could not null"); + return BeanMap.create(bean).get(propertyName); + } + + /** + * 设置Bean属性 + * + * @param bean bean + * @param propertyName 属性名 + * @param value 属性值 + * @since 1.0.0 + */ + public static void setProperty(Object bean, String propertyName, Object value) { + Assert.notNull(bean, "bean Could not null"); + BeanMap.create(bean).put(propertyName, value); + } + + /** + * 深复制 + * 注意: 不支持链式Bean + * + * @param 泛型标记 + * @param source 源对象 + * @return T t + * @since 1.0.0 + */ + public static T clone(T source) { + return (T) BeanUtils.copy(source, source.getClass()); + } + + /** + * copy 对象属性到另一个对象,默认不使用Convert + * 注意: 不支持链式Bean,链式用 copyProperties + * + * @param 泛型标记 + * @param source 源对象 + * @param clazz 类名 + * @return T t + * @since 1.0.0 + */ + @Contract("null, _ -> null") + public static T copy(Object source, Class clazz) { + if (source == null) { + return null; + } + return BeanUtils.copy(source, source.getClass(), clazz); + } + + /** + * copy 对象属性,默认不使用Convert + *

+ * 支持 map bean copy + *

+ * + * @param 泛型标记 + * @param source 源对象 + * @param sourceClazz 源类型 + * @param targetClazz 转换成的类型 + * @return T t + * @since 1.0.0 + */ + @Contract("null, _, _ -> null") + public static T copy(Object source, Class sourceClazz, Class targetClazz) { + BaseBeanCopier copier = BaseBeanCopier.create(sourceClazz, targetClazz, false); + T to = newInstance(targetClazz); + copier.copy(source, to, null); + return to; + } + + /** + * copy 列表对象,默认不使用Convert + *

+ * 支持 map bean copy + *

+ * + * @param 泛型标记 + * @param sourceList 源列表 + * @param targetClazz 转换成的类型 + * @return T list + * @since 1.0.0 + */ + @Contract("null, _ -> !null") + public static List copy(@Nullable Collection sourceList, Class targetClazz) { + if (sourceList == null || sourceList.isEmpty()) { + return Collections.emptyList(); + } + List outList = new ArrayList<>(sourceList.size()); + Class sourceClazz = null; + for (Object source : sourceList) { + if (source == null) { + continue; + } + if (sourceClazz == null) { + sourceClazz = source.getClass(); + } + T bean = BeanUtils.copy(source, sourceClazz, targetClazz); + outList.add(bean); + } + return outList; + } + + /** + * 拷贝对象并对不同类型属性进行转换 + *

+ * 支持 map bean copy + *

+ * + * @param 泛型标记 + * @param source 源对象 + * @param targetClazz 转换成的类 + * @return T t + * @since 1.0.0 + */ + @Contract("null, _ -> null") + @Nullable + public static T copyWithConvert(@Nullable Object source, Class targetClazz) { + if (source == null) { + return null; + } + return BeanUtils.copyWithConvert(source, source.getClass(), targetClazz); + } + + /** + * 拷贝对象并对不同类型属性进行转换 + *

+ * 支持 map bean copy + *

+ * + * @param 泛型标记 + * @param source 源对象 + * @param sourceClazz 源类 + * @param targetClazz 转换成的类 + * @return T t + * @since 1.0.0 + */ + @Contract("null, _, _ -> null") + @Nullable + public static T copyWithConvert(@Nullable Object source, Class sourceClazz, Class targetClazz) { + if (source == null) { + return null; + } + BaseBeanCopier copier = BaseBeanCopier.create(sourceClazz, targetClazz, true); + T to = newInstance(targetClazz); + copier.copy(source, to, new CustomConverter(sourceClazz, targetClazz)); + return to; + } + + /** + * 拷贝列表并对不同类型属性进行转换 + *

+ * 支持 map bean copy + *

+ * + * @param 泛型标记 + * @param sourceList 源对象列表 + * @param targetClazz 转换成的类 + * @return List list + * @since 1.0.0 + */ + @Contract("null, _ -> !null") + public static List copyWithConvert(@Nullable Collection sourceList, Class targetClazz) { + if (sourceList == null || sourceList.isEmpty()) { + return Collections.emptyList(); + } + List outList = new ArrayList<>(sourceList.size()); + Class sourceClazz = null; + for (Object source : sourceList) { + if (source == null) { + continue; + } + if (sourceClazz == null) { + sourceClazz = source.getClass(); + } + T bean = BeanUtils.copyWithConvert(source, sourceClazz, targetClazz); + outList.add(bean); + } + return outList; + } + + /** + * Copy the property values of the given source bean into the target class. + *

Note: The source and target classes do not have to match or even be derived + * from each other, as long as the properties match. Any bean properties that the + * source bean exposes but the target bean does not will silently be ignored. + *

This is just a convenience method. For more complex transfer needs, + * + * @param 泛型标记 + * @param sourceList the source list bean + * @param targetClazz the target bean class + * @return List list + * @throws BeansException if the copying failed + * @since 1.0.0 + */ + @Contract("null, _ -> !null") + public static List copyProperties(@Nullable Collection sourceList, Class targetClazz) { + if (sourceList == null || sourceList.isEmpty()) { + return Collections.emptyList(); + } + List outList = new ArrayList<>(sourceList.size()); + for (Object source : sourceList) { + if (source == null) { + continue; + } + T bean = BeanUtils.copyProperties(source, targetClazz); + outList.add(bean); + } + return outList; + } + + /** + * Copy the property values of the given source bean into the target class. + *

Note: The source and target classes do not have to match or even be derived + * from each other, as long as the properties match. Any bean properties that the + * source bean exposes but the target bean does not will silently be ignored. + *

This is just a convenience method. For more complex transfer needs, + * + * @param 泛型标记 + * @param source the source bean + * @param targetClazz the target bean class + * @return T t + * @throws BeansException if the copying failed + * @since 1.0.0 + */ + @Contract("null, _ -> null") + @Nullable + public static T copyProperties(@Nullable Object source, Class targetClazz) { + if (source == null) { + return null; + } + T to = newInstance(targetClazz); + BeanUtils.copyProperties(source, to); + return to; + } + + /** + * 将对象装成 map 形式. + * 注意: 返回的 Map 不能使用 put 添加键值对, 因为 BeanMap 未做实现, 所以不会添加成功, 必须使用强转为 BeanMap 后才使用 put + * + * @param bean 源对象 + * @return {Map} + * @since 1.0.0 + */ + @Contract("null -> new") + @SuppressWarnings("unchecked") + public static Map toMap(Object bean) { + if (bean == null) { + return Maps.newHashMapWithExpectedSize(0); + } + return BeanMap.create(bean); + } + + /** + * To string map map + * + * @param bean bean + * @return the map + * @since 1.0.0 + */ + @Contract("null -> !null") + public static Map toStringMap(Object bean) { + Map map = toMap(bean); + + Map stringMap = Maps.newHashMapWithExpectedSize(map.size()); + map.forEach((k, v) -> { + if (ObjectUtils.isNotNull(v)) { + stringMap.put(k, String.valueOf(v)); + } + }); + return stringMap; + } + + /** + * 将map 转为 bean + * + * @param 泛型标记 + * @param beanMap map + * @param valueType 对象类型 + * @return {T} + * @since 1.0.0 + */ + public static T toBean(Map beanMap, Class valueType) { + Objects.requireNonNull(beanMap, "beanMap Could not null"); + T bean = BeanUtils.newInstance(valueType); + if (beanMap.isEmpty()) { + return bean; + } + BeanMap.create(bean).putAll(beanMap); + return bean; + } + + /** + * 给一个Bean添加字段 + * + * @param superBean 父级Bean + * @param props 新增属性 + * @return {Object} + * @since 1.0.0 + */ + public static Object generator(@NotNull Object superBean, BeanProperty... props) { + Class superclass = superBean.getClass(); + Object genBean = generator(superclass, props); + BeanUtils.copy(superBean, genBean); + return genBean; + } + + /** + * 给一个class添加字段 + * + * @param superclass 父级 + * @param props 新增属性 + * @return {Object} + * @since 1.0.0 + */ + public static Object generator(Class superclass, @NotNull BeanProperty... props) { + BeanGenerator generator = new BeanGenerator(); + generator.setSuperclass(superclass); + generator.setUseCache(true); + for (BeanProperty prop : props) { + generator.addProperty(prop.getName(), prop.getType()); + } + return generator.create(); + } + + /** + * 拷贝对象 + * 注意: 不支持链式Bean,链式用 copyProperties + * + * @param source 源对象 + * @param targetBean 需要赋值的对象 + * @since 1.0.0 + */ + public static void copy(@NotNull Object source, @NotNull Object targetBean) { + BaseBeanCopier copier = BaseBeanCopier + .create(source.getClass(), targetBean.getClass(), false); + + copier.copy(source, targetBean, null); + } + + /** + * 获取 Bean 的所有 get方法 + * + * @param type 类 + * @return PropertyDescriptor数组 property descriptor [ ] + * @since 1.0.0 + */ + public static PropertyDescriptor[] getBeanGetters(Class type) { + return getPropertiesHelper(type, true, false); + } + + /** + * Get properties helper property descriptor [ ] + * + * @param type type + * @param read read + * @param write write + * @return the property descriptor [ ] + * @since 1.0.0 + */ + private static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) { + try { + PropertyDescriptor[] all = BeanUtils.getPropertyDescriptors(type); + if (read && write) { + return all; + } else { + List properties = new ArrayList<>(all.length); + for (PropertyDescriptor pd : all) { + if (read && pd.getReadMethod() != null) { + properties.add(pd); + } else if (write && pd.getWriteMethod() != null) { + properties.add(pd); + } + } + return properties.toArray(new PropertyDescriptor[0]); + } + } catch (BeansException ex) { + throw new CodeGenerationException(ex); + } + } + + /** + * 获取 Bean 的所有 set方法 + * + * @param type 类 + * @return PropertyDescriptor数组 property descriptor [ ] + * @since 1.0.0 + */ + public static PropertyDescriptor[] getBeanSetters(Class type) { + return getPropertiesHelper(type, false, true); + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/CollectionUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/CollectionUtils.java new file mode 100644 index 0000000..0dc1701 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/CollectionUtils.java @@ -0,0 +1,303 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.starter.base.utils.StringUtils; +import com.aivfo.el.starter.core.util.DataTypeUtils; +import com.google.common.collect.Maps; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Array; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 集合工具类 + */ +@UtilityClass +@SuppressWarnings("java:S2176") +public class CollectionUtils extends org.springframework.util.CollectionUtils { + + /** + * THREAD_LOCAL_RANDOM + */ + public static final ThreadLocalRandom THREAD_LOCAL_RANDOM = ThreadLocalRandom.current(); + + /** + * Return {@code true} if the supplied Collection is not {@code null} or empty. + * Otherwise, return {@code false}. + * + * @param collection the Collection to check + * @return whether the given Collection is not empty + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isNotEmpty(@Nullable Collection collection) { + return !isEmpty(collection); + } + + /** + * Return {@code true} if the supplied Map is not {@code null} or empty. + * Otherwise, return {@code false}. + * + * @param map the Map to check + * @return whether the given Map is not empty + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isNotEmpty(@Nullable Map map) { + return !isEmpty(map); + } + + /** + * Check whether the given Array contains the given element. + * + * @param The generic tag + * @param array the Array to check + * @param element the element to look for + * @return {@code true} if found, {@code false} else + * @since 1.0.0 + */ + @Contract("null, _ -> false") + public static boolean contains(@Nullable T[] array, T element) { + if (array == null) { + return false; + } + return Arrays.stream(array).anyMatch(x -> ObjectUtils.nullSafeEquals(x, element)); + } + + /** + * Concatenates 2 arrays + * + * @param one 数组1 + * @param other 数组2 + * @return 新数组 string [ ] + * @since 1.0.0 + */ + public static String[] concat(String[] one, String[] other) { + return concat(one, other, String.class); + } + + /** + * Concatenates 2 arrays + * + * @param the type parameter + * @param one 数组1 + * @param other 数组2 + * @param clazz 数组类 + * @return 新数组 t [ ] + * @since 1.0.0 + */ + @SuppressWarnings("unchecked") + public static T[] concat(@NotNull T[] one, @NotNull T[] other, Class clazz) { + T[] target = (T[]) Array.newInstance(clazz, one.length + other.length); + System.arraycopy(one, 0, target, 0, one.length); + System.arraycopy(other, 0, target, one.length, other.length); + return target; + } + + /** + * 不可变 Set + * + * @param 泛型 + * @param es 对象 + * @return 集合 set + * @since 1.0.0 + */ + public static Set ofImmutableSet(E[] es) { + Objects.requireNonNull(es, "args es is null."); + return Arrays.stream(es).collect(Collectors.toSet()); + } + + /** + * 不可变 List + * + * @param 泛型 + * @param es 对象 + * @return 集合 list + * @since 1.0.0 + */ + public static List ofImmutableList(E[] es) { + Objects.requireNonNull(es, "args es is null."); + return Arrays.stream(es).collect(Collectors.toList()); + } + + /** + * Iterable 转换为List集合 + * + * @param 泛型 + * @param elements Iterable + * @return 集合 list + * @since 1.0.0 + */ + public static List toList(Iterable elements) { + Objects.requireNonNull(elements, "elements es is null."); + if (elements instanceof Collection) { + return new ArrayList<>((Collection) elements); + } + Iterator iterator = elements.iterator(); + List list = new ArrayList<>(); + while (iterator.hasNext()) { + list.add(iterator.next()); + } + return list; + } + + /** + * 将key value 数组转为 map + * + * @param key + * @param value + * @param keysValues key value 数组 + * @return map 集合 + * @since 1.0.0 + */ + @SuppressWarnings("unchecked") + public static Map toMap(@NotNull Object... keysValues) { + int separate = 2; + if (keysValues.length % separate != 0) { + throw new IllegalArgumentException("wrong number of arguments for met, keysValues length can not be odd"); + } + Map keyValueMap = Maps.newHashMapWithExpectedSize(keysValues.length / 2); + for (int i = keysValues.length - separate; i >= 0; i -= separate) { + keyValueMap.put((K) keysValues[i], (V) keysValues[i + 1]); + } + return keyValueMap; + } + + /** + * 对象是否为数组对象 + * + * @param obj 对象 + * @return 是否为数组对象 ,如果为{@code null} 返回false + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isArray(Object obj) { + if (null == obj) { + return false; + } + return obj.getClass().isArray(); + } + + /** + * Find value of type t + * + * @param parameter + * @param map map + * @param key key + * @param type type + * @return the t + * @since 1.0.0 + */ + @Contract("null, _, _ -> null") + @Nullable + public static T findValueOfType(Map map, String key, @Nullable Class type) { + if (map == null || !map.containsKey(key) || StringUtils.isBlank(String.valueOf(map.get(key)))) { + return null; + } + return DataTypeUtils.convert(type, map.get(key)); + } + + /** + * 通过 map 的 value 排序 + * + * @param parameter + * @param parameter + * @param map map + * @param asc asc + * @return the map + * @version jdk1.8 + * @since 1.0.0 + */ + public static > Map sortByValue(@NotNull Map map, boolean asc) { + Map result = new LinkedHashMap<>(); + Stream> stream = map.entrySet().stream(); + // 升序 + if (asc) { + stream.sorted(Map.Entry.comparingByValue()) + .forEachOrdered(e -> result.put(e.getKey(), e.getValue())); + } else { + // 降序 + stream.sorted(Map.Entry.comparingByValue().reversed()) + .forEachOrdered(e -> result.put(e.getKey(), e.getValue())); + } + return result; + } + + /** + * 通过 map 的 key 排序 + * + * @param parameter + * @param parameter + * @param map map + * @param asc asc + * @return the map + * @version jdk1.8 + * @since 1.0.0 + */ + public static , V> Map sortByKey(@NotNull Map map, boolean asc) { + Map result = new LinkedHashMap<>(); + Stream> stream = map.entrySet().stream(); + if (asc) { + stream.sorted(Map.Entry.comparingByKey()) + .forEachOrdered(e -> result.put(e.getKey(), e.getValue())); + } else { + stream.sorted(Map.Entry.comparingByKey().reversed()) + .forEachOrdered(e -> result.put(e.getKey(), e.getValue())); + } + return result; + } + + /** + * 获取多条集合中随机值 + * + * @param parameter + * @param list list + * @param num num + * @return the random list + * @Description list 随机取数据 + * @params list list集合 num 随机取多少条 + * @since 2.0.0 + */ + public static List getRandomList(@NotNull List list, int num) { + List randomList = new ArrayList<>(); + if (list.size() <= num) { + return list; + } else { + for (int i = 0; i < num; i++) { + int intRandom = THREAD_LOCAL_RANDOM.nextInt(list.size() - 1); + randomList.add(list.get(intRandom)); + list.remove(list.get(intRandom)); + } + return randomList; + } + } + + /** + * 获取集合中随机值 + * + * @param parameter + * @param list list + * @return the random list + * @Description list 随机取数据 + * @params list list集合 num 随机取多少条 + * @since 2.0.0 + */ + public static T getRandom(@NotNull List list) { + if (isEmpty(list)) { + return null; + } + int size = list.size(); + if (size == 1) { + return list.get(0); + } + return list.get(THREAD_LOCAL_RANDOM.nextInt(size - 1)); + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ConvertUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ConvertUtils.java new file mode 100644 index 0000000..7ddabf9 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ConvertUtils.java @@ -0,0 +1,91 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.start.core.convert.CustomConversionService; +import com.aivfo.el.starter.base.utils.ClassUtils; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.lang.Nullable; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 基于 spring ConversionService 类型转换 + */ +@UtilityClass +@SuppressWarnings("unchecked") +public class ConvertUtils { + + /** + * Convenience operation for converting a source object to the specified targetType. + * {@link TypeDescriptor#forObject(Object)}. + * + * @param 泛型标记 + * @param source the source object + * @param targetType the target type + * @return the converted value + * @throws IllegalArgumentException if targetType is {@code null}, or sourceType is {@code null} but source is not {@code null} + * @since 1.0.0 + */ + @Contract("null, _ -> null") + @Nullable + public static T convert(@Nullable Object source, Class targetType) { + if (source == null) { + return null; + } + if (ClassUtils.isAssignableValue(targetType, source)) { + return (T) source; + } + GenericConversionService conversionService = CustomConversionService.getInstance(); + return conversionService.convert(source, targetType); + } + + /** + * Convenience operation for converting a source object to the specified targetType, + * where the target type is a descriptor that provides additional conversion context. + * {@link TypeDescriptor#forObject(Object)}. + * + * @param 泛型标记 + * @param source the source object + * @param sourceType the source type + * @param targetType the target type + * @return the converted value + * @throws IllegalArgumentException if targetType is {@code null}, or sourceType is {@code null} but source is not {@code null} + * @since 1.0.0 + */ + @Contract("null, _, _ -> null") + @Nullable + public static T convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + if (source == null) { + return null; + } + GenericConversionService conversionService = CustomConversionService.getInstance(); + return (T) conversionService.convert(source, sourceType, targetType); + } + + /** + * Convenience operation for converting a source object to the specified targetType, + * where the target type is a descriptor that provides additional conversion context. + * Simply delegates to {@link #convert(Object, TypeDescriptor, TypeDescriptor)} and + * encapsulates the construction of the source type descriptor using + * {@link TypeDescriptor#forObject(Object)}. + * + * @param 泛型标记 + * @param source the source object + * @param targetType the target type + * @return the converted value + * @throws IllegalArgumentException if targetType is {@code null}, or sourceType is {@code null} but source is not {@code null} + * @since 1.0.0 + */ + @Contract("null, _ -> null") + @Nullable + public static T convert(@Nullable Object source, TypeDescriptor targetType) { + if (source == null) { + return null; + } + GenericConversionService conversionService = CustomConversionService.getInstance(); + return (T) conversionService.convert(source, targetType); + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/DateTimeUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/DateTimeUtils.java new file mode 100644 index 0000000..c91a70f --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/DateTimeUtils.java @@ -0,0 +1,130 @@ +package com.aivfo.el.start.core.util; + +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.NotNull; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: DateTime + */ +@UtilityClass +public class DateTimeUtils { + /** + * The constant DATETIME_FORMAT. + */ + public static final DateTimeFormatter DATETIME_FORMAT = DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATETIME); + /** + * The constant DATE_FORMAT. + */ + public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATE); + /** + * The constant TIME_FORMAT. + */ + public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern(DateUtils.PATTERN_TIME); + + /** + * 日期时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatDateTime(TemporalAccessor temporal) { + return DATETIME_FORMAT.format(temporal); + } + + /** + * 日期时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatDate(TemporalAccessor temporal) { + return DATE_FORMAT.format(temporal); + } + + /** + * 时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatTime(TemporalAccessor temporal) { + return TIME_FORMAT.format(temporal); + } + + /** + * 日期格式化 + * + * @param temporal 时间 + * @param pattern 表达式 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String format(TemporalAccessor temporal, String pattern) { + return DateTimeFormatter.ofPattern(pattern).format(temporal); + } + + /** + * 将字符串转换为时间 + * + * @param dateStr 时间字符串 + * @param pattern 表达式 + * @return 时间 temporal accessor + * @since 1.0.0 + */ + @NotNull + public static TemporalAccessor parse(String dateStr, String pattern) { + DateTimeFormatter format = DateTimeFormatter.ofPattern(pattern); + return format.parse(dateStr); + } + + /** + * 将字符串转换为时间 + * + * @param dateStr 时间字符串 + * @param formatter DateTimeFormatter + * @return 时间 temporal accessor + * @since 1.0.0 + */ + @NotNull + public static TemporalAccessor parse(String dateStr, @NotNull DateTimeFormatter formatter) { + return formatter.parse(dateStr); + } + + /** + * 时间转 Instant + * + * @param dateTime 时间 + * @return Instant instant + * @since 1.0.0 + */ + public static Instant toInstant(@NotNull LocalDateTime dateTime) { + return dateTime.atZone(ZoneId.systemDefault()).toInstant(); + } + + /** + * Instant 转 时间 + * + * @param instant Instant + * @return Instant local date time + * @since 1.0.0 + */ + @NotNull + public static LocalDateTime toDateTime(Instant instant) { + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/DateUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/DateUtils.java new file mode 100644 index 0000000..86901ae --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/DateUtils.java @@ -0,0 +1,857 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.starter.base.asserts.Assertions; +import com.aivfo.el.starter.base.constant.ConfigDefaultValue; +import com.aivfo.el.starter.base.utils.ConcurrentDateFormat; +import com.aivfo.el.starter.base.utils.Exceptions; +import com.aivfo.el.starter.base.utils.StringPool; +import com.aivfo.el.starter.base.utils.StringUtils; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.util.Assert; + +import java.text.ParseException; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAmount; +import java.time.temporal.TemporalQuery; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 日期工具类 + */ +@UtilityClass +@SuppressWarnings("checkstyle:MethodLimit") +public class DateUtils { + + /** + * PATTERN_MS_DATETIME + */ + public static final String PATTERN_MS_DATETIME = ConfigDefaultValue.DEFAULT_DATE_FORMAT.concat(":SSS"); + /** + * The constant PATTERN_DATETIME. + */ + public static final String PATTERN_DATETIME = ConfigDefaultValue.DEFAULT_DATE_FORMAT; + /** + * The constant PATTERN_DATE. + */ + public static final String PATTERN_DATE = "yyyy-MM-dd"; + /** + * The constant PATTERN_DATE_HOUR. + */ + public static final String PATTERN_DATE_HOUR = "yyyy-MM-dd HH"; + /** + * PATTERN_MONTH + */ + public static final String PATTERN_MONTH = "yyyyMM"; + /** + * 时分秒 + */ + public static final String PATTERN_TIME = "HH:mm:ss"; + /** + * PATTERN_DATE_NO_SEPARATOR + */ + public static final String PATTERN_DATE_NO_SEPARATOR = "yyyyMMdd"; + /** + * PATTERN_TIME_NO_SEPARATOR + */ + public static final String PATTERN_TIME_NO_SEPARATOR = "HHmmss"; + /** + * PATTERN_DATETIME_NO_SEPARATOR + */ + public static final String PATTERN_DATETIME_NO_SEPARATOR = PATTERN_DATE_NO_SEPARATOR + PATTERN_TIME_NO_SEPARATOR; + /** + * 老 date 格式化 + */ + public static final ConcurrentDateFormat DATETIME_FORMAT = ConcurrentDateFormat.of(PATTERN_DATETIME); + /** + * DATETIME_FORMAT_NO_SEPARATOR + */ + public static final ConcurrentDateFormat DATETIME_FORMAT_NO_SEPARATOR = ConcurrentDateFormat.of(PATTERN_DATETIME_NO_SEPARATOR); + /** + * The constant DATE_FORMAT. + */ + public static final ConcurrentDateFormat DATE_FORMAT = ConcurrentDateFormat.of(PATTERN_DATE); + /** + * DATE_FORMAT_NO_SEPARATOR + */ + public static final ConcurrentDateFormat DATE_FORMAT_NO_SEPARATOR = ConcurrentDateFormat.of(PATTERN_DATE_NO_SEPARATOR); + /** + * The constant TIME_FORMAT. + */ + public static final ConcurrentDateFormat TIME_FORMAT = ConcurrentDateFormat.of(PATTERN_TIME); + /** + * TIME_FORMAT_NO_SEPARATOR + */ + public static final ConcurrentDateFormat TIME_FORMAT_NO_SEPARATOR = ConcurrentDateFormat.of(PATTERN_TIME_NO_SEPARATOR); + /** + * java 8 时间格式化 + */ + public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATETIME); + /** + * DATETIME_FORMATTER_NO_SEPARATOR + */ + public static final DateTimeFormatter DATETIME_FORMATTER_NO_SEPARATOR = + DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATETIME_NO_SEPARATOR); + /** + * The constant DATE_FORMATTER. + */ + public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATE); + /** + * DATE_FORMATTER_NO_SEPARATOR + */ + public static final DateTimeFormatter DATE_FORMATTER_NO_SEPARATOR = DateTimeFormatter.ofPattern(DateUtils.PATTERN_DATE_NO_SEPARATOR); + /** + * The constant TIME_FORMATTER. + */ + public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtils.PATTERN_TIME); + /** + * TIME_FORMATTER_NO_SEPARATOR + */ + public static final DateTimeFormatter TIME_FORMATTER_NO_SEPARATOR = DateTimeFormatter.ofPattern(DateUtils.PATTERN_TIME_NO_SEPARATOR); + + /** + * 添加年 + * + * @param date 时间 + * @param yearsToAdd 添加的年数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusYears(Date date, int yearsToAdd) { + return DateUtils.set(date, Calendar.YEAR, yearsToAdd); + } + + /** + * 设置日期属性 + * + * @param date 时间 + * @param calendarField 更改的属性 + * @param amount 更改数,-1表示减少 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + private static Date set(Date date, int calendarField, int amount) { + Assert.notNull(date, "The date must not be null"); + Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + /** + * 添加月 + * + * @param date 时间 + * @param monthsToAdd 添加的月数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusMonths(Date date, int monthsToAdd) { + return DateUtils.set(date, Calendar.MONTH, monthsToAdd); + } + + /** + * 添加周 + * + * @param date 时间 + * @param weeksToAdd 添加的周数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusWeeks(Date date, int weeksToAdd) { + return DateUtils.plus(date, Period.ofWeeks(weeksToAdd)); + } + + /** + * 日期添加时间量 + * + * @param date 时间 + * @param amount 时间量 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plus(@NotNull Date date, TemporalAmount amount) { + Instant instant = date.toInstant(); + return Date.from(instant.plus(amount)); + } + + /** + * 添加天 + * + * @param date 时间 + * @param daysToAdd 添加的天数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusDays(Date date, long daysToAdd) { + return DateUtils.plus(date, Duration.ofDays(daysToAdd)); + } + + /** + * 添加小时 + * + * @param date 时间 + * @param hoursToAdd 添加的小时数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusHours(Date date, long hoursToAdd) { + return DateUtils.plus(date, Duration.ofHours(hoursToAdd)); + } + + /** + * 添加分钟 + * + * @param date 时间 + * @param minutesToAdd 添加的分钟数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusMinutes(Date date, long minutesToAdd) { + return DateUtils.plus(date, Duration.ofMinutes(minutesToAdd)); + } + + /** + * 添加秒 + * + * @param date 时间 + * @param secondsToAdd 添加的秒数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusSeconds(Date date, long secondsToAdd) { + return DateUtils.plus(date, Duration.ofSeconds(secondsToAdd)); + } + + /** + * 添加毫秒 + * + * @param date 时间 + * @param millisToAdd 添加的毫秒数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusMillis(Date date, long millisToAdd) { + return DateUtils.plus(date, Duration.ofMillis(millisToAdd)); + } + + /** + * 添加纳秒 + * + * @param date 时间 + * @param nanosToAdd 添加的纳秒数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date plusNanos(Date date, long nanosToAdd) { + return DateUtils.plus(date, Duration.ofNanos(nanosToAdd)); + } + + /** + * 减少年 + * + * @param date 时间 + * @param years 减少的年数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusYears(Date date, int years) { + return DateUtils.set(date, Calendar.YEAR, -years); + } + + /** + * 减少月 + * + * @param date 时间 + * @param months 减少的月数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusMonths(Date date, int months) { + return DateUtils.set(date, Calendar.MONTH, -months); + } + + /** + * 减少周 + * + * @param date 时间 + * @param weeks 减少的周数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusWeeks(Date date, int weeks) { + return DateUtils.minus(date, Period.ofWeeks(weeks)); + } + + /** + * 日期减少时间量 + * + * @param date 时间 + * @param amount 时间量 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minus(@NotNull Date date, TemporalAmount amount) { + Instant instant = date.toInstant(); + return Date.from(instant.minus(amount)); + } + + /** + * 减少天 + * + * @param date 时间 + * @param days 减少的天数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusDays(Date date, long days) { + return DateUtils.minus(date, Duration.ofDays(days)); + } + + /** + * 减少小时 + * + * @param date 时间 + * @param hours 减少的小时数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusHours(Date date, long hours) { + return DateUtils.minus(date, Duration.ofHours(hours)); + } + + /** + * 减少分钟 + * + * @param date 时间 + * @param minutes 减少的分钟数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusMinutes(Date date, long minutes) { + return DateUtils.minus(date, Duration.ofMinutes(minutes)); + } + + /** + * 减少秒 + * + * @param date 时间 + * @param seconds 减少的秒数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusSeconds(Date date, long seconds) { + return DateUtils.minus(date, Duration.ofSeconds(seconds)); + } + + /** + * 减少毫秒 + * + * @param date 时间 + * @param millis 减少的毫秒数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusMillis(Date date, long millis) { + return DateUtils.minus(date, Duration.ofMillis(millis)); + } + + /** + * 减少纳秒 + * + * @param date 时间 + * @param nanos 减少的纳秒数 + * @return 设置后的时间 date + * @since 1.0.0 + */ + @NotNull + public static Date minusNanos(Date date, long nanos) { + return DateUtils.minus(date, Duration.ofNanos(nanos)); + } + + /** + * 日期时间格式化 + * + * @param date 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + public static String formatDateTime(Date date) { + return DATETIME_FORMAT.format(date); + } + + /** + * 日期格式化 + * + * @param date 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + public static String formatDate(Date date) { + return DATE_FORMAT.format(date); + } + + /** + * 时间格式化 + * + * @param date 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + public static String formatTime(Date date) { + return TIME_FORMAT.format(date); + } + + /** + * java8 日期时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatDateTime(TemporalAccessor temporal) { + return DATETIME_FORMATTER.format(temporal); + } + + /** + * java8 日期时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatDate(TemporalAccessor temporal) { + return DATE_FORMATTER.format(temporal); + } + + /** + * java8 时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatTime(TemporalAccessor temporal) { + return TIME_FORMATTER.format(temporal); + } + + /** + * java8 日期格式化 + * + * @param temporal 时间 + * @param pattern 表达式 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String format(TemporalAccessor temporal, String pattern) { + return DateTimeFormatter.ofPattern(pattern).format(temporal); + } + + /** + * Parse date time + * + * @param dateStr date str + * @return the date + * @since 1.6.0 + */ + public static Date parseDateTime(String dateStr) { + return parse(dateStr, PATTERN_DATETIME); + } + + /** + * 将字符串转换为时间 + * + * @param dateStr 时间字符串 + * @param pattern 表达式 + * @return 时间 date + * @since 1.0.0 + */ + @NotNull + public static Date parse(String dateStr, String pattern) { + Assertions.notBlank(dateStr, "参数错误, 时间字符串不能为空"); + ConcurrentDateFormat format = ConcurrentDateFormat.of(pattern); + try { + return format.parse(dateStr); + } catch (ParseException e) { + throw Exceptions.unchecked(StringUtils.format("不能将 {} 以 {} 格式转换为 Date 类型", dateStr, pattern), e); + } + } + + /** + * 将字符串转换为时间 + * + * @param dateStr 时间字符串 + * @param format ConcurrentDateFormat + * @return 时间 date + * @since 1.0.0 + */ + public static Date parse(String dateStr, @NotNull ConcurrentDateFormat format) { + try { + return format.parse(dateStr); + } catch (ParseException e) { + throw Exceptions.unchecked(StringUtils.format("不能将 {} 以 {} 格式转换为 Date 类型", dateStr, format.getFormat()), e); + } + } + + /** + * 将字符串转换为时间 + * + * @param the type parameter + * @param dateStr 时间字符串 + * @param pattern 表达式 + * @param query the query + * @return 时间 t + * @since 1.0.0 + */ + public static T parse(String dateStr, String pattern, TemporalQuery query) { + return DateTimeFormatter.ofPattern(pattern).parse(dateStr, query); + } + + /** + * Instant 转 时间 + * + * @param instant Instant + * @return Instant local date time + * @since 1.0.0 + */ + @NotNull + public static LocalDateTime toDateTime(Instant instant) { + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + } + + /** + * 转换成 date + * + * @param dateTime LocalDateTime + * @return Date date + * @since 1.0.0 + */ + @NotNull + public static Date toDate(LocalDateTime dateTime) { + return Date.from(DateUtils.toInstant(dateTime)); + } + + /** + * 时间转 Instant + * + * @param dateTime 时间 + * @return Instant instant + * @since 1.0.0 + */ + public static Instant toInstant(@NotNull LocalDateTime dateTime) { + return dateTime.atZone(ZoneId.systemDefault()).toInstant(); + } + + /** + * 转换成 date + * + * @param localDate LocalDate + * @return Date date + * @since 1.0.0 + */ + @NotNull + public static Date toDate(@NotNull LocalDate localDate) { + return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + } + + /** + * Converts local date time to Calendar. + * + * @param localDateTime the local date time + * @return the calendar + * @since 1.0.0 + */ + @NotNull + public static Calendar toCalendar(LocalDateTime localDateTime) { + return GregorianCalendar.from(ZonedDateTime.of(localDateTime, ZoneId.systemDefault())); + } + + /** + * localDate 转换成毫秒数 + * + * @param localDate LocalDate + * @return long long + * @since 1.0.0 + */ + public static long toMilliseconds(@NotNull LocalDate localDate) { + return toMilliseconds(localDate.atStartOfDay()); + } + + /** + * localDateTime 转换成毫秒数 + * + * @param localDateTime LocalDateTime + * @return long long + * @since 1.0.0 + */ + public static long toMilliseconds(@NotNull LocalDateTime localDateTime) { + return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + /** + * 转换成java8 时间 + * + * @param calendar 日历 + * @return LocalDateTime local date time + * @since 1.0.0 + */ + @NotNull + public static LocalDateTime fromCalendar(@NotNull Calendar calendar) { + TimeZone tz = calendar.getTimeZone(); + ZoneId zid = tz == null ? ZoneId.systemDefault() : tz.toZoneId(); + return LocalDateTime.ofInstant(calendar.toInstant(), zid); + } + + /** + * 转换成java8 时间 + * + * @param instant Instant + * @return LocalDateTime local date time + * @since 1.0.0 + */ + @NotNull + public static LocalDateTime fromInstant(Instant instant) { + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + } + + /** + * 转换成java8 时间 + * + * @param date Date + * @return LocalDateTime local date time + * @since 1.0.0 + */ + @NotNull + public static LocalDateTime fromDate(@NotNull Date date) { + return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); + } + + /** + * 转换成java8 时间 + * + * @param milliseconds 毫秒数 + * @return LocalDateTime local date time + * @since 1.0.0 + */ + @NotNull + public static LocalDateTime fromMilliseconds(long milliseconds) { + return LocalDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), ZoneId.systemDefault()); + } + + /** + * 比较2个时间差,跨度比较小 + * + * @param startInclusive 开始时间 + * @param endExclusive 结束时间 + * @return 时间间隔 duration + * @since 1.0.0 + */ + public static Duration between(Temporal startInclusive, Temporal endExclusive) { + return Duration.between(startInclusive, endExclusive); + } + + /** + * 比较2个时间差,跨度比较大,年月日为单位 + * + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return 时间间隔 period + * @since 1.0.0 + */ + public static Period between(LocalDate startDate, LocalDate endDate) { + return Period.between(startDate, endDate); + } + + /** + * 比较2个 时间差 + * + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return 时间间隔 duration + * @since 1.0.0 + */ + public static Duration between(@NotNull Date startDate, @NotNull Date endDate) { + return Duration.between(startDate.toInstant(), endDate.toInstant()); + } + + /** + * Between + * + * @param startTime start time + * @param targetDate target date + * @param endTime end time + * @return the boolean + * @since 1.6.0 + */ + public static boolean between(Date startTime, Date targetDate, Date endTime) { + return startTime.getTime() <= targetDate.getTime() + && endTime.getTime() >= targetDate.getTime(); + } + + /** + * 将秒数转换为日时分秒 + * + * @param second 秒数 + * @return 时间 string + * @since 1.0.0 + */ + public static String secondToTime(Long second) { + // 判断是否为空 + if (second == null || second == 0L) { + return StringPool.EMPTY; + } + //转换天数 + long days = second / 86400; + //剩余秒数 + second = second % 86400; + //转换小时 + long hours = second / 3600; + //剩余秒数 + second = second % 3600; + //转换分钟 + long minutes = second / 60; + //剩余秒数 + second = second % 60; + if (days > 0) { + return StringUtils.format("{}天{}小时{}分{}秒", days, hours, minutes, second); + } else { + return StringUtils.format("{}小时{}分{}秒", hours, minutes, second); + } + } + + /** + * 将字符串格式的时间转换为 long + * + * @param dateString the date string + * @return the long + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static Long timeToSecond(String dateString) { + return parse(dateString, PATTERN_DATETIME).getTime(); + } + + /** + * Time to millisecond + * + * @param dateString date string + * @return the long + * @since 1.7.0 + */ + @NotNull + @Contract(pure = true) + public static Long timeToMillisecond(String dateString) { + return parse(dateString, PATTERN_MS_DATETIME).getTime(); + } + + /** + * 获取今天的日期 + * + * @return 时间 string + * @since 1.0.0 + */ + public static String today() { + return format(new Date(), PATTERN_DATE_NO_SEPARATOR); + } + + /** + * Now date + * + * @return the date + * @since 1.0.0 + */ + @NotNull + @Contract(" -> new") + public static Date now() { + return new Date(); + } + + /** + * 日期格式化 + * + * @param date 时间 + * @param pattern 表达式 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + public static String format(Date date, String pattern) { + return ConcurrentDateFormat.of(pattern).format(date); + } + + /** + * 获取当前时间到当日最后时间相差的分钟数 + * + * @param nowTime the now time + * @return int int + * @since 1.0.0 + */ + public static int diffMinutesTime(Date nowTime) { + nowTime = null == nowTime ? now() : nowTime; + + Calendar todayLastTime = DateUtils.getCurrentCalendar(); + todayLastTime.set(Calendar.HOUR_OF_DAY, 23); + todayLastTime.set(Calendar.MINUTE, 59); + todayLastTime.set(Calendar.SECOND, 59); + + return (int) ((todayLastTime.getTimeInMillis() - nowTime.getTime()) / 1000 / 60); + } + + /** + * Diff minutes time int + * + * @return the int + * @since 1.0.0 + */ + public static int diffMinutesTime() { + return diffMinutesTime(now()); + } + + /** + * Gets current calendar * + * + * @return the current calendar + * @since 1.0.0 + */ + private static Calendar getCurrentCalendar() { + Calendar calendar = Calendar.getInstance(); + TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai"); + calendar.setTimeZone(timeZone); + return calendar; + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/DigestUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/DigestUtils.java new file mode 100644 index 0000000..5d09279 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/DigestUtils.java @@ -0,0 +1,769 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.starter.base.utils.Charsets; +import com.aivfo.el.starter.base.utils.Exceptions; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.lang.Nullable; + +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 加密相关工具类 + */ +@UtilityClass +@SuppressWarnings("all") +public class DigestUtils extends org.springframework.util.DigestUtils { + + /** + * HEX_VALUE + */ + private static final String HEX_VALUE = "0123456789abcdef"; + /** + * HEX_CODE + */ + private static final char[] HEX_CODE = HEX_VALUE.toCharArray(); + + /** + * Calculates the MD5 digest. + * + * @param data Data to digest + * @return MD5 digest as a hex array + * @since 1.0.0 + */ + public static byte[] md5(byte[] data) { + return org.springframework.util.DigestUtils.md5Digest(data); + } + + /** + * Calculates the MD5 digest. + * + * @param data Data to digest + * @return MD5 digest as a hex array + * @since 1.0.0 + */ + public static byte[] md5(@NotNull String data) { + return org.springframework.util.DigestUtils.md5Digest(data.getBytes(Charsets.UTF_8)); + } + + /** + * Return a hexadecimal string representation of the MD5 digest of the given bytes. + * + * @param bytes the bytes to calculate the digest over + * @return a hexadecimal digest string + * @since 1.0.0 + */ + @NotNull + public static String md5Hex(byte[] bytes) { + return org.springframework.util.DigestUtils.md5DigestAsHex(bytes); + } + + /** + * sha1Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha1Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha1(data.getBytes(Charsets.UTF_8))); + } + + /** + * encode Hex + * + * @param bytes Data to Hex + * @return bytes as a hex string + * @since 1.0.0 + */ + @NotNull + public static String encodeHex(byte[] bytes) { + StringBuilder r = new StringBuilder(bytes.length * 2); + for (byte b : bytes) { + r.append(HEX_CODE[(b >> 4) & 0xF]); + r.append(HEX_CODE[(b & 0xF)]); + } + return r.toString(); + } + + /** + * sha1 + * + * @param bytes Data to digest + * @return digest as a hex array + * @since 1.0.0 + */ + public static byte[] sha1(byte[] bytes) { + return DigestUtils.digest("SHA-1", bytes); + } + + /** + * digest + * + * @param algorithm 算法 + * @param bytes Data to digest + * @return digest byte array + * @since 1.0.0 + */ + public static byte[] digest(String algorithm, byte[] bytes) { + try { + MessageDigest md = MessageDigest.getInstance(algorithm); + return md.digest(bytes); + } catch (NoSuchAlgorithmException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * sha1Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha1Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha1(bytes)); + } + + /** + * SHA224 + * + * @param data Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha224(@NotNull String data) { + return DigestUtils.sha224(data.getBytes(Charsets.UTF_8)); + } + + /** + * SHA224 + * + * @param bytes Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha224(byte[] bytes) { + return DigestUtils.digest("SHA-224", bytes); + } + + /** + * SHA224Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha224Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha224(data.getBytes(Charsets.UTF_8))); + } + + /** + * SHA224Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha224Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha224(bytes)); + } + + /** + * sha256Hex + * + * @param data Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha256(@NotNull String data) { + return DigestUtils.sha256(data.getBytes(Charsets.UTF_8)); + } + + /** + * sha256Hex + * + * @param bytes Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha256(byte[] bytes) { + return DigestUtils.digest("SHA-256", bytes); + } + + /** + * sha256Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha256Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha256(data.getBytes(Charsets.UTF_8))); + } + + /** + * sha256Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha256Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha256(bytes)); + } + + /** + * sha384 + * + * @param data Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha384(@NotNull String data) { + return DigestUtils.sha384(data.getBytes(Charsets.UTF_8)); + } + + /** + * sha384 + * + * @param bytes Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha384(byte[] bytes) { + return DigestUtils.digest("SHA-384", bytes); + } + + /** + * sha384Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha384Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha384(data.getBytes(Charsets.UTF_8))); + } + + /** + * sha384Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha384Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha384(bytes)); + } + + /** + * sha512Hex + * + * @param data Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha512(@NotNull String data) { + return DigestUtils.sha512(data.getBytes(Charsets.UTF_8)); + } + + /** + * sha512Hex + * + * @param bytes Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha512(byte[] bytes) { + return DigestUtils.digest("SHA-512", bytes); + } + + /** + * sha512Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha512Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha512(data.getBytes(Charsets.UTF_8))); + } + + /** + * sha512Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha512Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha512(bytes)); + } + + /** + * digest Hex + * + * @param algorithm 算法 + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String digestHex(String algorithm, byte[] bytes) { + return DigestUtils.encodeHex(digest(algorithm, bytes)); + } + + /** + * hmacMd5 + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacMd5(@NotNull String data, String key) { + return DigestUtils.hmacMd5(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacMd5 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacMd5(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacMD5", bytes, key); + } + + /** + * digest Hmac + * + * @param algorithm 算法 + * @param bytes Data to digest + * @param key the key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] digestHmac(String algorithm, byte[] bytes, @NotNull String key) { + SecretKey secretKey = new SecretKeySpec(key.getBytes(Charsets.UTF_8), algorithm); + try { + Mac mac = Mac.getInstance(secretKey.getAlgorithm()); + mac.init(secretKey); + return mac.doFinal(bytes); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * hmacMd5 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacMd5Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacMd5(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacMd5 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacMd5Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacMd5(bytes, key)); + } + + /** + * hmacSha1 + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha1(@NotNull String data, String key) { + return DigestUtils.hmacSha1(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha1 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha1(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA1", bytes, key); + } + + /** + * hmacSha1 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha1Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha1(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha1 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha1Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha1(bytes, key)); + } + + /** + * hmacSha224 + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + public static byte[] hmacSha224(@NotNull String data, String key) { + return DigestUtils.hmacSha224(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha224 + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + public static byte[] hmacSha224(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA224", bytes, key); + } + + /** + * hmacSha224 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha224Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha224(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha224 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha224Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha224(bytes, key)); + } + + /** + * hmacSha256 + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + public static byte[] hmacSha256(@NotNull String data, String key) { + return DigestUtils.hmacSha256(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha256 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha256(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA256", bytes, key); + } + + /** + * hmacSha256 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + @NotNull + public static String hmacSha256Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha256(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha256 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha256Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha256(bytes, key)); + } + + /** + * hmacSha384 + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha384(@NotNull String data, String key) { + return DigestUtils.hmacSha384(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha384 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha384(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA384", bytes, key); + } + + /** + * hmacSha384 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha384Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha384(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha384 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha384Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha384(bytes, key)); + } + + /** + * hmacSha512 + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha512(@NotNull String data, String key) { + return DigestUtils.hmacSha512(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha512 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha512(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA512", bytes, key); + } + + /** + * hmacSha512 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha512Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha512(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha512 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha512Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha512(bytes, key)); + } + + /** + * digest Hmac Hex + * + * @param algorithm 算法 + * @param bytes Data to digest + * @param key the key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String digestHmacHex(String algorithm, byte[] bytes, String key) { + return DigestUtils.encodeHex(DigestUtils.digestHmac(algorithm, bytes, key)); + } + + /** + * decode Hex + * + * @param hexStr Hex string + * @return decode hex to bytes + * @since 1.0.0 + */ + @SuppressWarnings("PMD.UndefineMagicConstantRule") + public static byte[] decodeHex(@NotNull String hexStr) { + int len = hexStr.length(); + if ((len & 0x01) != 0) { + throw new IllegalArgumentException("hexBinary needs to be even-length: " + hexStr); + } + String hexText = hexStr.toLowerCase(); + byte[] out = new byte[len >> 1]; + for (int i = 0; i < len; i += 2) { + int hn = HEX_VALUE.indexOf(hexText.charAt(i)); + int ln = HEX_VALUE.indexOf(hexText.charAt(i + 1)); + if (hn == -1 || ln == -1) { + throw new IllegalArgumentException("contains illegal character for hexBinary: " + hexStr); + } + out[i / 2] = (byte) ((hn << 4) | ln); + } + return out; + } + + /** + * 比较字符串,避免字符串因为过长,产生耗时 + * + * @param a String + * @param b String + * @return 是否相同 boolean + * @since 1.0.0 + */ + @Contract("null, _ -> false; !null, null -> false") + public static boolean slowEquals(@Nullable String a, @Nullable String b) { + if (a == null || b == null) { + return false; + } + return DigestUtils.slowEquals(a.getBytes(Charsets.UTF_8), b.getBytes(Charsets.UTF_8)); + } + + /** + * 比较 byte 数组,避免字符串因为过长,产生耗时 + * + * @param a byte array + * @param b byte array + * @return 是否相同 boolean + * @since 1.0.0 + */ + @Contract(value = "null, _ -> false; !null, null -> false", pure = true) + public static boolean slowEquals(@Nullable byte[] a, @Nullable byte[] b) { + if (a == null || b == null) { + return false; + } + if (a.length != b.length) { + return false; + } + int diff = a.length ^ b.length; + for (int i = 0; i < a.length; i++) { + diff |= a[i] ^ b[i]; + } + return diff == 0; + } + + /** + * 自定义加密 先MD5再SHA1 + * + * @param data 数据 + * @return String string + * @since 1.0.0 + */ + @NotNull + public static String encrypt(String data) { + return DigestUtils.encodeHex(sha1(md5Hex(data))); + } + + /** + * sha1 + * + * @param data Data to digest + * @return digest as a hex array + * @since 1.0.0 + */ + public static byte[] sha1(@NotNull String data) { + return DigestUtils.sha1(data.getBytes(Charsets.UTF_8)); + } + + /** + * Calculates the MD5 digest and returns the value as a 32 character hex string. + * + * @param data Data to digest + * @return MD5 digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String md5Hex(@NotNull String data) { + return org.springframework.util.DigestUtils.md5DigestAsHex(data.getBytes(Charsets.UTF_8)); + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/FileUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/FileUtils.java new file mode 100644 index 0000000..3d9ec71 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/FileUtils.java @@ -0,0 +1,656 @@ +package com.aivfo.el.start.core.util; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.file.FileNameUtil; +import com.aivfo.el.starter.base.asserts.Assertions; +import com.aivfo.el.starter.base.constant.ConfigKey; +import com.aivfo.el.starter.base.utils.*; +import lombok.Data; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.FileSystemUtils; +import org.springframework.util.PatternMatchUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 文件工具类 + */ +@Slf4j +@UtilityClass +@SuppressWarnings("checkstyle:MethodLimit") +public class FileUtils extends org.springframework.util.FileCopyUtils { + + /** + * 扫描目录下的文件 + * + * @param path 路径 + * @return 文件集合 list + * @since 1.0.0 + */ + public static List list(String path) { + File file = new File(path); + return list(file, TrueFilter.TRUE); + } + + /** + * 扫描目录下的文件 + * + * @param path 路径 + * @param fileNamePattern 文件名 * 号 + * @return 文件集合 list + * @since 1.0.0 + */ + public static List list(String path, String fileNamePattern) { + File file = new File(path); + return list(file, pathname -> { + String fileName = pathname.getName(); + return PatternMatchUtils.simpleMatch(fileNamePattern, fileName); + }); + } + + /** + * 扫描目录下的文件 + * + * @param path 路径 + * @param filter 文件过滤 + * @return 文件集合 list + * @since 1.0.0 + */ + public static List list(String path, FileFilter filter) { + File file = new File(path); + return list(file, filter); + } + + /** + * 扫描目录下的文件 + * + * @param file 文件 + * @return 文件集合 list + * @since 1.0.0 + */ + public static List list(File file) { + List fileList = new ArrayList<>(); + return list(file, fileList, TrueFilter.TRUE); + } + + /** + * 扫描目录下的文件 + * + * @param file 文件 + * @param fileNamePattern Spring AntPathMatcher 规则 + * @return 文件集合 list + * @since 1.0.0 + */ + public static List list(File file, String fileNamePattern) { + List fileList = new ArrayList<>(); + return list(file, fileList, pathname -> { + String fileName = pathname.getName(); + return PatternMatchUtils.simpleMatch(fileNamePattern, fileName); + }); + } + + /** + * 扫描目录下的文件 + * + * @param file 文件 + * @param filter 文件过滤 + * @return 文件集合 list + * @since 1.0.0 + */ + public static List list(File file, FileFilter filter) { + List fileList = new ArrayList<>(); + return list(file, fileList, filter); + } + + /** + * 扫描目录下的文件 + * + * @param file 文件 + * @param fileList file list + * @param filter 文件过滤 + * @return 文件集合 list + * @since 1.0.0 + */ + @Contract("_, _, _ -> param2") + private static List list(@NotNull File file, List fileList, FileFilter filter) { + if (file.isDirectory()) { + File[] files = file.listFiles(); + if (files != null) { + for (File f : files) { + list(f, fileList, filter); + } + } + } else { + // 过滤文件 + boolean accept = filter.accept(file); + if (file.exists() && accept) { + fileList.add(file); + } + } + return fileList; + } + + /** + * 获取文件后缀名 + * + * @param fullName 文件全名 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String getFileExtension(String fullName) { + Assertions.notNull(fullName, "file fullName is null."); + String fileName = new File(fullName).getName(); + int dotIndex = fileName.lastIndexOf('.'); + return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1); + } + + /** + * 获取文件名,去除后缀名 + * + * @param file 文件 + * @return {String} + * @since 1.0.0 + */ + public static @NotNull String getNameWithoutExtension(String file) { + Assertions.notNull(file, "file is null."); + String fileName = new File(file).getName(); + int dotIndex = fileName.lastIndexOf(CharPool.DOT); + return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex); + } + + /** + * Reads the contents of a file into a String. + * The file is always closed. + * + * @param file the file to read, must not be {@code null} + * @return the file contents, never {@code null} + * @since 1.0.0 + */ + @NotNull + public static String readToString(File file) { + return readToString(file, Charsets.UTF_8); + } + + /** + * Reads the contents of a file into a String. + * The file is always closed. + * + * @param file the file to read, must not be {@code null} + * @param encoding the encoding to use, {@code null} means platform default + * @return the file contents, never {@code null} + * @since 1.0.0 + */ + @NotNull + public static String readToString(File file, Charset encoding) { + try (InputStream in = Files.newInputStream(file.toPath())) { + return IoUtils.toString(in, encoding); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Reads the contents of a file into a String. + * The file is always closed. + * + * @param file the file to read, must not be {@code null} + * @return the file contents, never {@code null} + * @since 1.0.0 + */ + @NotNull + public static byte[] readToByteArray(File file) { + try (InputStream in = Files.newInputStream(file.toPath())) { + return IoUtils.toByteArray(in); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Writes a String to a file creating the file if it does not exist. + * + * @param file the file to write + * @param data the content to write to the file + * @since 1.0.0 + */ + public static void writeToFile(File file, String data) { + writeToFile(file, data, Charsets.UTF_8, false); + } + + /** + * Writes a String to a file creating the file if it does not exist. + * + * @param file the file to write + * @param data the content to write to the file + * @param encoding the encoding to use, {@code null} means platform default + * @param append if {@code true}, then the String will be added to the end of the file rather than overwriting + * @since 1.0.0 + */ + public static void writeToFile(File file, String data, Charset encoding, boolean append) { + try (OutputStream out = new FileOutputStream(file, append)) { + IoUtils.write(data, out, encoding); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Writes a String to a file creating the file if it does not exist. + * + * @param file the file to write + * @param data the content to write to the file + * @param append if {@code true}, then the String will be added to the end of the file rather than overwriting + * @since 1.0.0 + */ + public static void writeToFile(File file, String data, boolean append) { + writeToFile(file, data, Charsets.UTF_8, append); + } + + /** + * Writes a String to a file creating the file if it does not exist. + * + * @param file the file to write + * @param data the content to write to the file + * @param encoding the encoding to use, {@code null} means platform default + * @since 1.0.0 + */ + public static void writeToFile(File file, String data, Charset encoding) { + writeToFile(file, data, encoding, false); + } + + /** + * 转成file + * + * @param multipartFile MultipartFile + * @param file File + * @since 1.0.0 + */ + public static void toFile(@NotNull MultipartFile multipartFile, File file) { + try { + FileUtils.toFile(multipartFile.getInputStream(), file); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * 转成file + * + * @param in InputStream + * @param file File + * @since 1.0.0 + */ + public static void toFile(InputStream in, File file) { + try (OutputStream out = new FileOutputStream(file)) { + FileUtils.copy(in, out); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Moves a file. + *

+ * When the destination file is on another file system, do a "copy and delete". + * + * @param srcFile the file to be moved + * @param destFile the destination file + * @throws IOException if source or destination is invalid + * @since 1.0.0 + */ + public static void moveFile(File srcFile, File destFile) throws IOException { + Assert.notNull(srcFile, "Source must not be null"); + Assert.notNull(destFile, "Destination must not be null"); + if (!srcFile.exists()) { + throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); + } + if (srcFile.isDirectory()) { + throw new IOException("Source '" + srcFile + "' is a directory"); + } + if (destFile.exists()) { + throw new IOException("Destination '" + destFile + "' already exists"); + } + if (destFile.isDirectory()) { + throw new IOException("Destination '" + destFile + "' is a directory"); + } + boolean rename = srcFile.renameTo(destFile); + if (!rename) { + FileUtils.copy(srcFile, destFile); + if (!srcFile.delete()) { + FileUtils.deleteQuietly(destFile); + throw new IOException("Failed to delete original file '" + srcFile + "' after copy to '" + destFile + "'"); + } + } + } + + /** + * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories. + *

+ * The difference between File.delete() and this method are: + *

    + *
  • A directory to be deleted does not have to be empty.
  • + *
  • No exceptions are thrown when a file or directory cannot be deleted.
  • + *
+ * + * @param file file or directory to delete, can be {@code null} + * @return {@code true} if the file or directory was deleted, otherwise {@code false} + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean deleteQuietly(@Nullable File file) { + if (file == null) { + return false; + } + try { + if (file.isDirectory()) { + FileSystemUtils.deleteRecursively(file); + } + } catch (Exception ignored) { + } + + try { + return file.delete(); + } catch (Exception ignored) { + return false; + } + } + + /** + * NIO 按行读取文件 + * + * @param path 文件路径 + * @return 行列表 list + * @since 1.0.0 + */ + public static List readLines(String path) { + return readLines(Paths.get(path)); + } + + /** + * NIO 按行读取文件 + * + * @param path 文件路径 + * @return 行列表 list + * @since 1.0.0 + */ + public static List readLines(Path path) { + return readLines(path, Charsets.UTF_8); + } + + /** + * NIO 按行读取文件 + * + * @param path 文件路径 + * @param cs 字符集 + * @return 行列表 list + * @since 1.0.0 + */ + public static List readLines(Path path, Charset cs) { + try { + return Files.readAllLines(path, cs); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * NIO 按行读取文件 + * + * @param file 文件 + * @return 行列表 list + * @since 1.0.0 + */ + public static List readLines(@NotNull File file) { + return readLines(file.toPath()); + } + + /** + * NIO 按行读取文件 + * + * @param path 文件路径 + * @param cs 字符集 + * @return 行列表 list + * @since 1.0.0 + */ + public static List readLines(String path, Charset cs) { + return readLines(Paths.get(path), cs); + } + + /** + * NIO 按行读取文件 + * + * @param file 文件 + * @param cs 字符集 + * @return 行列表 list + * @since 1.0.0 + */ + public static List readLines(@NotNull File file, Charset cs) { + return readLines(file.toPath(), cs); + } + + /** + * 转换成不同平台的下的路径 + * + * @param path the path + * @return the string + * @since 1.0.0 + */ + @NotNull + public static String getRealFilePath(@NotNull String path) { + return path.replace("/", File.separator).replace("\\", File.separator); + } + + /** + * 拼接不同平台下的文件路径, 末尾不含路径分隔符 + * + * @param paths paths + * @return the string + * @since 1.0.0 + */ + @NotNull + public static String appendPath(@NotNull String... paths) { + // 删除前缀后后缀 + for (int i = 0; i < paths.length; i++) { + paths[i] = StringUtils.removeSuffix(paths[i], File.separator); + // 第一个路径不删除前缀 + if (i == 0) { + continue; + } + paths[i] = StringUtils.removePrefix(paths[i], File.separator); + } + + return String.join(File.separator, paths); + } + + /** + * 递归删除指定目录及文件 + * + * @param first first + * @param more more + * @return the boolean + * @since 1.0.0 + */ + @SneakyThrows + public static boolean deleteFiles(String first, String... more) { + Path start = Paths.get(first, more); + if (start.toFile().exists()) { + Files.walkFileTree(start, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + try { + Files.delete(file); + } catch (IOException ignored) { + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException e) { + if (e == null) { + try { + Files.delete(dir); + } catch (IOException ignored) { + } + return FileVisitResult.CONTINUE; + } + // 如果存在异常, 则说明文件不存在 + return FileVisitResult.SKIP_SUBTREE; + } + }); + } + + return true; + } + + /** + * 创建文件路径且处理 path 最后一个路径分隔符 + * + * @param path path + * @return the string + * @since 1.4.0 + */ + public static @NotNull String toPath(String path) { + File logFile = new File(path); + if (!logFile.exists() && logFile.mkdirs()) { + log.info(String.format("创建日志目录: %s", logFile.getPath())); + } + return logFile.getPath(); + } + + /** + * 默认为true + * + * @author L.cm + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 18:26 + * @since 1.0.0 + */ + public static class TrueFilter implements FileFilter, Serializable { + /** + * The constant TRUE. + */ + public static final TrueFilter TRUE = new TrueFilter(); + /** + * serialVersionUID + */ + private static final long serialVersionUID = -6420452043795072619L; + + /** + * Accept boolean + * + * @param pathname pathname + * @return the boolean + * @since 1.0.0 + */ + @Override + public boolean accept(File pathname) { + return true; + } + } + + /** + * Touch + * + * @param file file + * @throws IOException io exception + * @since 1.5.0 + */ + public static void touch(@NotNull File file) throws IOException { + if (!file.exists()) { + OutputStream out = openOutputStream(file); + IoUtils.closeQuietly(out); + } + + boolean success = file.setLastModified(System.currentTimeMillis()); + if (!success) { + throw new IOException("Unable to set the last modification time for " + file); + } + } + + /** + * Open output stream + * + * @param file file + * @return the file output stream + * @throws IOException io exception + * @since 1.5.0 + */ + public static @NotNull FileOutputStream openOutputStream(File file) throws IOException { + return openOutputStream(file, false); + } + + /** + * Open output stream + * + * @param file file + * @param append append + * @return the file output stream + * @throws IOException io exception + * @since 1.5.0 + */ + @Contract("_, _ -> new") + public static @NotNull FileOutputStream openOutputStream(@NotNull File file, boolean append) throws IOException { + if (file.exists()) { + if (file.isDirectory()) { + throw new IOException("File '" + file + "' exists but is a directory"); + } + + if (!file.canWrite()) { + throw new IOException("File '" + file + "' cannot be written to"); + } + } else { + File parent = file.getParentFile(); + if (parent != null && !parent.mkdirs() && !parent.isDirectory()) { + throw new IOException("Directory '" + parent + "' could not be created"); + } + } + + return new FileOutputStream(file, append); + } + + /** + * 获取文件的文件名和文件后缀 文件名不带后缀 + * + * @param fileName 一个文件名,可以是全路径 + * @return com.aivfo.el.base.utils.AivfoFileUtils.FileNameInfo + * @Date 2023/5/4 + * @Author wangyl + */ + public static FileNameInfo splitFileExt(String fileName) { + File file = FileUtil.file(fileName); + String mainNameName = FileNameUtil.mainName(file); + String extName = FileNameUtil.extName(file); + FileNameInfo fileNameInfo = new FileNameInfo(); + fileNameInfo.setMainName(mainNameName); + fileNameInfo.setExtName(extName); + return fileNameInfo; + } + + @Data + public static class FileNameInfo { + /** + * 文件名 + */ + String mainName; + /** + * 后缀名 + */ + String extName; + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/JvmRandom.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/JvmRandom.java new file mode 100644 index 0000000..d63ade7 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/JvmRandom.java @@ -0,0 +1,214 @@ +package com.aivfo.el.start.core.util; + +import org.jetbrains.annotations.Contract; + +import java.util.Random; + +/** + * JVMRandom是一个包装器, 它通过数学随机方法及其系统范围内的随机对象. + * 这样做是为了允许在类的所有成员之间共享种子的随机类-更好的名称应该是shared seed Random. + * 当前的实现覆盖了方法 {@link Random#nextInt(int)} 和 {@link Random#nextLong()} 生成从 0 (包含) 到最大值 (不包含) 的正数. + */ +public final class JvmRandom extends Random { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = 1L; + /** + * SHARED_RANDOM + */ + private static final Random SHARED_RANDOM = new Random(); + /** + * Constructed + */ + private final boolean constructed; + + /** + * Constructs a new instance. + * + * @since 1.0.0 + */ + JvmRandom() { + this.constructed = true; + } + + /** + * Unsupported in 2.0. + * + * @param seed ignored + * @since 1.0.0 + */ + @Contract(pure = true) + @Override + public synchronized void setSeed(long seed) { + if (this.constructed) { + throw new UnsupportedOperationException(); + } + } + + /** + * Unsupported in 2.0. + * + * @param byteArray ignored + * @since 1.0.0 + */ + @Contract("_ -> fail") + @Override + public void nextBytes(byte[] byteArray) { + throw new UnsupportedOperationException(); + } + + /** + *

Returns the next pseudorandom, uniformly distributed int value from the Math.random() sequence.

Identical + * to nextInt(Integer.MAX_VALUE)

N.B. All values are >= 0.

+ * + * @return the random int + * @since 1.0.0 + */ + @Override + public int nextInt() { + return this.nextInt(Integer.MAX_VALUE); + } + + /** + *

Returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified + * value (exclusive), from the Math.random() sequence.

+ * + * @param n the specified exclusive max-value + * @return the random int + * @throws IllegalArgumentException when n <= 0 + * @since 1.0.0 + */ + @Override + public int nextInt(int n) { + return SHARED_RANDOM.nextInt(n); + } + + /** + *

Returns the next pseudorandom, uniformly distributed long value from the Math.random() sequence.

Identical + * to nextLong(Long.MAX_VALUE)

N.B. All values are >= 0.

+ * + * @return the random long + * @since 1.0.0 + */ + @Override + public long nextLong() { + return nextLong(Long.MAX_VALUE); + } + + /** + *

Returns a pseudorandom, uniformly distributed long value between 0 (inclusive) and the specified + * value (exclusive), from the Math.random() sequence.

+ * + * @param n the specified exclusive max-value + * @return the random long + * @throws IllegalArgumentException when n <= 0 + * @since 1.0.0 + */ + public static long nextLong(long n) { + if (n <= 0) { + throw new IllegalArgumentException( + "Upper bound for nextInt must be positive" + ); + } + // Code adapted from Harmony Random#nextInt(int) + // n is power of 2 + if ((n & -n) == n) { + // dropping lower order bits improves behaviour for low values of n + return next63bits() >> 63 - bitsRequired(n - 1); + } + // Not a power of two + long val; + long bits; + // reject some values to improve distribution + do { + bits = next63bits(); + val = bits % n; + } while (bits - val + (n - 1) < 0); + return val; + } + + /** + * Get the next unsigned random long + * + * @return unsigned random long + * @since 1.0.0 + */ + private static long next63bits() { + // drop the sign bit to leave 63 random bits + return SHARED_RANDOM.nextLong() & 0x7fffffffffffffffL; + } + + /** + * Count the number of bits required to represent a long number. + * + * @param num long number + * @return number of bits required + * @since 1.0.0 + */ + @Contract(pure = true) + private static int bitsRequired(long num) { + // Derived from Hacker's Delight, Figure 5-9 + long y = num; + int n = 0; + while (true) { + // 64 = number of bits in a long + if (num < 0) { + return 64 - n; + } + if (y == 0) { + return n; + } + n++; + num = num << 1; + y = y >> 1; + } + } + + /** + *

Returns the next pseudorandom, uniformly distributed boolean value from the Math.random() sequence.

+ * + * @return the random boolean + * @since 1.0.0 + */ + @Override + public boolean nextBoolean() { + return SHARED_RANDOM.nextBoolean(); + } + + /** + *

Returns the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 + * from the Math.random() sequence.

+ * + * @return the random float + * @since 1.0.0 + */ + @Override + public float nextFloat() { + return SHARED_RANDOM.nextFloat(); + } + + /** + *

Synonymous to the Math.random() call.

+ * + * @return the random double + * @since 1.0.0 + */ + @Override + public double nextDouble() { + return SHARED_RANDOM.nextDouble(); + } + + /** + * Next gaussian double + * + * @return the double + * @since 1.0.0 + */ + @Contract(" -> fail") + @Override + public synchronized double nextGaussian() { + throw new UnsupportedOperationException(); + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ObjectUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ObjectUtils.java new file mode 100644 index 0000000..92b1755 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ObjectUtils.java @@ -0,0 +1,418 @@ +package com.aivfo.el.start.core.util; + +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.springframework.lang.Nullable; + +import java.util.Objects; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 对象工具类 + */ +@UtilityClass +@SuppressWarnings("checkstyle:ReturnCount") +public class ObjectUtils extends org.springframework.util.ObjectUtils { + + /** + * 判断对象为null + * + * @param object 对象 + * @return 对象是否为空 boolean + * @since 1.0.0 + */ + @Contract(value = "!null -> false; null -> true", pure = true) + public static boolean isNull(@Nullable Object object) { + return Objects.isNull(object); + } + + /** + * 判断对象不为null + * + * @param object 对象 + * @return 对象是否不为空 boolean + * @since 1.0.0 + */ + @Contract(value = "!null -> true; null -> false", pure = true) + public static boolean isNotNull(@Nullable Object object) { + return Objects.nonNull(object); + } + + /** + * 判断对象为true + * + * @param object 对象 + * @return 对象是否为true boolean + * @since 1.0.0 + */ + @Contract(value = "null -> false", pure = true) + public static boolean isTrue(@Nullable Boolean object) { + return Boolean.TRUE.equals(object); + } + + /** + * 判断对象为false + * + * @param object 对象 + * @return 对象是否为false boolean + * @since 1.0.0 + */ + @Contract(value = "null -> true", pure = true) + public static boolean isFalse(@Nullable Boolean object) { + return object == null || Boolean.FALSE.equals(object); + } + + /** + * 判断数组不为空 + * + * @param array 数组 + * @return 数组是否为空 boolean + * @since 1.0.0 + */ + @Contract(value = "null -> false", pure = true) + public static boolean isNotEmpty(@Nullable Object[] array) { + return !ObjectUtils.isEmpty(array); + } + + /** + * 判断对象不为空 + * + * @param obj 数组 + * @return 数组是否为空 boolean + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isNotEmpty(@Nullable Object obj) { + return !ObjectUtils.isEmpty(obj); + } + + /** + * 对象 eq + * + * @param o1 Object + * @param o2 Object + * @return 是否eq boolean + * @since 1.0.0 + */ + @Contract(value = "null, !null -> false; !null, null -> false", pure = true) + public static boolean equals(@Nullable Object o1, @Nullable Object o2) { + return Objects.equals(o1, o2); + } + + /** + * 比较两个对象是否不相等.
+ * + * @param o1 对象1 + * @param o2 对象2 + * @return 是否不eq boolean + * @since 1.0.0 + */ + @Contract(value = "null, !null -> true; !null, null -> true", pure = true) + public static boolean isNotEqual(Object o1, Object o2) { + return !Objects.equals(o1, o2); + } + + /** + * 返回对象的 hashCode + * + * @param obj Object + * @return hashCode int + * @since 1.0.0 + */ + public static int hashCode(@Nullable Object obj) { + return Objects.hashCode(obj); + } + + /** + * 如果对象为null,返回默认值 + * + * @param object Object + * @param defaultValue 默认值 + * @return Object object + * @since 1.0.0 + */ + @Contract(value = "!null, _ -> param1; null, _ -> param2", pure = true) + public static Object defaultIfNull(@Nullable Object object, Object defaultValue) { + return object != null ? object : defaultValue; + } + + /** + * 强转string + * + * @param object Object + * @return String string + * @since 1.0.0 + */ + @Nullable + public static String toStr(@Nullable Object object) { + return toStr(object, null); + } + + /** + * 强转string + * + * @param object Object + * @param defaultValue 默认值 + * @return String string + * @since 1.0.0 + */ + @Contract("null, _ -> param2") + @Nullable + public static String toStr(@Nullable Object object, @Nullable String defaultValue) { + if (null == object) { + return defaultValue; + } + if (object instanceof CharSequence) { + return ((CharSequence) object).toString(); + } + return String.valueOf(object); + } + + /** + * 对象转为 int (支持 String 和 Number) ,默认: 0 + * + * @param object Object + * @return int int + * @since 1.0.0 + */ + public static int toInt(@Nullable Object object) { + return toInt(object, 0); + } + + /** + * 对象转为 int (支持 String 和 Number) + * + * @param object Object + * @param defaultValue 默认值 + * @return int int + * @since 1.0.0 + */ + @Contract("null, _ -> param2") + public static int toInt(@Nullable Object object, int defaultValue) { + if (object instanceof Number) { + return ((Number) object).intValue(); + } + if (object instanceof CharSequence) { + String value = ((CharSequence) object).toString(); + try { + return Integer.parseInt(value); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + return defaultValue; + } + + /** + * 对象转为 long (支持 String 和 Number) ,默认: 0L + * + * @param object Object + * @return long long + * @since 1.0.0 + */ + public static long toLong(@Nullable Object object) { + return toLong(object, 0L); + } + + /** + * 对象转为 long (支持 String 和 Number) ,默认: 0L + * + * @param object Object + * @param defaultValue the default value + * @return long long + * @since 1.0.0 + */ + @Contract("null, _ -> param2") + public static long toLong(@Nullable Object object, long defaultValue) { + if (object instanceof Number) { + return ((Number) object).longValue(); + } + if (object instanceof CharSequence) { + String value = ((CharSequence) object).toString(); + try { + return Long.parseLong(value); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + return defaultValue; + } + + /** + * 对象转为 Float + * + * @param object Object + * @return 结果 float + * @since 1.0.0 + */ + public static float toFloat(@Nullable Object object) { + return toFloat(object, 0.0f); + } + + /** + * 对象转为 Float + * + * @param object Object + * @param defaultValue float + * @return 结果 float + * @since 1.0.0 + */ + @Contract("null, _ -> param2") + public static float toFloat(@Nullable Object object, float defaultValue) { + if (object instanceof Number) { + return ((Number) object).floatValue(); + } + if (object instanceof CharSequence) { + String value = ((CharSequence) object).toString(); + try { + return Float.parseFloat(value); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + return defaultValue; + } + + /** + * 对象转为 Double + * + * @param object Object + * @return 结果 double + * @since 1.0.0 + */ + public static double toDouble(@Nullable Object object) { + return toDouble(object, 0.0d); + } + + /** + * 对象转为 Double + * + * @param object Object + * @param defaultValue double + * @return 结果 double + * @since 1.0.0 + */ + @Contract("null, _ -> param2") + public static double toDouble(@Nullable Object object, double defaultValue) { + if (object instanceof Number) { + return ((Number) object).doubleValue(); + } + if (object instanceof CharSequence) { + String value = ((CharSequence) object).toString(); + try { + return Double.parseDouble(value); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + return defaultValue; + } + + /** + * 对象转为 Byte + * + * @param object Object + * @return 结果 byte + * @since 1.0.0 + */ + public static byte toByte(@Nullable Object object) { + return toByte(object, (byte) 0); + } + + /** + * 对象转为 Byte + * + * @param object Object + * @param defaultValue byte + * @return 结果 byte + * @since 1.0.0 + */ + @Contract("null, _ -> param2") + public static byte toByte(@Nullable Object object, byte defaultValue) { + if (object instanceof Number) { + return ((Number) object).byteValue(); + } + if (object instanceof CharSequence) { + String value = ((CharSequence) object).toString(); + try { + return Byte.parseByte(value); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + return defaultValue; + } + + /** + * 对象转为 Short + * + * @param object Object + * @return 结果 short + * @since 1.0.0 + */ + public static short toShort(@Nullable Object object) { + return toShort(object, (short) 0); + } + + /** + * 对象转为 Short + * + * @param object Object + * @param defaultValue short + * @return 结果 short + * @since 1.0.0 + */ + @Contract("null, _ -> param2") + public static short toShort(@Nullable Object object, short defaultValue) { + if (object instanceof Number) { + return ((Number) object).byteValue(); + } + if (object instanceof CharSequence) { + String value = ((CharSequence) object).toString(); + try { + return Short.parseShort(value); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + return defaultValue; + } + + /** + * 对象转为 Boolean + * + * @param object Object + * @return 结果 boolean + * @since 1.0.0 + */ + @Nullable + public static Boolean toBoolean(@Nullable Object object) { + return toBoolean(object, null); + } + + /** + * 对象转为 Boolean + * + * @param object Object + * @param defaultValue 默认值 + * @return 结果 boolean + * @since 1.0.0 + */ + @Contract("null, _ -> param2") + @Nullable + public static Boolean toBoolean(@Nullable Object object, @Nullable Boolean defaultValue) { + if (object instanceof Boolean) { + return (Boolean) object; + } + if (object instanceof CharSequence) { + String value = ((CharSequence) object).toString(); + try { + return Boolean.parseBoolean(value.trim()); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + return defaultValue; + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/PathUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/PathUtils.java new file mode 100644 index 0000000..a1e0b71 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/PathUtils.java @@ -0,0 +1,88 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.starter.base.utils.Charsets; +import com.aivfo.el.starter.core.util.UrlUtils; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.springframework.lang.Nullable; + +import java.io.File; +import java.net.URL; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 用来获取各种目录 + */ +@UtilityClass +@SuppressWarnings("checkstyle:ReturnCount") +public class PathUtils { + /** + * FILE_PROTOCOL + */ + public static final String FILE_PROTOCOL = "file"; + /** + * JAR_PROTOCOL + */ + public static final String JAR_PROTOCOL = "jar"; + /** + * ZIP_PROTOCOL + */ + public static final String ZIP_PROTOCOL = "zip"; + /** + * FILE_PROTOCOL_PREFIX + */ + public static final String FILE_PROTOCOL_PREFIX = "file:"; + /** + * JAR_FILE_SEPARATOR + */ + public static final String JAR_FILE_SEPARATOR = "!/"; + + /** + * 获取jar包运行时的当前目录 + * + * @return {String} + * @since 1.0.0 + */ + @Nullable + public static String getJarPath() { + try { + URL url = PathUtils.class.getResource("/").toURI().toURL(); + return PathUtils.toFilePath(url); + } catch (Exception e) { + String path = PathUtils.class.getResource("").getPath(); + return new File(path).getParentFile().getParentFile().getAbsolutePath(); + } + } + + /** + * To file path string. + * + * @param url the url + * @return the string + * @since 1.0.0 + */ + @Contract("null -> null") + @Nullable + public static String toFilePath(@Nullable URL url) { + if (url == null) { + return null; + } + String protocol = url.getProtocol(); + String file = UrlUtils.decodeUrl(url.getPath(), Charsets.UTF_8); + if (FILE_PROTOCOL.equals(protocol)) { + return new File(file).getParentFile().getParentFile().getAbsolutePath(); + } else if (JAR_PROTOCOL.equals(protocol) || ZIP_PROTOCOL.equals(protocol)) { + int ipos = file.indexOf(JAR_FILE_SEPARATOR); + if (ipos > 0) { + file = file.substring(0, ipos); + } + if (file.startsWith(FILE_PROTOCOL_PREFIX)) { + file = file.substring(FILE_PROTOCOL_PREFIX.length()); + } + return new File(file).getParentFile().getAbsolutePath(); + } + return file; + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/RandomUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/RandomUtils.java new file mode 100644 index 0000000..c03dce3 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/RandomUtils.java @@ -0,0 +1,780 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.start.core.enums.RandomType; +import com.aivfo.el.starter.base.asserts.Assertions; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 随机工具 + */ +@SuppressWarnings("checkstyle:MethodLimit") +@UtilityClass +public class RandomUtils { + + /** + * S_INT + */ + private static final String S_INT = "0123456789"; + /** + * S_STR + */ + private static final String S_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + /** + * S_ALL + */ + private static final String S_ALL = S_INT + S_STR; + /** + * UNICODE_START + */ + private static final int UNICODE_START = 19968; + /** + * UNICODE_END + */ + private static final int UNICODE_END = 40864; + /** + * An instance of {@link JvmRandom}. + */ + private static final Random JVM_RANDOM = new JvmRandom(); + + /** + *

Returns the next pseudorandom, uniformly distributed int value from the Math.random() sequence.

N.B. + * All values are >= 0. + * + * @return the random int + * @since 1.0.0 + */ + public static int nextInt() { + return nextInt(JVM_RANDOM); + } + + /** + *

Returns the next pseudorandom, uniformly distributed int value from the given random + * sequence.

+ * + * @param random the Random sequence generator. + * @return the random int + * @since 1.0.0 + */ + public static int nextInt(@NotNull Random random) { + return random.nextInt(); + } + + /** + *

Returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified + * value (exclusive), from the Math.random() sequence.

+ * + * @param n the specified exclusive max-value + * @return the random int + * @since 1.0.0 + */ + public static int nextInt(int n) { + return nextInt(JVM_RANDOM, n); + } + + /** + *

Returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified + * value (exclusive), from the given Random sequence.

+ * + * @param random the Random sequence generator. + * @param n the specified exclusive max-value + * @return the random int + * @since 1.0.0 + */ + public static int nextInt(@NotNull Random random, int n) { + // check this cannot return 'n' + return random.nextInt(n); + } + + /** + *

Returns the next pseudorandom, uniformly distributed long value from the Math.random() sequence.

N.B. + * All values are >= 0. + * + * @return the random long + * @since 1.0.0 + */ + public static long nextLong() { + return nextLong(JVM_RANDOM); + } + + /** + *

Returns the next pseudorandom, uniformly distributed long value from the given Random sequence.

+ * + * @param random the Random sequence generator. + * @return the random long + * @since 1.0.0 + */ + public static long nextLong(@NotNull Random random) { + return random.nextLong(); + } + + /** + *

Returns the next pseudorandom, uniformly distributed boolean value from the Math.random() sequence.

+ * + * @return the random boolean + * @since 1.0.0 + */ + public static boolean nextBoolean() { + return nextBoolean(JVM_RANDOM); + } + + /** + *

Returns the next pseudorandom, uniformly distributed boolean value from the given random sequence.

+ * + * @param random the Random sequence generator. + * @return the random boolean + * @since 1.0.0 + */ + public static boolean nextBoolean(@NotNull Random random) { + return random.nextBoolean(); + } + + /** + *

Returns the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 + * from the Math.random() sequence.

+ * + * @return the random float + * @since 1.0.0 + */ + public static float nextFloat() { + return nextFloat(JVM_RANDOM); + } + + /** + *

Returns the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 + * from the given Random sequence.

+ * + * @param random the Random sequence generator. + * @return the random float + * @since 1.0.0 + */ + public static float nextFloat(@NotNull Random random) { + return random.nextFloat(); + } + + /** + *

Returns the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 + * from the Math.random() sequence.

+ * + * @return the random double + * @since 1.0.0 + */ + public static double nextDouble() { + return nextDouble(JVM_RANDOM); + } + + /** + *

Returns the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 + * from the given Random sequence.

+ * + * @param random the Random sequence generator. + * @return the random double + * @since 1.0.0 + */ + public static double nextDouble(@NotNull Random random) { + return random.nextDouble(); + } + + /** + * Next int int. + * + * @param startInclusive the start inclusive + * @param endExclusive the end exclusive + * @return the int + * @since 1.0.0 + */ + public static int nextInt(int startInclusive, int endExclusive) { + return startInclusive + JVM_RANDOM.nextInt(endExclusive - startInclusive); + } + + /** + * Next long long. + * + * @param startInclusive the start inclusive + * @param endExclusive the end exclusive + * @return the long + * @since 1.0.0 + */ + public static long nextLong(long startInclusive, long endExclusive) { + return (long) nextDouble(startInclusive, endExclusive); + } + + /** + * Next double double. + * + * @param startInclusive the start inclusive + * @param endInclusive the end inclusive + * @return the double + * @since 1.0.0 + */ + public static double nextDouble(double startInclusive, double endInclusive) { + return startInclusive + ((endInclusive - startInclusive) * JVM_RANDOM.nextDouble()); + } + + /** + * Next float float. + * + * @param startInclusive the start inclusive + * @param endInclusive the end inclusive + * @return the float + * @since 1.0.0 + */ + public static float nextFloat(float startInclusive, float endInclusive) { + return startInclusive + ((endInclusive - startInclusive) * JVM_RANDOM.nextFloat()); + } + + /** + * Next size int. + * + * @param startInclusive the start inclusive + * @param endInclusive the end inclusive + * @return the int + * @since 1.0.0 + */ + public static int nextSize(int startInclusive, int endInclusive) { + return startInclusive + JVM_RANDOM.nextInt(endInclusive - startInclusive + 1); + } + + /** + * 随机数生成 + * + * @param count 字符长度 + * @return 随机数 string + * @since 1.0.0 + */ + @NotNull + public static String random(int count) { + return RandomUtils.random(count, RandomType.ALL); + } + + /** + * 随机数生成 + * + * @param count 字符长度 + * @param randomType 随机数类别 + * @return 随机数 string + * @since 1.0.0 + */ + @NotNull + public static String random(int count, RandomType randomType) { + if (count == 0) { + return ""; + } + Assertions.isTrue(count > 0, "Requested random string length " + count + " is less than 0."); + ThreadLocalRandom random = ThreadLocalRandom.current(); + char[] buffer = new char[count]; + for (int i = 0; i < count; i++) { + if (RandomType.INT == randomType) { + buffer[i] = S_INT.charAt(random.nextInt(S_INT.length())); + } else if (RandomType.STRING == randomType) { + buffer[i] = S_STR.charAt(random.nextInt(S_STR.length())); + } else { + buffer[i] = S_ALL.charAt(random.nextInt(S_ALL.length())); + } + } + return new String(buffer); + } + + /** + * Gets long more than zero less than * + * + * @param n n + * @return the long more than zero less than + * @since 1.0.0 + */ + static long getLongMoreThanZeroLessThan(long n) { + long res = getLongLessThan(n); + while (res <= 0L) { + res = getLongLessThan(n); + } + + return res; + } + + /** + * Gets long less than * + * + * @param n n + * @return the long less than + * @since 1.0.0 + */ + static long getLongLessThan(long n) { + long res = JVM_RANDOM.nextLong(); + return res % n; + } + + /** + * Gets long between * + * + * @param n n + * @param m m + * @return the long between + * @since 1.0.0 + */ + static long getLongBetween(long n, long m) { + if (m <= n) { + return n; + } else { + long res = getLongMoreThanZero(); + return n + res % (m - n); + } + } + + /** + * Gets long more than zero * + * + * @return the long more than zero + * @since 1.0.0 + */ + static long getLongMoreThanZero() { + long res = JVM_RANDOM.nextLong(); + while (res <= 0L) { + res = JVM_RANDOM.nextLong(); + } + + return res; + } + + /** + * Gets integer less than * + * + * @param n n + * @return the integer less than + * @since 1.0.0 + */ + static int getIntegerLessThan(int n) { + int res = JVM_RANDOM.nextInt(); + return res % n; + } + + /** + * Gets string with number * + * + * @param n n + * @return the string with number + * @since 1.0.0 + */ + static @NotNull String getStringWithNumber(int n) { + int[] arg = new int[]{48, 58}; + return getString(n, arg); + } + + /** + * Gets string * + * + * @param n n + * @param arg arg + * @return the string + * @since 1.0.0 + */ + private static @NotNull String getString(int n, int[] arg) { + StringBuilder res = new StringBuilder(); + + for (int i = 0; i < n; ++i) { + res.append(getChar(arg)); + } + + return res.toString(); + } + + /** + * Gets char * + * + * @param arg arg + * @return the char + * @since 1.0.0 + */ + private static char getChar(int @NotNull [] arg) { + int size = arg.length; + int c = JVM_RANDOM.nextInt(size / 2); + c *= 2; + return (char) getIntegerBetween(arg[c], arg[c + 1]); + } + + /** + * Gets integer between * + * + * @param n n + * @param m m + * @return the integer between + * @since 1.0.0 + */ + static int getIntegerBetween(int n, int m) { + if (m == n) { + return n; + } else { + int res = getIntegerMoreThanZero(); + return n + res % (m - n); + } + } + + /** + * Gets integer more than zero * + * + * @return the integer more than zero + * @since 1.0.0 + */ + static int getIntegerMoreThanZero() { + int res = JVM_RANDOM.nextInt(); + while (res <= 0) { + res = JVM_RANDOM.nextInt(); + } + + return res; + } + + /** + * Gets random string * + * + * @param length length + * @return the random string + * @since 1.0.0 + */ + static @NotNull String getRandomString(int length) { + StringBuilder buffer = new StringBuilder(S_ALL); + StringBuilder sb = new StringBuilder(); + Random random = new Random(); + int range = buffer.length(); + + for (int i = 0; i < length; ++i) { + sb.append(buffer.charAt(random.nextInt(range))); + } + + return sb.toString(); + } + + /** + * Gets string shorten than * + * + * @param n n + * @return the string shorten than + * @since 1.0.0 + */ + static @NotNull String getStringShortenThan(int n) { + int len = getIntegerMoreThanZeroLessThan(n); + return getStringWithCharacter(len); + } + + /** + * Gets integer more than zero less than * + * + * @param n n + * @return the integer more than zero less than + * @since 1.0.0 + */ + static int getIntegerMoreThanZeroLessThan(int n) { + int res = JVM_RANDOM.nextInt(n); + while (res == 0) { + res = JVM_RANDOM.nextInt(n); + } + + return res; + } + + /** + * Gets string with character * + * + * @param n n + * @return the string with character + * @since 1.0.0 + */ + static @NotNull String getStringWithCharacter(int n) { + int[] arg = new int[]{97, 123, 65, 91}; + return getString(n, arg); + } + + /** + * Gets string with num and cha shorten than * + * + * @param n n + * @return the string with num and cha shorten than + * @since 1.0.0 + */ + static @NotNull String getStringWithNumAndChaShortenThan(int n) { + int len = getIntegerMoreThanZeroLessThan(n); + return getStringWithNumAndCha(len); + } + + /** + * Gets string with num and cha * + * + * @param n n + * @return the string with num and cha + * @since 1.0.0 + */ + static @NotNull String getStringWithNumAndCha(int n) { + int[] arg = new int[]{97, 123, 65, 91, 48, 58}; + return getString(n, arg); + } + + /** + * Gets string between * + * + * @param n n + * @param m m + * @return the string between + * @since 1.0.0 + */ + static @NotNull String getStringBetween(int n, int m) { + int len = getIntegerBetween(n, m); + return getStringWithCharacter(len); + } + + /** + * Gets string with num and cha between * + * + * @param n n + * @param m m + * @return the string with num and cha between + * @since 1.0.0 + */ + static @NotNull String getStringWithNumAndChaBetween(int n, int m) { + int len = getIntegerBetween(n, m); + return getStringWithNumAndCha(len); + } + + /** + * Gets string with prefix * + * + * @param n n + * @param prefix prefix + * @return the string with prefix + * @since 1.0.0 + */ + static @NotNull String getStringWithPrefix(int n, @NotNull String prefix) { + int len = prefix.length(); + if (n <= len) { + return prefix; + } else { + len = n - len; + return prefix + getStringWithCharacter(len); + } + } + + /** + * Gets string with suffix * + * + * @param n n + * @param suffix suffix + * @return the string with suffix + * @since 1.0.0 + */ + static @NotNull String getStringWithSuffix(int n, @NotNull String suffix) { + int len = suffix.length(); + if (n <= len) { + return suffix; + } else { + len = n - len; + return getStringWithCharacter(len) + suffix; + } + } + + /** + * Gets string with both * + * + * @param n n + * @param prefix prefix + * @param suffix suffix + * @return the string with both + * @since 1.0.0 + */ + static @NotNull String getStringWithBoth(int n, @NotNull String prefix, @NotNull String suffix) { + int len = prefix.length() + suffix.length(); + StringBuilder res = new StringBuilder(prefix); + if (n <= len) { + return res.append(suffix).toString(); + } else { + len = n - len; + res.append(getStringWithCharacter(len)); + res.append(suffix); + return res.toString(); + } + } + + /** + * Gets chese word with prifix * + * + * @param n n + * @param prefix prefix + * @return the chese word with prifix + * @since 1.0.0 + */ + static @NotNull String getCheseWordWithPrifix(int n, @NotNull String prefix) { + int len = prefix.length(); + if (n <= len) { + return prefix; + } else { + len = n - len; + return prefix + getCheseWord(len); + } + } + + /** + * Gets chese word * + * + * @param len len + * @return the chese word + * @since 1.0.0 + */ + static @NotNull String getCheseWord(int len) { + StringBuilder res = new StringBuilder(); + + for (int i = 0; i < len; ++i) { + char str = getCheseChar(); + res.append(str); + } + + return res.toString(); + } + + /** + * Gets chese char * + * + * @return the chese char + * @since 1.0.0 + */ + private static char getCheseChar() { + return (char) (UNICODE_START + JVM_RANDOM.nextInt(UNICODE_END - UNICODE_START)); + } + + /** + * Gets chese word with suffix * + * + * @param n n + * @param suffix suffix + * @return the chese word with suffix + * @since 1.0.0 + */ + static @NotNull String getCheseWordWithSuffix(int n, @NotNull String suffix) { + int len = suffix.length(); + if (n <= len) { + return suffix; + } else { + len = n - len; + return getCheseWord(len) + suffix; + } + } + + /** + * Gets chese word with both * + * + * @param n n + * @param prefix prefix + * @param suffix suffix + * @return the chese word with both + * @since 1.0.0 + */ + static @NotNull String getCheseWordWithBoth(int n, @NotNull String prefix, @NotNull String suffix) { + int len = prefix.length() + suffix.length(); + StringBuilder res = new StringBuilder(prefix); + if (n <= len) { + return res.append(suffix).toString(); + } else { + len = n - len; + res.append(getCheseWord(len)); + res.append(suffix); + return res.toString(); + } + } + + /** + * Gets boolean * + * + * @return the boolean + * @since 1.0.0 + */ + static boolean getBoolean() { + return getIntegerMoreThanZeroLessThan(3) == 1; + } + + /** + * Get random array int [ ] + * + * @param min min + * @param max max + * @param n n + * @return the int [ ] + * @since 1.0.0 + */ + static int @Nullable [] getRandomArray(int min, int max, int n) { + int len = max - min + 1; + if (max >= min && n <= len) { + int[] source = new int[len]; + + int i = min; + while (i < min + len) { + source[i - min] = i++; + } + + int[] result = new int[n]; + Random rd = new Random(); + + for (int y = 0; y < result.length; ++y) { + int index = Math.abs(rd.nextInt() % len--); + result[y] = source[index]; + source[index] = source[len]; + } + + return result; + } else { + return null; + } + } + + /** + * Gets random collection * + * + * @param min min + * @param max max + * @param n n + * @return the random collection + * @since 1.0.0 + */ + static @NotNull Collection getRandomCollection(int min, int max, int n) { + Set res = new HashSet<>(); + int mx = max; + int mn = min; + int i; + if (n == max + 1 - min) { + for (i = 1; i <= n; ++i) { + res.add(i); + } + + } else { + for (i = 0; i < n; ++i) { + int v = getIntegerBetween(mn, mx); + if (v == mx) { + --mx; + } + + if (v == mn) { + ++mn; + } + + while (res.contains(v)) { + v = getIntegerBetween(mn, mx); + if (v == mx) { + mx = v; + } + + if (v == mn) { + mn = v; + } + } + + res.add(v); + } + + } + return res; + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ReflectionUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ReflectionUtils.java new file mode 100644 index 0000000..c6fd1a3 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ReflectionUtils.java @@ -0,0 +1,621 @@ +package com.aivfo.el.start.core.util; + +import cn.hutool.core.util.StrUtil; +import com.aivfo.el.start.core.exception.BaseException; +import com.aivfo.el.starter.base.asserts.Assertions; +import com.aivfo.el.starter.base.utils.Exceptions; +import com.aivfo.el.starter.base.utils.StringPool; +import com.aivfo.el.starter.base.utils.StringUtils; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.beans.BeansException; +import org.springframework.cglib.core.CodeGenerationException; +import org.springframework.core.convert.Property; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.util.Assert; + +import java.beans.PropertyDescriptor; +import java.lang.annotation.Annotation; +import java.lang.reflect.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 反射工具类 + */ +@Slf4j +@UtilityClass +public class ReflectionUtils extends org.springframework.util.ReflectionUtils { + + /** + * class field cache + */ + private static final Map, List> CLASS_FIELD_CACHE = new ConcurrentHashMap<>(); + + /** + * PRIMITIVE_WRAPPER_TYPE_MAP + */ + private static final Map, Class> PRIMITIVE_WRAPPER_TYPE_MAP = new IdentityHashMap<>(8); + + static { + PRIMITIVE_WRAPPER_TYPE_MAP.put(Boolean.class, boolean.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Byte.class, byte.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Character.class, char.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Double.class, double.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Float.class, float.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Integer.class, int.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Long.class, long.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Short.class, short.class); + } + + /** + * 获取 Bean 的所有 get方法 + * + * @param type 类 + * @return PropertyDescriptor数组 property descriptor [ ] + * @since 1.0.0 + */ + public static PropertyDescriptor[] getBeanGetters(Class type) { + return getPropertiesHelper(type, true, false); + } + + /** + * 获取 Bean 的所有 PropertyDescriptor + * + * @param type 类 + * @param read 读取方法 + * @param write 写方法 + * @return PropertyDescriptor数组 property descriptor [ ] + * @since 1.0.0 + */ + public static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) { + try { + PropertyDescriptor[] all = BeanUtils.getPropertyDescriptors(type); + if (read && write) { + return all; + } else { + List properties = new ArrayList<>(all.length); + for (PropertyDescriptor pd : all) { + boolean canRead = read && pd.getReadMethod() != null; + boolean canWrite = write && pd.getWriteMethod() != null; + if (canRead || canWrite) { + properties.add(pd); + } + } + return properties.toArray(new PropertyDescriptor[0]); + } + } catch (BeansException ex) { + throw new CodeGenerationException(ex); + } + } + + /** + * 获取 Bean 的所有 set方法 + * + * @param type 类 + * @return PropertyDescriptor数组 property descriptor [ ] + * @since 1.0.0 + */ + public static PropertyDescriptor[] getBeanSetters(Class type) { + return getPropertiesHelper(type, false, true); + } + + /** + * 获取 bean 的属性信息 + * + * @param propertyType 类型 + * @param propertyName 属性名 + * @return {Property} + * @since 1.0.0 + */ + @Nullable + public static TypeDescriptor getTypeDescriptor(Class propertyType, String propertyName) { + Property property = ReflectionUtils.getProperty(propertyType, propertyName); + if (property == null) { + return null; + } + return new TypeDescriptor(property); + } + + /** + * 获取 bean 的属性信息 + * + * @param propertyType 类型 + * @param propertyName 属性名 + * @return {Property} + * @since 1.0.0 + */ + @Nullable + public static Property getProperty(Class propertyType, String propertyName) { + PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(propertyType, propertyName); + if (propertyDescriptor == null) { + return null; + } + return ReflectionUtils.getProperty(propertyType, propertyDescriptor, propertyName); + } + + /** + * 获取 bean 的属性信息 + * + * @param propertyType 类型 + * @param propertyDescriptor PropertyDescriptor + * @param propertyName 属性名 + * @return {Property} + * @since 1.0.0 + */ + @NotNull + public static Property getProperty(Class propertyType, @NotNull PropertyDescriptor propertyDescriptor, String propertyName) { + Method readMethod = propertyDescriptor.getReadMethod(); + Method writeMethod = propertyDescriptor.getWriteMethod(); + return new Property(propertyType, readMethod, writeMethod, propertyName); + } + + /** + * 获取 类属性信息 + * + * @param propertyType 类型 + * @param propertyDescriptor PropertyDescriptor + * @param propertyName 属性名 + * @return {Property} + * @since 1.0.0 + */ + @NotNull + public static TypeDescriptor getTypeDescriptor(Class propertyType, + @NotNull PropertyDescriptor propertyDescriptor, + String propertyName) { + Method readMethod = propertyDescriptor.getReadMethod(); + Method writeMethod = propertyDescriptor.getWriteMethod(); + Property property = new Property(propertyType, readMethod, writeMethod, propertyName); + return new TypeDescriptor(property); + } + + /** + * 获取 所有 field 属性上的注解 + * + * @param 注解泛型 + * @param clazz 类 + * @param fieldName 属性名 + * @param annotationClass 注解 + * @return 注解 annotation + * @since 1.0.0 + */ + @Nullable + public static T getAnnotation(Class clazz, String fieldName, Class annotationClass) { + Field field = ReflectionUtils.getField(clazz, fieldName); + if (field == null) { + return null; + } + return field.getAnnotation(annotationClass); + } + + /** + * 获取 类属性 + * + * @param clazz 类信息 + * @param fieldName 属性名 + * @return Field field + * @since 1.0.0 + */ + @Nullable + public static Field getField(Class clazz, String fieldName) { + while (clazz != Object.class) { + try { + return clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + return null; + } + + /** + * 反射 method 方法名, 例如 getId + * + * @param field field + * @param str 属性字符串内容 + * @return the method capitalize + * @since 1.0.0 + * @deprecated 3.3.0 {@link #guessGetterName(Field, String)} + */ + @Deprecated + public static String getMethodCapitalize(@NotNull Field field, String str) { + Class fieldType = field.getType(); + return guessGetterName(str, fieldType); + } + + /** + * 反射 method 方法名, 例如 setVersion + * + * @param field Field + * @param str String JavaBean类的version属性名 + * @return version属性的setter方法名称 , e.g. setVersion + * @since 1.0.0 + * @deprecated 3.0.8 + */ + @Deprecated + public static String setMethodCapitalize(Field field, String str) { + return concatCapitalize("set", str); + } + + /** + * 拼接字符串第二个字符串第一个字母大写 + * + * @param concatStr concat str + * @param str str + * @return the string + * @since 1.0.0 + */ + private static String concatCapitalize(String concatStr, String str) { + if (StringUtils.isBlank(concatStr)) { + concatStr = StringPool.EMPTY; + } + if (str == null || str.length() == 0) { + return str; + } + + char firstChar = str.charAt(0); + if (Character.isTitleCase(firstChar)) { + // already capitalized + return str; + } + return concatStr + Character.toTitleCase(firstChar) + str.substring(1); + } + + /** + *

+ * 获取 public get方法的值 + *

+ * + * @param cls ignore + * @param entity 实体 + * @param str 属性字符串内容 + * @return Object method value + * @since 1.0.0 + */ + public static Object getMethodValue(Class cls, Object entity, String str) { + Map fieldMaps = getFieldMap(cls); + try { + Assert.notEmpty(fieldMaps, StringUtils.format("Error: NoSuchField in {} for {}. Cause:", cls.getSimpleName(), str)); + Method method = cls.getMethod(guessGetterName(fieldMaps.get(str), str)); + return method.invoke(entity); + } catch (NoSuchMethodException e) { + throw new BaseException("Error: NoSuchMethod in %s. Cause:", e, cls.getSimpleName()); + } catch (IllegalAccessException e) { + throw new BaseException("Error: Cannot execute a private method. in %s. Cause:", e, cls.getSimpleName()); + } catch (InvocationTargetException e) { + throw new BaseException("Error: InvocationTargetException on getMethodValue. Cause:" + e); + } + } + + /** + * 猜测方法名 + * + * @param field 字段 + * @param str 属性字符串内容 + * @return the string + * @since 1.0.0 + */ + private static String guessGetterName(@NotNull Field field, String str) { + return guessGetterName(str, field.getType()); + } + + /** + * 猜测方法属性对应的 Getter 名称, 具体规则请参考 JavaBeans 规范 + * + * @param name 属性名称 + * @param type 属性类型 + * @return 返回猜测的名称 string + * @since 1.0.0 + */ + private static String guessGetterName(String name, Class type) { + return boolean.class == type ? name.startsWith("is") ? name : "is" + StrUtil.upperFirst(name) : "get" + StrUtil.upperFirst(name); + } + + /** + * 获取 public get方法的值 + * + * @param entity 实体 + * @param str 属性字符串内容 + * @return Object method value + * @since 1.0.0 + */ + @Contract("null, _ -> null") + public static Object getMethodValue(Object entity, String str) { + if (null == entity) { + return null; + } + return getMethodValue(entity.getClass(), entity, str); + } + + /** + * 反射对象获取泛型 + * + * @param clazz 对象 + * @param index 泛型所在位置 + * @return Class super class generic type + * @since 1.0.0 + */ + public static Class getSuperClassGenericType(@NotNull Class clazz, int index) { + Type genType = clazz.getGenericSuperclass(); + if (!(genType instanceof ParameterizedType)) { + log.warn("Warn: [{}] superclass not ParameterizedType", clazz.getSimpleName()); + return Object.class; + } + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + if (index >= params.length || index < 0) { + log.warn("Warn: Index: [{}], Size of [{}] Parameterized Type: [{}] .", index, clazz.getSimpleName(), params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) { + log.warn("Warn: [{}] not set the actual class on superclass generic parameter", clazz.getSimpleName()); + return Object.class; + } + return (Class) params[index]; + } + + /** + * 获取该类的所有属性列表 + * + * @param clazz 反射类 + * @return the field map + * @since 1.0.0 + */ + public static Map getFieldMap(Class clazz) { + List fieldList = getFieldList(clazz); + return CollectionUtils.isNotEmpty(fieldList) ? fieldList.stream() + .collect(Collectors.toMap(Field::getName, field -> field)) : Collections.emptyMap(); + } + + /** + * 获取该类的所有属性列表 + * + * @param clazz 反射类 + * @return the field list + * @since 1.0.0 + */ + public static List getFieldList(Class clazz) { + if (Objects.isNull(clazz)) { + return Collections.emptyList(); + } + List fields = CLASS_FIELD_CACHE.get(clazz); + if (CollectionUtils.isEmpty(fields)) { + synchronized (CLASS_FIELD_CACHE) { + fields = doGetFieldList(clazz); + CLASS_FIELD_CACHE.put(clazz, fields); + } + } + return fields; + } + + /** + * 获取该类的所有属性列表 + * + * @param clazz 反射类 + * @return the list + * @since 1.0.0 + */ + public static @NotNull List doGetFieldList(@NotNull Class clazz) { + if (clazz.getSuperclass() != null) { + // 排除重载属性 + Map fieldMap = excludeOverrideSuperField(clazz.getDeclaredFields(), + // 处理父类字段 + getFieldList(clazz.getSuperclass())); + List fieldList = new ArrayList<>(); + /* + * 重写父类属性过滤后处理忽略部分, 支持过滤父类属性功能 + * 场景: 中间表不需要记录创建时间, 忽略父类 createTime 公共属性 + * 中间表实体重写父类属性 ` private transient Date createTime; ` + */ + fieldMap.forEach((k, v) -> { + // 过滤静态属性 + if (!Modifier.isStatic(v.getModifiers()) + // 过滤 transient关键字修饰的属性 + && !Modifier.isTransient(v.getModifiers())) { + fieldList.add(v); + } + }); + return fieldList; + } else { + return Collections.emptyList(); + } + } + + /** + * 排序重置父类属性 + * + * @param fields 子类属性 + * @param superFieldList 父类属性 + * @return the map + * @since 1.0.0 + */ + public static Map excludeOverrideSuperField(Field[] fields, @NotNull List superFieldList) { + // 子类属性 + Map fieldMap = Stream.of(fields) + .collect(toMap(Field::getName, identity(), (u, v) -> { + throw new IllegalStateException(String.format("Duplicate key %s", u)); + }, LinkedHashMap::new)); + superFieldList.stream() + .filter(field -> !fieldMap.containsKey(field.getName())) + .forEach(f -> fieldMap.put(f.getName(), f)); + return fieldMap; + } + + /** + * 获取字段get方法 + * + * @param cls class + * @param field 字段 + * @return Get方法 method + * @since 1.0.0 + */ + public static @NotNull Method getMethod(@NotNull Class cls, Field field) { + try { + return cls.getDeclaredMethod(guessGetterName(field, field.getName())); + } catch (NoSuchMethodException e) { + throw new BaseException("Error: NoSuchMethod in %s. Cause:", e, cls.getName()); + } + } + + /** + * 判断是否为基本类型或基本包装类型 + * + * @param clazz class + * @return 是否基本类型或基本包装类型 boolean + * @since 1.0.0 + */ + public static boolean isPrimitiveOrWrapper(Class clazz) { + Assertions.notNull(clazz, "Class must not be null"); + return (clazz.isPrimitive() || PRIMITIVE_WRAPPER_TYPE_MAP.containsKey(clazz)); + } + + /** + * 循环向上转型, 获取对象的 DeclaredMethod + * + * @param object : 子类对象 + * @param methodName : 父类中的方法名 + * @param parameterTypes : 父类中的方法参数类型 + * @return 父类中的方法对象 declared method + * @since 1.0.0 + */ + public static @Nullable Method getDeclaredMethod(@NotNull Object object, String methodName, Class... parameterTypes) { + Method method; + for (Class clazz = object.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) { + try { + method = clazz.getDeclaredMethod(methodName, parameterTypes); + return method; + } catch (Exception e) { + // 这里甚么都不要做!并且这里的异常必须这样写, 不能抛出去. + // 如果这里的异常打印或者往外抛, 则就不会执行clazz=clazz.getSuperclass(),最后就不会进入到父类中了 + } + } + return null; + } + + /** + * 直接调用对象方法, 而忽略修饰符(private, protected, default) + * + * @param object : 子类对象 + * @param methodName : 父类中的方法名 + * @param parameterTypes : 父类中的方法参数类型 + * @param parameters : 父类中的方法参数 + * @return 父类中方法的执行结果 object + * @since 1.0.0 + */ + public static @Nullable Object invokeMethod(Object object, + String methodName, + Class[] parameterTypes, + Object[] parameters) { + // 根据 对象、方法名和对应的方法参数 通过反射 调用上面的方法获取 Method 对象 + Method method = getDeclaredMethod(object, methodName, parameterTypes); + try { + if (null != method) { + // 抑制Java对方法进行检查,主要是针对私有方法而言 + method.setAccessible(true); + // 调用object 的 method 所代表的方法, 其方法的参数是 parameters + return method.invoke(object, parameters); + } + } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { + throw Exceptions.unchecked(e); + } + return null; + } + + /** + * 循环向上转型, 获取对象的 DeclaredField + * + * @param object : 子类对象 + * @param fieldName : 父类中的属性名 + * @return 父类中的属性对象 declared field + * @since 1.0.0 + */ + public static @Nullable Field getDeclaredField(@NotNull Object object, String fieldName) { + Field field; + Class clazz = object.getClass(); + for (; clazz != Object.class; clazz = clazz.getSuperclass()) { + try { + field = clazz.getDeclaredField(fieldName); + return field; + } catch (Exception e) { + // 这里甚么都不要做!并且这里的异常必须这样写, 不能抛出去. + // 如果这里的异常打印或者往外抛, 则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了 + } + } + return null; + } + + /** + * 直接设置对象属性值, 忽略 private/protected 修饰符, 也不经过 setter + * + * @param object : 子类对象 + * @param fieldName : 父类中的属性名 + * @param value : 将要设置的值 + * @since 1.0.0 + */ + public static void setFieldValue(Object object, String fieldName, Object value) { + // 根据 对象和属性名通过反射 调用上面的方法获取 Field对象 + Field field = getDeclaredField(object, fieldName); + try { + if (field != null) { + // 抑制Java对其的检查 + field.setAccessible(true); + // 将 object 中 field 所代表的值 设置为 value + field.set(object, value); + } + } catch (IllegalArgumentException | IllegalAccessException e) { + throw Exceptions.unchecked(e); + } + + } + + /** + * 直接读取对象的属性值, 忽略 private/protected 修饰符, 也不经过 getter + * + * @param object : 子类对象 + * @param fieldName : 父类中的属性名 + * @return : 父类中的属性值 + * @since 1.0.0 + */ + public static @Nullable Object getFieldValue(Object object, String fieldName) { + + // 根据 对象和属性名通过反射 调用上面的方法获取 Field对象 + Field field = getDeclaredField(object, fieldName); + + try { + if (field != null) { + // 抑制Java对其的检查 + field.setAccessible(true); + // 获取 object 中 field 所代表的属性值 + return field.get(object); + } + } catch (Exception e) { + throw Exceptions.unchecked(e); + } + return null; + } + + /** + * 直接读取对象的属性值, 忽略 private/protected 修饰符, 也不经过 getter + * + * @param parameter + * @param object object + * @param fieldName field name + * @param type type + * @return the field value + * @since 1.7.0 + */ + @SuppressWarnings("unchecked") + public static @Nullable T getFieldValue(Object object, String fieldName, Class type) { + Object fieldValue = getFieldValue(object, fieldName); + return fieldValue != null ? (T) fieldValue : null; + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ResourceUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ResourceUtils.java new file mode 100644 index 0000000..caa81d4 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ResourceUtils.java @@ -0,0 +1,65 @@ +package com.aivfo.el.start.core.util; + +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.core.io.*; +import org.springframework.util.Assert; + +import java.io.IOException; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 资源工具类 + */ +@UtilityClass +public class ResourceUtils extends org.springframework.util.ResourceUtils { + /** + * HTTP_REGEX + */ + public static final String HTTP_REGEX = "^https?:.+$"; + /** + * FTP_URL_PREFIX + */ + public static final String FTP_URL_PREFIX = "ftp:"; + + /** + * 获取资源 + *

+ * 支持以下协议: + *

+ * 1. classpath: + * 2. file: + * 3. ftp: + * 4. http: and https: + * 5. classpath*: + * 6. C:/dir1/ and /Users/lcm + *

+ * + * @param resourceLocation 资源路径 + * @return {Resource} + * @throws IOException IOException + * @since 1.0.0 + */ + @Contract("_ -> new") + @NotNull + @SuppressWarnings("checkstyle:ReturnCount") + public static Resource getResource(String resourceLocation) throws IOException { + Assert.notNull(resourceLocation, "Resource location must not be null"); + if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) { + return new ClassPathResource(resourceLocation); + } + if (resourceLocation.startsWith(FTP_URL_PREFIX)) { + return new UrlResource(resourceLocation); + } + if (resourceLocation.matches(HTTP_REGEX)) { + return new UrlResource(resourceLocation); + } + if (resourceLocation.startsWith(FILE_URL_PREFIX)) { + return new FileUrlResource(resourceLocation); + } + return new FileSystemResource(resourceLocation); + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ResultCodeUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ResultCodeUtils.java new file mode 100644 index 0000000..2c899db --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ResultCodeUtils.java @@ -0,0 +1,127 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.start.core.api.IResultCode; +import com.aivfo.el.starter.base.Result; +import com.aivfo.el.starter.base.annotation.BusinessLevel; +import com.aivfo.el.starter.base.annotation.ModelSerial; +import com.aivfo.el.starter.base.annotation.SystemLevel; +import com.aivfo.el.starter.base.annotation.ThirdLevel; +import com.aivfo.el.starter.base.asserts.Assertions; +import com.aivfo.el.starter.base.utils.StringPool; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Field; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 根据错误码生成一定规则的错误编码【A-项目标识-错误码】 + */ +@UtilityClass +public class ResultCodeUtils { + /** + * 枚举类缓存Map + */ + private static final Map> ENUM_FIELD_CACHE = new ConcurrentHashMap<>(); + + /** + * 初始化枚举类字段等到缓存Map中 + * + * @param resultCode result code + * @since 1.5.0 + */ + private void initEnumMap(@NotNull IResultCode resultCode) { + String simpleName = resultCode.getClass().getSimpleName(); + Field[] fields = resultCode.getClass().getFields(); + + Map collect = Stream.of(fields).collect(toMap(Field::getName, identity(), (k, v) -> { + throw new IllegalStateException(String.format("Duplicate key %s", k)); + }, LinkedHashMap::new)); + + ENUM_FIELD_CACHE.put(simpleName, collect); + } + + /** + * 根据错误码生成一定规则的错误编码【B-项目标识-错误码】 + * + * @param resultCode result code + * @return the string + * @since 1.5.0 + */ + public @NotNull String generateCode(@NotNull IResultCode resultCode) { + String enumName = resultCode.name(); + String simpleName = resultCode.getClass().getSimpleName(); + // 处理错误码中的项目标识部分 + String modelNameSerial = ModelSerial.DEFAULT; + if (resultCode.getClass().isAnnotationPresent(ModelSerial.class)) { + modelNameSerial = resultCode.getClass().getAnnotation(ModelSerial.class).modelName(); + } + modelNameSerial = modelNameSerial.concat(StringPool.DASH); + + // 缓存中获取, 如果缓存中没有, 则需要先初始化到缓存 + if (!ENUM_FIELD_CACHE.containsKey(simpleName)) { + initEnumMap(resultCode); + } + Map stringFieldMap = ENUM_FIELD_CACHE.get(simpleName); + + // 处理错误码中的错误来源部分 + String errorSource = "B"; + if (stringFieldMap.containsKey(enumName)) { + Field field = stringFieldMap.get(enumName); + // 获取错误来源S(系统错误)、B(业务错误)、T(第三方服务错误) + if (field != null && field.isAnnotationPresent(BusinessLevel.class)) { + errorSource = BusinessLevel.class.getSimpleName().substring(0, 1); + } + if (field != null && field.isAnnotationPresent(SystemLevel.class)) { + errorSource = SystemLevel.class.getSimpleName().substring(0, 1); + } + if (field != null && field.isAnnotationPresent(ThirdLevel.class)) { + errorSource = ThirdLevel.class.getSimpleName().substring(0, 1); + } + } + errorSource = errorSource.concat(StringPool.DOT); + + return errorSource + modelNameSerial + resultCode.getCode(); + } + + /** + * 将【B-项目标识-错误码】格式的 code 反解析为 int 类型 错误码 + * + * @param code code + * @return the integer + * @since 1.5.0 + */ + @SuppressWarnings("all") + public @NotNull Integer convert(String code) { + Assertions.notBlank(code, "code 不能为 空"); + + String[] split = code.split(StringPool.DASH); + if (split.length == 2) { + return Integer.parseInt(split[1]); + } else if (split.length == 3) { + return Integer.parseInt(split[2]); + } else { + return Integer.parseInt(code); + } + } + + /** + * Convert + * + * @param result result + * @return the integer + * @since 1.5.0 + */ + public @NotNull Integer convert(Result result) { + Assertions.notNull(result, "result 不能为 空"); + return convert(result.getCode()); + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/SnowflakeBuilder.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/SnowflakeBuilder.java new file mode 100644 index 0000000..475aaf9 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/SnowflakeBuilder.java @@ -0,0 +1,79 @@ +package com.aivfo.el.start.core.util; + +import cn.hutool.core.lang.Snowflake; +import com.aivfo.el.starter.core.util.NetUtils; +import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.StringUtils; + +import java.net.Inet4Address; +import java.net.UnknownHostException; + +/** + *

Description: 雪花算法生成全局唯一 id

+ */ +@UtilityClass +public class SnowflakeBuilder { + + /** + * 活动机器编号 + * + * @return work id + * @since 1.0.0 + */ + private static Long getWorkId() { + try { + String hostAddress = Inet4Address.getLocalHost().getHostAddress(); + int[] ints = StringUtils.toCodePoints(hostAddress); + int sums = 0; + for (int b : ints) { + sums += b; + } + return (long) (sums % 32); + } catch (UnknownHostException e) { + // 如果获取失败, 则使用随机数备用 + return RandomUtils.nextLong(0, 31); + } + } + + /** + * 区域编号 + * + * @return data center id + * @since 1.0.0 + */ + private static Long getDataCenterId() { + String hostName = SystemUtils.getHostName(); + if (StringUtils.isBlank(hostName)) { + hostName = NetUtils.getLocalHost(); + } + int[] ints = StringUtils.toCodePoints(hostName); + int sums = 0; + for (int i : ints) { + sums += i; + } + return (long) (sums % 32); + + } + + /** + * Builder long + * + * @return the long + * @since 1.0.0 + */ + public static long builder() { + return new Snowflake(getWorkId(), getDataCenterId()).nextId(); + } + + /** + * Builder long + * + * @param workerId worker id + * @param regionId region id + * @return the long + * @since 1.0.0 + */ + public static long builder(long workerId, long regionId) { + return new Snowflake(workerId, regionId).nextId(); + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/SystemUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/SystemUtils.java new file mode 100644 index 0000000..4e9fe91 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/SystemUtils.java @@ -0,0 +1,116 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.starter.base.utils.StringUtils; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 系统工具类 + */ +@Slf4j +@UtilityClass +public class SystemUtils extends org.apache.commons.lang3.SystemUtils { + /** + * NAMESPACE + */ + public static final String USER_NAMESPACE = "user.namespace"; + /** + * 获取 user home + */ + @Nullable + public static final String USER_HOME = getProperty("user.home"); + /** + * 获取用户地址 + */ + @Nullable + public static final String USER_DIR = getProperty("user.dir"); + /** + * 获取用户名 + */ + @Nullable + public static final String USER_NAME = getProperty("user.name"); + /** + * os 名 + */ + @Nullable + public static final String OS_NAME = getProperty("os.name"); + /** + * 在启动参数中直接修改 namespace, 优先级最高 + */ + @Nullable + public static final String USER_NAMESPACE_VALUE = getProperty(USER_NAMESPACE); + + /** + * 优先获取 JVM 参数, 然后才是系统环境变量 + * + * @param property property + * @return the system property + * @since 1.0.0 + */ + @Nullable + public static String getProperty(String property) { + String value = System.getProperty(property); + return StringUtils.isBlank(value) ? System.getenv(property) : value; + } + + /** + * Is linux boolean + * + * @return boolean boolean + * @since 1.0.0 + */ + public static boolean isLinux() { + return org.apache.commons.lang3.SystemUtils.IS_OS_LINUX; + } + + /** + * Is mac boolean + * + * @return the boolean + * @since 1.0.0 + */ + @Contract(pure = true) + public static boolean isMac() { + return org.apache.commons.lang3.SystemUtils.IS_OS_MAC; + } + + /** + * Is windows boolean + * + * @return the boolean + * @since 1.0.0 + */ + @Contract(pure = true) + public static boolean isWindows() { + return org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS; + } + + /** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.2.5 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.02.28 19:28 + * @since 1.0.0 + */ + public enum OsType { + /** + * Linux os type + */ + LINUX, + /** + * Mac os type + */ + MAC, + /** + * Windows os type + */ + WINDOWS + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ThreadUtils.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ThreadUtils.java new file mode 100644 index 0000000..7c3f94f --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/ThreadUtils.java @@ -0,0 +1,602 @@ +package com.aivfo.el.start.core.util; + +import cn.hutool.core.thread.ConcurrencyTester; +import cn.hutool.core.thread.ThreadUtil; +import com.aivfo.el.starter.base.asserts.Assertions; +import com.aivfo.el.starter.base.exception.BasicException; +import com.aivfo.el.starter.core.function.CheckedRunnable; +import lombok.Getter; +import lombok.Setter; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.locks.LockSupport; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 多线程工具类 + */ +@Slf4j +@UtilityClass +@SuppressWarnings("checkstyle:MethodLimit") +public class ThreadUtils { + + /** + * Executor + */ + @Setter + @Getter + private ExecutorService executor; + + /** + * Thread sleep + * + * @param millis 时长 + * @since 1.0.0 + */ + public void sleep(long millis) { + sleep(millis, TimeUnit.MILLISECONDS); + } + + /** + * Sleep * + * + * @param time time + * @param timeUnit time unit + * @since 1.0.0 + */ + public void sleep(long time, @NotNull TimeUnit timeUnit) { + LockSupport.parkNanos(timeUnit.toNanos(time)); + } + + /** + * Join + * + * @since 1.0.0 + */ + @SneakyThrows + public void join() { + Thread.currentThread().join(); + } + + /** + * Submit + * + * @param executorService executor service + * @param threadCount thread count + * @param runnable runnable + * @since 16.0 + */ + public void submit(Executor executorService, int threadCount, CheckedRunnable runnable) { + Assertions.isTrue(threadCount > 0, "线程数量必须大于 0"); + for (int i = 0; i < threadCount; i++) { + execute(executorService, runnable, null); + } + + } + + /** + * 新增线程执行逻辑 + * + * @param threadCount thread count + * @param runnable runnable + * @since 1.6.0 + */ + public void submit(int threadCount, CheckedRunnable runnable) { + Assertions.isTrue(threadCount > 0, "线程数量必须大于 0"); + + if (executor == null) { + executor = newExecutor(threadCount, threadCount); + } + + submit(executor, threadCount, runnable); + } + + /** + * 提交多个并行任务 + * + * @param runnables runnables + * @since 1.7.1 + */ + public void submit(List runnables) throws BrokenBarrierException, InterruptedException { + if (executor == null) { + executor = newExecutor(5, 5); + } + + CyclicBarrier barrier = new CyclicBarrier(runnables.size()); + + runnables.forEach(runnable -> execute(executor, runnable, barrier)); + barrier.await(); + } + + /** + * 执行任务 + * + * @param executor executor + * @param runnable runnable + * @param barrier barrier + * @since 1.7.1 + */ + private static void execute(Executor executor, CheckedRunnable runnable, CyclicBarrier barrier) { + executor.execute(() -> { + try { + runnable.run(); + } catch (Throwable throwable) { + if (throwable instanceof BasicException) { + BasicException exception = (BasicException) throwable; + log.error("线程池执行异常: <{}>: {}:{}", exception.getTraceId(), exception.getCode(), exception.getMessage()); + return; + } + log.error("", throwable); + } + + if (barrier != null) { + try { + barrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + log.error(e.getMessage(), e); + } + } + }); + } + + /** + * Submit + * + * @param runnable runnable + * @since 1.6.0 + */ + @SuppressWarnings("PMD.ThreadPoolCreationRule") + public void submit(CheckedRunnable runnable) { + Executor fixedThreadPool = Executors.newSingleThreadExecutor(); + submit(fixedThreadPool, 1, runnable); + } + + /** + * 多线程测试用例 + * + * @param threadCount 开启线程数 + * @param runnable runnable + * @param callback 线程结束后执行的方法 + * @since 1.0.0 + */ + @SuppressWarnings("all") + public void submit(int threadCount, CheckedRunnable runnable, CheckedRunnable callback) { + Assertions.isTrue(threadCount > 0, "线程数量必须大于 0"); + CountDownLatch countDownLatch = new CountDownLatch(1); + + if (executor == null) { + executor = newExecutor(threadCount, threadCount); + } + + for (int i = 0; i < threadCount; i++) { + executor.execute(() -> { + try { + countDownLatch.await(); + runnable.run(); + } catch (Throwable throwable) { + if (throwable instanceof BasicException) { + throw (BasicException) throwable; + } + log.error("", throwable); + } + }); + } + countDownLatch.countDown(); + executor.shutdown(); + while (!executor.isTerminated()) { + try { + Thread.sleep(0L); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } + } + try { + callback.run(); + } catch (Throwable throwable) { + if (throwable instanceof BasicException) { + throw (BasicException) throwable; + } + log.error(throwable.getMessage(), throwable); + } + } + + /** + * New executor + * + * @param corePoolSize core pool size + * @return the executor service + * @since 1.7.0 + */ + public static ExecutorService newExecutor(int corePoolSize) { + return ThreadUtil.newExecutor(corePoolSize); + } + + /** + * New executor + * + * @return the executor service + * @since 1.7.0 + */ + public static ExecutorService newExecutor() { + return ThreadUtil.newExecutor(); + } + + /** + * New single executor + * + * @return the executor service + * @since 1.7.0 + */ + public static ExecutorService newSingleExecutor() { + return ThreadUtil.newSingleExecutor(); + } + + /** + * New executor + * + * @param corePoolSize core pool size + * @param maximumPoolSize maximum pool size + * @return the thread pool executor + * @since 1.7.0 + */ + public static ThreadPoolExecutor newExecutor(int corePoolSize, int maximumPoolSize) { + return ThreadUtil.newExecutor(corePoolSize, maximumPoolSize); + } + + /** + * New executor + * + * @param corePoolSize core pool size + * @param maximumPoolSize maximum pool size + * @param maximumQueueSize maximum queue size + * @return the executor service + * @since 1.7.0 + */ + public static ExecutorService newExecutor(int corePoolSize, int maximumPoolSize, int maximumQueueSize) { + return ThreadUtil.newExecutor(corePoolSize, maximumPoolSize, maximumQueueSize); + } + + /** + * New executor by blocking coefficient + * + * @param blockingCoefficient blocking coefficient + * @return the thread pool executor + * @since 1.7.0 + */ + public static ThreadPoolExecutor newExecutorByBlockingCoefficient(float blockingCoefficient) { + return ThreadUtil.newExecutorByBlockingCoefficient(blockingCoefficient); + + } + + /** + * Execute + * + * @param runnable runnable + * @since 1.7.0 + */ + public static void execute(Runnable runnable) { + ThreadUtil.execute(runnable); + } + + /** + * Exec async + * + * @param runnable runnable + * @param isDaemon is daemon + * @return the runnable + * @since 1.7.0 + */ + @Contract("_, _ -> param1") + public static Runnable execAsync(Runnable runnable, boolean isDaemon) { + return ThreadUtil.execAsync(runnable, isDaemon); + } + + /** + * Exec async + * + * @param parameter + * @param task task + * @return the future + * @since 1.7.0 + */ + public static Future execAsync(Callable task) { + return ThreadUtil.execAsync(task); + } + + /** + * Exec async + * + * @param runnable runnable + * @return the future + * @since 1.7.0 + */ + public static Future execAsync(Runnable runnable) { + return ThreadUtil.execAsync(runnable); + } + + /** + * New completion service + * + * @param parameter + * @return the completion service + * @since 1.7.0 + */ + @Contract(" -> new") + public static @NotNull CompletionService newCompletionService() { + return ThreadUtil.newCompletionService(); + } + + /** + * New completion service + * + * @param parameter + * @param executor executor + * @return the completion service + * @since 1.7.0 + */ + @Contract("_ -> new") + public static @NotNull CompletionService newCompletionService(ExecutorService executor) { + return ThreadUtil.newCompletionService(executor); + } + + /** + * New count down latch + * + * @param threadCount thread count + * @return the count down latch + * @since 1.7.0 + */ + public static CountDownLatch newCountDownLatch(int threadCount) { + return ThreadUtil.newCountDownLatch(threadCount); + } + + /** + * New thread + * + * @param runnable runnable + * @param name name + * @return the thread + * @since 1.7.0 + */ + @Contract("_, _ -> new") + public static @NotNull + Thread newThread(Runnable runnable, String name) { + return ThreadUtil.newThread(runnable, name); + } + + /** + * New thread + * + * @param runnable runnable + * @param name name + * @param isDaemon is daemon + * @return the thread + * @since 1.7.0 + */ + @Contract("_, _, _ -> new") + public static @NotNull + Thread newThread(Runnable runnable, String name, boolean isDaemon) { + return ThreadUtil.newThread(runnable, name, isDaemon); + } + + /** + * Safe sleep + * + * @param millis millis + * @return the boolean + * @since 1.7.0 + */ + @Contract("null -> true") + public static boolean safeSleep(Number millis) { + return ThreadUtil.safeSleep(millis); + } + + /** + * Safe sleep + * + * @param millis millis + * @return the boolean + * @since 1.7.0 + */ + public static boolean safeSleep(long millis) { + return ThreadUtil.safeSleep(millis); + + } + + /** + * Get stack trace + * + * @return the stack trace element [ ] + * @since 1.7.0 + */ + public static StackTraceElement[] getStackTrace() { + return ThreadUtil.getStackTrace(); + } + + /** + * Gets stack trace element * + * + * @param i 堆栈的层级 + * @return the stack trace element + * @since 1.7.0 + */ + public static StackTraceElement getStackTraceElement(int i) { + return ThreadUtil.getStackTraceElement(i); + + } + + /** + * Create thread local + * + * @param parameter + * @param isInheritable is inheritable + * @return the thread local + * @since 1.7.0 + */ + @Contract("_ -> new") + public static @NotNull ThreadLocal createThreadLocal(boolean isInheritable) { + return ThreadUtil.createThreadLocal(isInheritable); + } + + /** + * Create thread factory builder + * + * @return the cn . hutool . core . thread . thread factory builder + * @since 1.7.0 + */ + @Contract(value = " -> new", pure = true) + public static cn.hutool.core.thread.@NotNull ThreadFactoryBuilder createThreadFactoryBuilder() { + return ThreadUtil.createThreadFactoryBuilder(); + } + + /** + * Interrupt + * + * @param thread thread + * @param isJoin is join + * @since 1.7.0 + */ + public static void interrupt(Thread thread, boolean isJoin) { + ThreadUtil.interrupt(thread, isJoin); + + } + + /** + * Wait for die + * + * @since 1.7.0 + */ + public static void waitForDie() { + ThreadUtil.waitForDie(); + } + + /** + * Wait for die + * + * @param thread thread + * @since 1.7.0 + */ + public static void waitForDie(Thread thread) { + ThreadUtil.waitForDie(); + } + + /** + * Get threads + * + * @return the thread [ ] + * @since 1.7.0 + */ + @Contract(" -> new") + public static Thread[] getThreads() { + return ThreadUtil.getThreads(); + } + + /** + * Get threads + * + * @param group group + * @return the thread [ ] + * @since 1.7.0 + */ + @Contract("_ -> new") + public static Thread[] getThreads(ThreadGroup group) { + return ThreadUtil.getThreads(group); + } + + /** + * Gets main thread * + * + * @return the main thread + * @since 1.7.0 + */ + public static Thread getMainThread() { + return ThreadUtil.getMainThread(); + } + + /** + * Current thread group + * + * @return the thread group + * @since 1.7.0 + */ + public static ThreadGroup currentThreadGroup() { + return ThreadUtil.currentThreadGroup(); + } + + /** + * New named thread factory + * + * @param prefix prefix + * @param isDaemon is daemon + * @return the thread factory + * @since 1.7.0 + */ + @Contract("_, _ -> new") + public static @NotNull + ThreadFactory newNamedThreadFactory(String prefix, boolean isDaemon) { + return ThreadUtil.newNamedThreadFactory(prefix, isDaemon); + } + + /** + * New named thread factory + * + * @param prefix prefix + * @param threadGroup thread group + * @param isDaemon is daemon + * @return the thread factory + * @since 1.7.0 + */ + @Contract("_, _, _ -> new") + public static @NotNull + ThreadFactory newNamedThreadFactory(String prefix, ThreadGroup threadGroup, boolean isDaemon) { + return ThreadUtil.newNamedThreadFactory(prefix, threadGroup, isDaemon); + } + + /** + * New named thread factory + * + * @param prefix prefix + * @param threadGroup thread group + * @param isDaemon is daemon + * @param handler handler + * @return the thread factory + * @since 1.7.0 + */ + @Contract("_, _, _, _ -> new") + public static @NotNull + ThreadFactory newNamedThreadFactory(String prefix, ThreadGroup threadGroup, boolean isDaemon, + Thread.UncaughtExceptionHandler handler) { + return ThreadUtil.newNamedThreadFactory(prefix, threadGroup, isDaemon, handler); + } + + /** + * Sync + * + * @param obj obj + * @since 1.7.0 + */ + public static void sync(Object obj) { + ThreadUtil.sync(obj); + + } + + /** + * Concurrency test + * + * @param threadSize thread size + * @param runnable runnable + * @return the concurrency tester + * @since 1.7.0 + */ + public static ConcurrencyTester concurrencyTest(int threadSize, Runnable runnable) { + return ThreadUtil.concurrencyTest(threadSize, runnable); + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/Tools.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/Tools.java new file mode 100644 index 0000000..4695216 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/Tools.java @@ -0,0 +1,2846 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.start.core.enums.RandomType; +import com.aivfo.el.starter.base.support.StrFormatter; +import com.aivfo.el.starter.base.utils.*; +import com.aivfo.el.starter.core.function.CheckedConsumer; +import com.aivfo.el.starter.core.function.CheckedRunnable; +import com.aivfo.el.starter.core.util.NumberUtils; +import com.aivfo.el.starter.core.util.UrlUtils; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Maps; +import com.google.common.primitives.Ints; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.beans.BeansException; +import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.io.Resource; +import org.springframework.util.PatternMatchUtils; +import org.springframework.util.StopWatch; +import org.springframework.web.method.HandlerMethod; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Duration; +import java.time.format.DateTimeFormatter; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.util.*; +import java.util.function.Supplier; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 工具包集合, 只做简单的调用, 不删除原有工具类 + */ +@UtilityClass +@SuppressWarnings("all") +public class Tools { + + /** + * 断言,必须不能为 null + *
+     * public Foo(Bar bar) {
+     *     this.bar = $.requireNotNull(bar);
+     * }
+     * 
+ * + * @param the type of the reference + * @param obj the object reference to check for nullity + * @return {@code obj} if not {@code null} + * @throws NullPointerException if {@code obj} is {@code null} + * @since 1.0.0 + */ + @Contract(value = "null -> fail; !null -> param1", pure = true) + public static T requireNotNull(T obj) { + return Objects.requireNonNull(obj); + } + + /** + * 断言,必须不能为 null + *
+     * public Foo(Bar bar, Baz baz) {
+     *     this.bar = $.requireNotNull(bar, "bar must not be null");
+     *     this.baz = $.requireNotNull(baz, "baz must not be null");
+     * }
+     * 
+ * + * @param the type of the reference + * @param obj the object reference to check for nullity + * @param message detail message to be used in the event that a {@code + * NullPointerException} is thrown + * @return {@code obj} if not {@code null} + * @throws NullPointerException if {@code obj} is {@code null} + * @since 1.0.0 + */ + @Contract(value = "null, _ -> fail; !null, _ -> param1", pure = true) + public static T requireNotNull(T obj, String message) { + return Objects.requireNonNull(obj, message); + } + + /** + * 断言,必须不能为 null + *
+     * public Foo(Bar bar, Baz baz) {
+     *     this.bar = $.requireNotNull(bar, () -> "bar must not be null");
+     * }
+     * 
+ * + * @param the type of the reference + * @param obj the object reference to check for nullity + * @param messageSupplier supplier of the detail message to be used in the event that a {@code NullPointerException} is thrown + * @return {@code obj} if not {@code null} + * @throws NullPointerException if {@code obj} is {@code null} + * @since 1.0.0 + */ + @Contract("null, _ -> fail; !null, _ -> param1") + public static T requireNotNull(T obj, Supplier messageSupplier) { + return Objects.requireNonNull(obj, messageSupplier); + } + + /** + * 判断对象为true + * + * @param object 对象 + * @return 对象是否为true boolean + * @since 1.0.0 + */ + @Contract(value = "null -> false", pure = true) + public static boolean isTrue(@Nullable Boolean object) { + return ObjectUtils.isTrue(object); + } + + /** + * 判断对象为false + * + * @param object 对象 + * @return 对象是否为false boolean + * @since 1.0.0 + */ + @Contract(value = "null -> true", pure = true) + public static boolean isFalse(@Nullable Boolean object) { + return ObjectUtils.isFalse(object); + } + + /** + * 判断对象是否为null + *

+ * This method exists to be used as a + * {@link java.util.function.Predicate}, {@code context($::isNull)} + *

+ * + * @param obj a reference to be checked against {@code null} + * @return {@code true} if the provided reference is {@code null} otherwise {@code false} + * @see java.util.function.Predicate + * @since 1.0.0 + */ + @Contract(value = "!null -> false; null -> true", pure = true) + public static boolean isNull(@Nullable Object obj) { + return Objects.isNull(obj); + } + + /** + * 判断对象是否 not null + *

+ * This method exists to be used as a + * {@link java.util.function.Predicate}, {@code context($::notNull)} + *

+ * + * @param obj a reference to be checked against {@code null} + * @return {@code true} if the provided reference is non-{@code null} otherwise {@code false} + * @see java.util.function.Predicate + * @since 1.0.0 + */ + @Contract(value = "!null -> true; null -> false", pure = true) + public static boolean isNotNull(@Nullable Object obj) { + return Objects.nonNull(obj); + } + + /** + * 首字母变小写 + * + * @param str 字符串 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String firstCharToLower(String str) { + return StringUtils.firstCharToLower(str); + } + + /** + * 首字母变大写 + * + * @param str 字符串 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String firstCharToUpper(String str) { + return StringUtils.firstCharToUpper(str); + } + + /** + * 判断不为空字符串 + *
+     * $.isNotBlank(null)     = false
+     * $.isNotBlank("")          = false
+     * $.isNotBlank(" ")     = false
+     * $.isNotBlank("bob")     = true
+     * $.isNotBlank("  bob  ") = true
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is not empty and not null and not whitespace + * @see Character#isWhitespace Character#isWhitespaceCharacter#isWhitespaceCharacter#isWhitespaceCharacter#isWhitespace + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isNotBlank(@Nullable CharSequence cs) { + return StringUtils.isNotBlank(cs); + } + + /** + * 有 任意 一个 Blank + * + * @param css CharSequence + * @return boolean boolean + * @since 1.0.0 + */ + public static boolean isAnyBlank(CharSequence... css) { + return StringUtils.isAnyBlank(css); + } + + /** + * 判断是否全为非空字符串 + * + * @param css CharSequence + * @return boolean boolean + * @since 1.0.0 + */ + public static boolean isNoneBlank(CharSequence... css) { + return StringUtils.isNoneBlank(css); + } + + /** + * 判断对象是数组 + * + * @param obj the object to check + * @return 是否数组 boolean + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isArray(@Nullable Object obj) { + return ObjectUtils.isArray(obj); + } + + /** + * 对象不为空 object、map、list、set、字符串、数组 + * + * @param obj the object to check + * @return 是否不为空 boolean + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isNotEmpty(@Nullable Object obj) { + return !ObjectUtils.isEmpty(obj); + } + + /** + * 判断数组为空 + * + * @param array the array to check + * @return 数组是否为空 boolean + * @since 1.0.0 + */ + @Contract(value = "null -> true", pure = true) + public static boolean isEmpty(@Nullable Object[] array) { + return ObjectUtils.isEmpty(array); + } + + /** + * 判断数组不为空 + * + * @param array 数组 + * @return 数组是否不为空 boolean + * @since 1.0.0 + */ + @Contract("null -> false") + public static boolean isNotEmpty(@Nullable Object[] array) { + return ObjectUtils.isNotEmpty(array); + } + + /** + * 对象组中是否存在 Empty Object + * + * @param os 对象组 + * @return boolean boolean + * @since 1.0.0 + */ + public static boolean hasEmpty(@NotNull Object... os) { + for (Object o : os) { + if (isEmpty(o)) { + return true; + } + } + return false; + } + + /** + * 判断空对象 object、map、list、set、字符串、数组 + * + * @param obj the object to check + * @return 数组是否为空 boolean + * @since 1.0.0 + */ + @Contract("null -> true") + public static boolean isEmpty(@Nullable Object obj) { + return ObjectUtils.isEmpty(obj); + } + + /** + * 对象组中是否全是 Empty Object + * + * @param os 对象组 + * @return boolean boolean + * @since 1.0.0 + */ + public static boolean allEmpty(@NotNull Object... os) { + for (Object o : os) { + if (!isEmpty(o)) { + return false; + } + } + return true; + } + + /** + * 将字符串中特定模式的字符转换成map中对应的值 + *

+ * use: format("my name is ${name}, and i like ${like}!", {"name":"L.cm", "like": "Java"}) + * + * @param message 需要转换的字符串 + * @param params 转换所需的键值对集合 + * @return 转换后的字符串 string + * @since 1.0.0 + */ + @Contract("null, _ -> null; !null, null -> !null") + public static String format(@Nullable String message, @Nullable Map params) { + return StringUtils.format(message, params); + } + + /** + * 同 log 格式的 format 规则 + *

+ * use: format("my name is {}, and i like {}!", "L.cm", "Java") + * + * @param message 需要转换的字符串 + * @param arguments 需要替换的变量 + * @return 转换后的字符串 string + * @since 1.0.0 + */ + @Contract("null, _ -> null") + public static String format(@Nullable String message, @Nullable Object... arguments) { + return StringUtils.format(message, arguments); + } + + /** + * 清理字符串,清理出某些不可见字符和一些sql特殊字符 + * + * @param txt 文本 + * @return {String} + * @since 1.0.0 + */ + @Contract("null -> null") + @Nullable + public static String cleanText(@Nullable String txt) { + return StringUtils.cleanText(txt); + } + + /** + * 获取标识符,用于参数清理 + * + * @param param 参数 + * @return 清理后的标识符 string + * @since 1.0.0 + */ + @Contract("null -> null") + @Nullable + public static String cleanIdentifier(@Nullable String param) { + return StringUtils.cleanIdentifier(param); + } + + /** + * 比较两个对象是否相等.
+ * 相同的条件有两个,满足其一即可:
+ * + * @param obj1 对象1 + * @param obj2 对象2 + * @return 是否相等 boolean + * @since 1.0.0 + */ + @Contract(value = "null, !null -> false; !null, null -> false", pure = true) + public static boolean equals(Object obj1, Object obj2) { + return Objects.equals(obj1, obj2); + } + + /** + * Determine if the given objects are equal, returning {@code true} if + * both are {@code null} or {@code false} if only one is {@code null}. + *

Compares arrays with {@code Arrays.equals}, performing an equality + * check based on the array elements rather than the array reference. + * + * @param o1 first Object to compare + * @param o2 second Object to compare + * @return whether the given objects are equal + * @see Object#equals(Object) Object#equals(Object)Object#equals(Object)Object#equals(Object)Object#equals(Object)Object#equals + * (Object)Object#equals(Object) + * @see Arrays#equals Arrays#equalsArrays#equalsArrays#equalsArrays#equalsArrays#equalsArrays#equals + * @since 1.0.0 + */ + public static boolean equalsSafe(@Nullable Object o1, @Nullable Object o2) { + return ObjectUtils.nullSafeEquals(o1, o2); + } + + /** + * 比较两个对象是否不相等.
+ * + * @param o1 对象1 + * @param o2 对象2 + * @return 是否不eq boolean + * @since 1.0.0 + */ + @Contract(value = "null, !null -> true; !null, null -> true", pure = true) + public static boolean isNotEqual(Object o1, Object o2) { + return !Objects.equals(o1, o2); + } + + /** + * 返回对象的 hashCode + * + * @param obj Object + * @return hashCode int + * @since 1.0.0 + */ + public static int hashCode(@Nullable Object obj) { + return Objects.hashCode(obj); + } + + /** + * 如果对象为null,返回默认值 + * + * @param object Object + * @param defaultValue 默认值 + * @return Object object + * @since 1.0.0 + */ + @Contract(value = "!null, _ -> param1; null, _ -> param2", pure = true) + public static Object defaultIfNull(@Nullable Object object, Object defaultValue) { + return object != null ? object : defaultValue; + } + + /** + * 判断数组中是否包含元素 + * + * @param The generic tag + * @param array the Array to check + * @param element the element to look for + * @return {@code true} if found, {@code false} else + * @since 1.0.0 + */ + @Contract("null, _ -> false") + public static boolean contains(@Nullable T[] array, T element) { + return CollectionUtils.contains(array, element); + } + + /** + * 判断迭代器中是否包含元素 + * + * @param iterator the Iterator to check + * @param element the element to look for + * @return {@code true} if found, {@code false} otherwise + * @since 1.0.0 + */ + @Contract("null, _ -> false") + public static boolean contains(@Nullable Iterator iterator, Object element) { + return CollectionUtils.contains(iterator, element); + } + + /** + * 判断枚举是否包含该元素 + * + * @param enumeration the Enumeration to check + * @param element the element to look for + * @return {@code true} if found, {@code false} otherwise + * @since 1.0.0 + */ + @Contract("null, _ -> false") + public static boolean contains(@Nullable Enumeration enumeration, Object element) { + return CollectionUtils.contains(enumeration, element); + } + + /** + * Concatenates 2 arrays + * + * @param one 数组1 + * @param other 数组2 + * @return 新数组 string [ ] + * @since 1.0.0 + */ + public static String[] concat(String[] one, String[] other) { + return CollectionUtils.concat(one, other, String.class); + } + + /** + * Concatenates 2 arrays + * + * @param parameter + * @param one 数组1 + * @param other 数组2 + * @param clazz 数组类 + * @return 新数组 t [ ] + * @since 1.0.0 + */ + public static T[] concat(T[] one, T[] other, Class clazz) { + return CollectionUtils.concat(one, other, clazz); + } + + /** + * 不可变 Set + * + * @param 泛型 + * @param es 对象 + * @return 集合 set + * @since 1.0.0 + */ + public static Set ofImmutableSet(E[] es) { + return CollectionUtils.ofImmutableSet(es); + } + + /** + * 不可变 List + * + * @param 泛型 + * @param es 对象 + * @return 集合 list + * @since 1.0.0 + */ + public static List ofImmutableList(E[] es) { + return CollectionUtils.ofImmutableList(es); + } + + /** + * 强转string,并去掉多余空格 + * + * @param str 字符串 + * @return String string + * @since 1.0.0 + */ + @Contract(pure = true) + public static String toStr(Object str) { + return toStr(str, ""); + } + + /** + * 强转string,并去掉多余空格 + * + * @param str 字符串 + * @param defaultValue 默认值 + * @return String string + * @since 1.0.0 + */ + @Contract(value = "null, _ -> param2", pure = true) + public static String toStr(Object str, String defaultValue) { + return StrFormatter.toStr(str, defaultValue); + } + + /** + * 判断一个字符串是否是数字 + * + * @param cs the CharSequence to check, may be null + * @return {boolean} + * @since 1.0.0 + */ + public static boolean isNumeric(CharSequence cs) { + return StringUtils.isNumeric(cs); + } + + /** + *

Convert a String to an int, returning + * zero if the conversion fails.

+ *

If the string is null, zero is returned.

+ *
+     *   $.toInt(null) = 0
+     *   $.toInt("")   = 0
+     *   $.toInt("1")  = 1
+     * 
+ * + * @param value the string to convert, may be null + * @return the int represented by the string, or zero if conversion fails + * @since 1.0.0 + */ + public static int toInt(Object value) { + return NumberUtils.toInt(String.valueOf(value)); + } + + /** + *

Convert a String to a long, returning + * zero if the conversion fails.

+ *

If the string is null, zero is returned.

+ *
+     *   $.toLong(null) = 0L
+     *   $.toLong("")   = 0L
+     *   $.toLong("1")  = 1L
+     * 
+ * + * @param value the string to convert, may be null + * @return the long represented by the string, or 0 if conversion fails + * @since 1.0.0 + */ + public static long toLong(Object value) { + return NumberUtils.toLong(String.valueOf(value)); + } + + /** + *

Convert a String to a long, returning a + * default value if the conversion fails.

+ *

If the string is null, the default value is returned.

+ *
+     *   $.toLong(null, 1L) = 1L
+     *   $.toLong("", 1L)   = 1L
+     *   $.toLong("1", 0L)  = 1L
+     * 
+ * + * @param value the string to convert, may be null + * @param defaultValue the default value + * @return the long represented by the string, or the default if conversion fails + * @since 1.0.0 + */ + public static long toLong(Object value, long defaultValue) { + return NumberUtils.toLong(String.valueOf(value), defaultValue); + } + + /** + *

Convert a String to an Double, returning a + * default value if the conversion fails.

+ *

If the string is null, the default value is returned.

+ *
+     *   $.toDouble(null, 1) = 1.0
+     *   $.toDouble("", 1)   = 1.0
+     *   $.toDouble("1", 0)  = 1.0
+     * 
+ * + * @param value the string to convert, may be null + * @return the int represented by the string, or the default if conversion fails + * @since 1.0.0 + */ + public static Double toDouble(Object value) { + return toDouble(String.valueOf(value), -1.00); + } + + /** + *

Convert a String to an Double, returning a + * default value if the conversion fails.

+ *

If the string is null, the default value is returned.

+ *
+     *   $.toDouble(null, 1) = 1.0
+     *   $.toDouble("", 1)   = 1.0
+     *   $.toDouble("1", 0)  = 1.0
+     * 
+ * + * @param value the string to convert, may be null + * @param defaultValue the default value + * @return the int represented by the string, or the default if conversion fails + * @since 1.0.0 + */ + public static Double toDouble(Object value, Double defaultValue) { + return NumberUtils.toDouble(String.valueOf(value), defaultValue); + } + + /** + *

Convert a String to an Float, returning a + * default value if the conversion fails.

+ *

If the string is null, the default value is returned.

+ *
+     *   $.toFloat(null, 1) = 1.00f
+     *   $.toFloat("", 1)   = 1.00f
+     *   $.toFloat("1", 0)  = 1.00f
+     * 
+ * + * @param value the string to convert, may be null + * @return the int represented by the string, or the default if conversion fails + * @since 1.0.0 + */ + public static Float toFloat(Object value) { + return toFloat(String.valueOf(value), -1.0f); + } + + /** + *

Convert a String to an Float, returning a + * default value if the conversion fails.

+ *

If the string is null, the default value is returned.

+ *
+     *   $.toFloat(null, 1) = 1.00f
+     *   $.toFloat("", 1)   = 1.00f
+     *   $.toFloat("1", 0)  = 1.00f
+     * 
+ * + * @param value the string to convert, may be null + * @param defaultValue the default value + * @return the int represented by the string, or the default if conversion fails + * @since 1.0.0 + */ + public static Float toFloat(Object value, Float defaultValue) { + return NumberUtils.toFloat(String.valueOf(value), defaultValue); + } + + /** + * 对象转为 Byte + * + * @param object Object + * @return 结果 byte + * @since 1.0.0 + */ + public static byte toByte(@Nullable Object object) { + return ObjectUtils.toByte(object, (byte) 0); + } + + /** + * 对象转为 Byte + * + * @param object Object + * @param defaultValue byte + * @return 结果 byte + * @since 1.0.0 + */ + @Contract("null, _ -> param2") + public static byte toByte(@Nullable Object object, byte defaultValue) { + return ObjectUtils.toByte(object, defaultValue); + } + + /** + * 对象转为 Short + * + * @param object Object + * @return 结果 short + * @since 1.0.0 + */ + public static short toShort(@Nullable Object object) { + return ObjectUtils.toShort(object, (short) 0); + } + + /** + * 对象转为 Short + * + * @param object Object + * @param defaultValue short + * @return 结果 short + * @since 1.0.0 + */ + @Contract("null, _ -> param2") + public static short toShort(@Nullable Object object, short defaultValue) { + return ObjectUtils.toShort(object, defaultValue); + } + + /** + *

Convert a String to an Boolean, returning a + * default value if the conversion fails.

+ *

If the string is null, the default value is returned.

+ *
+     *   $.toBoolean("true", true)  = true
+     *   $.toBoolean("false")        = false
+     *   $.toBoolean("", false)       = false
+     * 
+ * + * @param value the string to convert, may be null + * @return the int represented by the string, or the default if conversion fails + * @since 1.0.0 + */ + public static Boolean toBoolean(Object value) { + return toBoolean(value, null); + } + + /** + *

Convert a String to an Boolean, returning a + * default value if the conversion fails.

+ *

If the string is null, the default value is returned.

+ *
+     *   $.toBoolean("true", true)  = true
+     *   $.toBoolean("false")        = false
+     *   $.toBoolean("", false)       = false
+     * 
+ * + * @param value the string to convert, may be null + * @param defaultValue the default value + * @return the int represented by the string, or the default if conversion fails + * @since 1.0.0 + */ + @Contract("null, _ -> param2") + public static Boolean toBoolean(Object value, Boolean defaultValue) { + if (value != null) { + String val = String.valueOf(value); + val = val.toLowerCase().trim(); + return Boolean.parseBoolean(val); + } + return defaultValue; + } + + /** + * 转换为Integer集合
+ * + * @param str 结果被转换的值 + * @return 结果 list + * @since 1.0.0 + */ + @NotNull + public static List toIntList(String str) { + return Arrays.asList(toIntArray(str)); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 integer [ ] + * @since 1.0.0 + */ + public static Integer[] toIntArray(String str) { + return toIntArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 integer [ ] + * @since 1.0.0 + */ + public static Integer[] toIntArray(String split, String str) { + if (StringUtils.isEmpty(str)) { + return new Integer[]{}; + } + String[] arr = str.split(split); + Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) { + int v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + *

Convert a String to an int, returning a + * default value if the conversion fails.

+ *

If the string is null, the default value is returned.

+ *
+     *   $.toInt(null, 1) = 1
+     *   $.toInt("", 1)   = 1
+     *   $.toInt("1", 0)  = 1
+     * 
+ * + * @param value the string to convert, may be null + * @param defaultValue the default value + * @return the int represented by the string, or the default if conversion fails + * @since 1.0.0 + */ + public static int toInt(Object value, int defaultValue) { + return NumberUtils.toInt(String.valueOf(value), defaultValue); + } + + /** + * 转换为Integer集合
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 list + * @since 1.0.0 + */ + @NotNull + public static List toIntList(String split, String str) { + return Arrays.asList(toIntArray(split, str)); + } + + /** + * 转换为String集合
+ * + * @param str 结果被转换的值 + * @return 结果 list + * @since 1.0.0 + */ + @NotNull + public static List toStrList(String str) { + return Arrays.asList(toStrArray(str)); + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 string [ ] + * @since 1.0.0 + */ + @NotNull + public static String[] toStrArray(String str) { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 string [ ] + * @since 1.0.0 + */ + @NotNull + public static String[] toStrArray(String split, String str) { + if (isBlank(str)) { + return new String[0]; + } + return str.split(split); + } + + /** + * 判断是否为空字符串 + *
+     * $.isBlank(null) = true
+     * $.isBlank("") = true
+     * $.isBlank(" ") = true
+     * $.isBlank("12345") = false
+     * $.isBlank(" 12345 ") = false
+     * 
+ * + * @param cs the {@code CharSequence} to check (may be {@code null}) + * @return {@code true} if the {@code CharSequence} is not {@code null}, its length is greater than 0, and it does not contain + * whitespace only + * @see Character#isWhitespace Character#isWhitespaceCharacter#isWhitespaceCharacter#isWhitespaceCharacter#isWhitespace + * @since 1.0.0 + */ + @Contract("null -> true") + public static boolean isBlank(@Nullable CharSequence cs) { + return StringUtils.isBlank(cs); + } + + /** + * 转换为String集合
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 list + * @since 1.0.0 + */ + @NotNull + public static List toStrList(String split, String str) { + return Arrays.asList(toStrArray(split, str)); + } + + /** + * 将 long 转短字符串 为 62 进制 + * + * @param num 数字 + * @return 短字符串 string + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static String to62String(long num) { + return NumberUtils.to62String(num); + } + + /** + * 将集合拼接成字符串,默认使用`,`拼接 + * + * @param coll the {@code Collection} to convert + * @return the delimited {@code String} + * @since 1.0.0 + */ + @NotNull + public static String join(Collection coll) { + return StringUtils.join(coll); + } + + /** + * 将集合拼接成字符串,默认指定分隔符 + * + * @param coll the {@code Collection} to convert + * @param delim the delimiter to use (typically a ",") + * @return the delimited {@code String} + * @since 1.0.0 + */ + @NotNull + public static String join(Collection coll, String delim) { + return StringUtils.join(coll, delim); + } + + /** + * 将数组拼接成字符串,默认使用`,`拼接 + * + * @param arr the array to display + * @return the delimited {@code String} + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static String join(Object[] arr) { + return StringUtils.join(arr); + } + + /** + * 将数组拼接成字符串,默认指定分隔符 + * + * @param arr the array to display + * @param delim the delimiter to use (typically a ",") + * @return the delimited {@code String} + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static String join(Object[] arr, String delim) { + return StringUtils.join(arr, delim); + } + + /** + * 分割 字符串 + * + * @param str 字符串 + * @param delimiter 分割符 + * @return 字符串数组 string [ ] + * @since 1.0.0 + */ + @NotNull + public static String[] split(@Nullable String str, @Nullable String delimiter) { + return StringUtils.delimitedListToStringArray(str, delimiter); + } + + /** + * 分割 字符串 删除常见 空白符 + * + * @param str 字符串 + * @param delimiter 分割符 + * @return 字符串数组 string [ ] + * @since 1.0.0 + */ + @NotNull + public static String[] splitTrim(@Nullable String str, @Nullable String delimiter) { + return StringUtils.splitTrim(str, delimiter).toArray(new String[0]); + } + + /** + * 字符串是否符合指定的 表达式 + *

+ * pattern styles: "xxx*", "*xxx", "*xxx*" and "xxx*yyy" + *

+ * + * @param pattern 表达式 + * @param str 字符串 + * @return 是否匹配 boolean + * @since 1.0.0 + */ + @Contract("_, null -> false; null, !null -> false") + public static boolean simpleMatch(@Nullable String pattern, @Nullable String str) { + return PatternMatchUtils.simpleMatch(pattern, str); + } + + /** + * 字符串是否符合指定的 表达式 + *

+ * pattern styles: "xxx*", "*xxx", "*xxx*" and "xxx*yyy" + *

+ * + * @param patterns 表达式 数组 + * @param str 字符串 + * @return 是否匹配 boolean + * @since 1.0.0 + */ + @Contract("null, _ -> false") + public static boolean simpleMatch(@Nullable String[] patterns, String str) { + return PatternMatchUtils.simpleMatch(patterns, str); + } + + /** + * 生成uuid + * + * @return UUID string + * @since 1.0.0 + */ + @NotNull + public static String randomUid() { + return StringUtils.randomUid(); + } + + /** + * 转义HTML用于安全过滤 + * + * @param html html + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String escapeHtml(String html) { + return StringUtils.escapeHtml(html); + } + + /** + * 随机数生成 + * + * @param count 字符长度 + * @return 随机数 string + * @since 1.0.0 + */ + @NotNull + public static String random(int count) { + return RandomUtils.random(count); + } + + /** + * 随机数生成 + * + * @param count 字符长度 + * @param randomType 随机数类别 + * @return 随机数 string + * @since 1.0.0 + */ + @NotNull + public static String random(int count, RandomType randomType) { + return RandomUtils.random(count, randomType); + } + + /** + * Calculates the MD5 digest. + * + * @param data Data to digest + * @return MD5 digest as a hex array + * @since 1.0.0 + */ + public static byte[] md5(byte[] data) { + return DigestUtils.md5(data); + } + + /** + * Calculates the MD5 digest. + * + * @param data Data to digest + * @return MD5 digest as a hex array + * @since 1.0.0 + */ + public static byte[] md5(String data) { + return DigestUtils.md5(data); + } + + + /** + * Calculates the MD5 digest and returns the value as a 32 character hex string. + * + * @param data Data to digest + * @return MD5 digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String md5Hex(String data) { + return DigestUtils.md5Hex(data); + } + + /** + * Return a hexadecimal string representation of the MD5 digest of the given bytes. + * + * @param bytes the bytes to calculate the digest over + * @return a hexadecimal digest string + * @since 1.0.0 + */ + @NotNull + public static String md5Hex(byte[] bytes) { + return DigestUtils.md5Hex(bytes); + } + + /** + * sha1 + * + * @param data Data to digest + * @return digest as a hex array + * @since 1.0.0 + */ + public static byte[] sha1(@NotNull String data) { + return DigestUtils.sha1(data.getBytes(Charsets.UTF_8)); + } + + /** + * sha1Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha1Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha1(data.getBytes(Charsets.UTF_8))); + } + + /** + * sha1 + * + * @param bytes Data to digest + * @return digest as a hex array + * @since 1.0.0 + */ + public static byte[] sha1(byte[] bytes) { + return DigestUtils.digest("SHA-1", bytes); + } + + /** + * sha1Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha1Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha1(bytes)); + } + + /** + * SHA224 + * + * @param data Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha224(@NotNull String data) { + return DigestUtils.sha224(data.getBytes(Charsets.UTF_8)); + } + + /** + * SHA224Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha224Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha224(data.getBytes(Charsets.UTF_8))); + } + + /** + * SHA224 + * + * @param bytes Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha224(byte[] bytes) { + return DigestUtils.digest("SHA-224", bytes); + } + + /** + * SHA224Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha224Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha224(bytes)); + } + + /** + * sha256Hex + * + * @param data Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha256(@NotNull String data) { + return DigestUtils.sha256(data.getBytes(Charsets.UTF_8)); + } + + /** + * sha256Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha256Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha256(data.getBytes(Charsets.UTF_8))); + } + + /** + * sha256Hex + * + * @param bytes Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha256(byte[] bytes) { + return DigestUtils.digest("SHA-256", bytes); + } + + /** + * sha256Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha256Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha256(bytes)); + } + + /** + * sha384 + * + * @param data Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha384(@NotNull String data) { + return DigestUtils.sha384(data.getBytes(Charsets.UTF_8)); + } + + /** + * sha384Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha384Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha384(data.getBytes(Charsets.UTF_8))); + } + + /** + * sha384 + * + * @param bytes Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha384(byte[] bytes) { + return DigestUtils.digest("SHA-384", bytes); + } + + /** + * sha384Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha384Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha384(bytes)); + } + + /** + * sha512Hex + * + * @param data Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha512(@NotNull String data) { + return DigestUtils.sha512(data.getBytes(Charsets.UTF_8)); + } + + /** + * sha512Hex + * + * @param data Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha512Hex(@NotNull String data) { + return DigestUtils.encodeHex(sha512(data.getBytes(Charsets.UTF_8))); + } + + /** + * sha512Hex + * + * @param bytes Data to digest + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] sha512(byte[] bytes) { + return DigestUtils.digest("SHA-512", bytes); + } + + /** + * sha512Hex + * + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String sha512Hex(byte[] bytes) { + return DigestUtils.encodeHex(sha512(bytes)); + } + + /** + * digest Hex + * + * @param algorithm 算法 + * @param bytes Data to digest + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String digestHex(String algorithm, byte[] bytes) { + return DigestUtils.encodeHex(digest(algorithm, bytes)); + } + + /** + * digest + * + * @param algorithm 算法 + * @param bytes Data to digest + * @return digest byte array + * @since 1.0.0 + */ + public static byte[] digest(String algorithm, byte[] bytes) { + try { + MessageDigest md = MessageDigest.getInstance(algorithm); + return md.digest(bytes); + } catch (NoSuchAlgorithmException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * hmacMd5 + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacMd5(@NotNull String data, String key) { + return DigestUtils.hmacMd5(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacMd5 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacMd5Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacMd5(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacMd5 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacMd5(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacMD5", bytes, key); + } + + /** + * hmacMd5 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacMd5Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacMd5(bytes, key)); + } + + /** + * hmacSha1 + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha1(@NotNull String data, String key) { + return DigestUtils.hmacSha1(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha1 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha1Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha1(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha1 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha1(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA1", bytes, key); + } + + /** + * hmacSha1 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha1Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha1(bytes, key)); + } + + /** + * hmacSha224 + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + public static byte[] hmacSha224(@NotNull String data, String key) { + return DigestUtils.hmacSha224(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha224 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha224Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha224(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha224 + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + public static byte[] hmacSha224(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA224", bytes, key); + } + + /** + * hmacSha224 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha224Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha224(bytes, key)); + } + + /** + * hmacSha256 + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + public static byte[] hmacSha256(@NotNull String data, String key) { + return DigestUtils.hmacSha256(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha256 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + @NotNull + public static String hmacSha256Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha256(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha256 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha256(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA256", bytes, key); + } + + /** + * hmacSha256 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha256Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha256(bytes, key)); + } + + /** + * hmacSha384 + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha384(@NotNull String data, String key) { + return DigestUtils.hmacSha384(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha384 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha384Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha384(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha384 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha384(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA384", bytes, key); + } + + /** + * hmacSha384 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha384Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha384(bytes, key)); + } + + /** + * hmacSha512 + * + * @param data Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha512(@NotNull String data, String key) { + return DigestUtils.hmacSha512(data.getBytes(Charsets.UTF_8), key); + } + + /** + * hmacSha512 Hex + * + * @param data Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha512Hex(@NotNull String data, String key) { + return DigestUtils.encodeHex(hmacSha512(data.getBytes(Charsets.UTF_8), key)); + } + + /** + * hmacSha512 + * + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] hmacSha512(byte[] bytes, String key) { + return DigestUtils.digestHmac("HmacSHA512", bytes, key); + } + + /** + * hmacSha512 Hex + * + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String hmacSha512Hex(byte[] bytes, String key) { + return DigestUtils.encodeHex(hmacSha512(bytes, key)); + } + + /** + * digest Hmac Hex + * + * @param algorithm 算法 + * @param bytes Data to digest + * @param key key + * @return digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String digestHmacHex(String algorithm, byte[] bytes, String key) { + return DigestUtils.encodeHex(DigestUtils.digestHmac(algorithm, bytes, key)); + } + + /** + * digest Hmac + * + * @param algorithm 算法 + * @param bytes Data to digest + * @param key key + * @return digest as a byte array + * @since 1.0.0 + */ + public static byte[] digestHmac(String algorithm, byte[] bytes, @NotNull String key) { + return DigestUtils.digestHmac(algorithm, bytes, key); + } + + /** + * byte 数组序列化成 hex + * + * @param bytes bytes to encode + * @return MD5 digest as a hex string + * @since 1.0.0 + */ + @NotNull + public static String encodeHex(byte[] bytes) { + return DigestUtils.encodeHex(sha1(bytes)); + } + + /** + * 字符串反序列化成 hex + * + * @param hexStr String to decode + * @return MD5 digest as a hex string + * @since 1.0.0 + */ + public static byte[] decodeHex(@NotNull String hexStr) { + return DigestUtils.decodeHex(hexStr); + } + + /** + * 自定义加密 先MD5再SHA1 + * + * @param data 字符串 + * @return String string + * @since 1.0.0 + */ + @NotNull + public static String encrypt(String data) { + return DigestUtils.encrypt(data); + } + + /** + * 编码 + * + * @param value 字符串 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String encodeBase64(String value) { + return Base64Utils.encode(value); + } + + /** + * 编码 + * + * @param value 字符串 + * @param charset 字符集 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String encodeBase64(String value, Charset charset) { + return Base64Utils.encode(value, charset); + } + + /** + * 编码URL安全 + * + * @param value 字符串 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String encodeBase64UrlSafe(String value) { + return Base64Utils.encodeUrlSafe(value); + } + + /** + * 编码URL安全 + * + * @param value 字符串 + * @param charset 字符集 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String encodeBase64UrlSafe(String value, Charset charset) { + return Base64Utils.encodeUrlSafe(value, charset); + } + + /** + * 解码 + * + * @param value 字符串 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String decodeBase64(String value) { + return Base64Utils.decode(value); + } + + /** + * 解码 + * + * @param value 字符串 + * @param charset 字符集 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String decodeBase64(String value, Charset charset) { + return Base64Utils.decode(value, charset); + } + + /** + * 解码URL安全 + * + * @param value 字符串 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String decodeBase64UrlSafe(String value) { + return Base64Utils.decodeUrlSafe(value); + } + + /** + * 解码URL安全 + * + * @param value 字符串 + * @param charset 字符集 + * @return {String} + * @since 1.0.0 + */ + @NotNull + public static String decodeBase64UrlSafe(String value, Charset charset) { + return Base64Utils.decodeUrlSafe(value, charset); + } + + /** + * closeQuietly + * + * @param closeable 自动关闭 + * @since 1.0.0 + */ + public static void closeQuietly(@Nullable Closeable closeable) { + IoUtils.closeQuietly(closeable); + } + + /** + * InputStream to String utf-8 + * + * @param input the InputStream to read from + * @return the requested String + * @throws NullPointerException if the input is null + * @since 1.0.0 + */ + @NotNull + public static String toString(InputStream input) { + return IoUtils.toString(input); + } + + /** + * InputStream to String + * + * @param input the InputStream to read from + * @param charset the Charsets + * @return the requested String + * @throws NullPointerException if the input is null + * @since 1.0.0 + */ + @NotNull + public static String toString(@Nullable InputStream input, Charset charset) { + return IoUtils.toString(input, charset); + } + + /** + * InputStream to bytes 数组 + * + * @param input InputStream + * @return the requested byte array + * @since 1.0.0 + */ + @NotNull + public static byte[] toByteArray(@Nullable InputStream input) { + return IoUtils.toByteArray(input); + } + + /** + * 读取文件为字符串 + * + * @param file the file to read, must not be {@code null} + * @return the file contents, never {@code null} + * @since 1.0.0 + */ + @NotNull + public static String toString(File file) { + return FileUtils.readToString(file); + } + + /** + * 读取文件为字符串 + * + * @param file the file to read, must not be {@code null} + * @param encoding the encoding to use, {@code null} means platform default + * @return the file contents, never {@code null} + * @since 1.0.0 + */ + @NotNull + public static String toString(File file, Charset encoding) { + return FileUtils.readToString(file, encoding); + } + + /** + * 读取文件为 byte 数组 + * + * @param file the file to read, must not be {@code null} + * @return the file contents, never {@code null} + * @since 1.0.0 + */ + public static byte[] toByteArray(File file) { + return FileUtils.readToByteArray(file); + } + + /** + * 获取资源,注意: boot 中请不要使用 Resource getFile,应该使用 getInputStream,支持一下协议: + *

+ * 1. classpath: + * 2. file: + * 3. ftp: + * 4. http: and https: + * 6. C:/dir1/ and /Users/lcm + *

+ * + * @param resourceLocation 资源路径 + * @return Resource resource + * @throws IOException io异常 + * @since 1.0.0 + */ + @NotNull + public static Resource getResource(String resourceLocation) throws IOException { + return ResourceUtils.getResource(resourceLocation); + } + + /** + * 将对象序列化成json字符串 + * + * @param object javaBean + * @return jsonString json字符串 + * @since 1.0.0 + */ + @NotNull + public static String toJson(Object object) { + return JsonUtils.toJson(object); + } + + /** + * 将对象序列化成 json byte 数组 + * + * @param object javaBean + * @return jsonString json字符串 + * @since 1.0.0 + */ + public static byte[] toJsonAsBytes(Object object) { + return JsonUtils.toJsonAsBytes(object); + } + + /** + * 将json字符串转成 JsonNode + * + * @param jsonString jsonString + * @return jsonString json字符串 + * @since 1.0.0 + */ + @NotNull + public static JsonNode readTree(String jsonString) { + return JsonUtils.readTree(jsonString); + } + + /** + * 将json字符串转成 JsonNode + * + * @param in InputStream + * @return jsonString json字符串 + * @since 1.0.0 + */ + @NotNull + public static JsonNode readTree(InputStream in) { + return JsonUtils.readTree(in); + } + + /** + * 将json字符串转成 JsonNode + * + * @param content content + * @return jsonString json字符串 + * @since 1.0.0 + */ + @NotNull + public static JsonNode readTree(byte[] content) { + return JsonUtils.readTree(content); + } + + /** + * 将json字符串转成 JsonNode + * + * @param jsonParser JsonParser + * @return jsonString json字符串 + * @since 1.0.0 + */ + @NotNull + public static JsonNode readTree(JsonParser jsonParser) { + return JsonUtils.readTree(jsonParser); + } + + /** + * 将json byte 数组反序列化成对象 + * + * @param T 泛型标记 + * @param bytes json bytes + * @param valueType class + * @return Bean t + * @since 1.0.0 + */ + @Nullable + public static T parse(byte[] bytes, Class valueType) { + return JsonUtils.parse(bytes, valueType); + } + + /** + * 将json反序列化成对象 + * + * @param T 泛型标记 + * @param jsonString jsonString + * @param valueType class + * @return Bean t + * @since 1.0.0 + */ + @NotNull + public static T parse(String jsonString, Class valueType) { + return JsonUtils.parse(jsonString, valueType); + } + + /** + * 将json反序列化成对象 + * + * @param T 泛型标记 + * @param in InputStream + * @param valueType class + * @return Bean t + * @since 1.0.0 + */ + @NotNull + public static T parse(InputStream in, Class valueType) { + return JsonUtils.parse(in, valueType); + } + + /** + * 将json反序列化成对象 + * + * @param T 泛型标记 + * @param bytes bytes + * @param typeReference 泛型类型 + * @return Bean t + * @since 1.0.0 + */ + @NotNull + public static T parse(byte[] bytes, TypeReference typeReference) { + return JsonUtils.parse(bytes, typeReference); + } + + /** + * 将json反序列化成对象 + * + * @param T 泛型标记 + * @param jsonString jsonString + * @param typeReference 泛型类型 + * @return Bean t + * @since 1.0.0 + */ + @NotNull + public static T parse(String jsonString, TypeReference typeReference) { + return JsonUtils.parse(jsonString, typeReference); + } + + /** + * 将json反序列化成对象 + * + * @param T 泛型标记 + * @param in InputStream + * @param typeReference 泛型类型 + * @return Bean t + * @since 1.0.0 + */ + @NotNull + public static T parse(InputStream in, TypeReference typeReference) { + return JsonUtils.parse(in, typeReference); + } + + /** + * 读取集合 + * + * @param 泛型 + * @param content bytes + * @param elementClass elementClass + * @return 集合 list + * @since 1.0.0 + */ + @NotNull + public static List toList(@Nullable byte[] content, Class elementClass) { + return JsonUtils.toList(content, elementClass); + } + + /** + * 读取集合 + * + * @param 泛型 + * @param content InputStream + * @param elementClass elementClass + * @return 集合 list + * @since 1.0.0 + */ + @NotNull + public static List toList(@Nullable InputStream content, Class elementClass) { + return JsonUtils.toList(content, elementClass); + } + + /** + * 读取集合 + * + * @param 泛型 + * @param content bytes + * @param elementClass elementClass + * @return 集合 list + * @since 1.0.0 + */ + @NotNull + public static List toList(@Nullable String content, Class elementClass) { + return JsonUtils.toList(content, elementClass); + } + + /** + * 读取集合 + * + * @param 泛型 + * @param 泛型 + * @param content bytes + * @param keyClass key类型 + * @param valueClass 值类型 + * @return 集合 map + * @since 1.0.0 + */ + @NotNull + public static Map toMap(@Nullable byte[] content, Class keyClass, Class valueClass) { + return JsonUtils.toMap(content, keyClass, valueClass); + } + + /** + * 读取集合 + * + * @param 泛型 + * @param 泛型 + * @param content InputStream + * @param keyClass key类型 + * @param valueClass 值类型 + * @return 集合 map + * @since 1.0.0 + */ + @NotNull + public static Map toMap(@Nullable InputStream content, Class keyClass, Class valueClass) { + return JsonUtils.toMap(content, keyClass, valueClass); + } + + /** + * 读取集合 + * + * @param 泛型 + * @param 泛型 + * @param content bytes + * @param keyClass key类型 + * @param valueClass 值类型 + * @return 集合 map + * @since 1.0.0 + */ + @NotNull + public static Map toMap(@Nullable String content, Class keyClass, Class valueClass) { + return JsonUtils.toMap(content, keyClass, valueClass); + } + + + /** + * Encode all characters that are either illegal, or have any reserved + * meaning, anywhere within a URI, as defined in + * RFC 3986. + * This is useful to ensure that the given String will be preserved as-is + * and will not have any o impact on the structure or meaning of the URI. + * + * @param source the String to be encoded + * @return the encoded String + * @since 1.0.0 + */ + @NotNull + public static String encode(String source) { + return UrlUtils.encode(source, Charsets.UTF_8); + } + + /** + * Encode all characters that are either illegal, or have any reserved + * meaning, anywhere within a URI, as defined in + * RFC 3986. + * This is useful to ensure that the given String will be preserved as-is + * and will not have any o impact on the structure or meaning of the URI. + * + * @param source the String to be encoded + * @param charset the character encoding to encode to + * @return the encoded String + * @since 1.0.0 + */ + @NotNull + public static String encode(String source, Charset charset) { + return UrlUtils.encode(source, charset); + } + + /** + * Decode the given encoded URI component. + *

See {@link org.springframework.util.StringUtils#uriDecode(String, Charset)} for the decoding rules. + * + * @param source the encoded String + * @return the decoded value + * @throws IllegalArgumentException when the given source contains invalid encoded sequences + * @see org.springframework.util.StringUtils#uriDecode org.springframework.util.StringUtils#uriDecodeorg.springframework.util + * .StringUtils#uriDecodeorg.springframework.util .StringUtils#uriDecode + * @see java.net.URLDecoder#decode java.net.URLDecoder#decodejava.net.URLDecoder#decodejava.net.URLDecoder#decode + * @since 1.0.0 + */ + @NotNull + public static String decode(String source) { + return org.springframework.util.StringUtils.uriDecode(source, Charsets.UTF_8); + } + + /** + * Decode the given encoded URI component. + *

See {@link org.springframework.util.StringUtils#uriDecode(String, Charset)} for the decoding rules. + * + * @param source the encoded String + * @param charset the character encoding to use + * @return the decoded value + * @throws IllegalArgumentException when the given source contains invalid encoded sequences + * @see org.springframework.util.StringUtils#uriDecode org.springframework.util.StringUtils#uriDecodeorg.springframework.util + * .StringUtils#uriDecodeorg.springframework.util .StringUtils#uriDecode + * @see java.net.URLDecoder#decode(String, String) java.net.URLDecoder#decode(String, String)java.net.URLDecoder#decode(String, + * String)java.net.URLDecoder#decode(String, String) + * @since 1.0.0 + */ + @NotNull + public static String decode(String source, Charset charset) { + return org.springframework.util.StringUtils.uriDecode(source, charset); + } + + /** + * 日期时间格式化 + * + * @param date 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + public static @NotNull String formatDateTime(Date date) { + return DateUtils.formatDateTime(date); + } + + /** + * 日期格式化 + * + * @param date 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + public static @NotNull String formatDate(Date date) { + return DateUtils.formatDate(date); + } + + /** + * 时间格式化 + * + * @param date 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + public static @NotNull String formatTime(Date date) { + return DateUtils.formatTime(date); + } + + /** + * 日期格式化 + * + * @param date 时间 + * @param pattern 表达式 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + public static @NotNull String format(Date date, String pattern) { + return DateUtils.format(date, pattern); + } + + /** + * 将字符串转换为时间 + * + * @param dateStr 时间字符串 + * @param pattern 表达式 + * @return 时间 date + * @since 1.0.0 + */ + public static Date parseDate(String dateStr, String pattern) { + return DateUtils.parse(dateStr, pattern); + } + + /** + * 将字符串转换为时间 + * + * @param dateStr 时间字符串 + * @param format ConcurrentDateFormat + * @return 时间 date + * @since 1.0.0 + */ + public static Date parse(String dateStr, ConcurrentDateFormat format) { + return DateUtils.parse(dateStr, format); + } + + /** + * 日期时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatDateTime(TemporalAccessor temporal) { + return DateTimeUtils.formatDateTime(temporal); + } + + /** + * 日期时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatDate(TemporalAccessor temporal) { + return DateTimeUtils.formatDate(temporal); + } + + /** + * 时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String formatTime(TemporalAccessor temporal) { + return DateTimeUtils.formatTime(temporal); + } + + /** + * 日期格式化 + * + * @param temporal 时间 + * @param pattern 表达式 + * @return 格式化后的时间 string + * @since 1.0.0 + */ + @NotNull + public static String format(TemporalAccessor temporal, String pattern) { + return DateTimeUtils.format(temporal, pattern); + } + + /** + * 将字符串转换为时间 + * + * @param dateStr 时间字符串 + * @param pattern 表达式 + * @return 时间 temporal accessor + * @since 1.0.0 + */ + @NotNull + public static TemporalAccessor parse(String dateStr, String pattern) { + return DateTimeUtils.parse(dateStr, pattern); + } + + /** + * 将字符串转换为时间 + * + * @param dateStr 时间字符串 + * @param formatter DateTimeFormatter + * @return 时间 temporal accessor + * @since 1.0.0 + */ + @NotNull + public static TemporalAccessor parse(String dateStr, DateTimeFormatter formatter) { + return DateTimeUtils.parse(dateStr, formatter); + } + + /** + * 对象类型转换 + * + * @param 泛型标记 + * @param source the source object + * @param targetType the target type + * @return the converted value + * @throws IllegalArgumentException if targetType is {@code null},or sourceType is {@code null} but source is not {@code null} + * @since 1.0.0 + */ + @Contract("null, _ -> null") + @Nullable + public static T convert(@Nullable Object source, Class targetType) { + if (source instanceof String) { + String json = (String) source; + if (JsonUtils.isJson(json)) { + return JsonUtils.parse(json, targetType); + } + } + + return ConvertUtils.convert(source, targetType); + } + + /** + * 对象类型转换 + * + * @param 泛型标记 + * @param source the source object + * @param sourceType the source type + * @param targetType the target type + * @return the converted value + * @throws IllegalArgumentException if targetType is {@code null}, or sourceType is {@code null} but source is not {@code null} + * @since 1.0.0 + */ + @Contract("null, _, _ -> null") + @Nullable + public static T convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + return ConvertUtils.convert(source, sourceType, targetType); + } + + /** + * 对象类型转换 + * + * @param 泛型标记 + * @param source the source object + * @param targetType the target type + * @return the converted value + * @throws IllegalArgumentException if targetType is {@code null}, or sourceType is {@code null} but source is not {@code null} + * @since 1.0.0 + */ + @Contract("null, _ -> null") + @Nullable + public static T convert(@Nullable Object source, TypeDescriptor targetType) { + return ConvertUtils.convert(source, targetType); + } + + /** + * 时间比较 + * + * @param startInclusive the start instant, inclusive, not null + * @param endExclusive the end instant, exclusive, not null + * @return a {@code Duration}, not null + * @since 1.0.0 + */ + public static Duration between(Temporal startInclusive, Temporal endExclusive) { + return Duration.between(startInclusive, endExclusive); + } + + /** + * 比较2个 时间差 + * + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return 时间间隔 duration + * @since 1.0.0 + */ + public static Duration between(Date startDate, Date endDate) { + return DateUtils.between(startDate, endDate); + } + + /** + * 获取方法参数信息 + * + * @param constructor 构造器 + * @param parameterIndex 参数序号 + * @return {MethodParameter} + * @since 1.0.0 + */ + public static @NotNull MethodParameter getMethodParameter(Constructor constructor, int parameterIndex) { + return ClassUtils.getMethodParameter(constructor, parameterIndex); + } + + /** + * 获取方法参数信息 + * + * @param method 方法 + * @param parameterIndex 参数序号 + * @return {MethodParameter} + * @since 1.0.0 + */ + public static MethodParameter getMethodParameter(Method method, int parameterIndex) { + return ClassUtils.getMethodParameter(method, parameterIndex); + } + + /** + * 获取Annotation + * + * @param 泛型标记 + * @param annotatedElement AnnotatedElement + * @param annotationType 注解类 + * @return {Annotation} + * @since 1.0.0 + */ + @Nullable + public static A getAnnotation(AnnotatedElement annotatedElement, Class annotationType) { + return AnnotatedElementUtils.findMergedAnnotation(annotatedElement, annotationType); + } + + /** + * 获取Annotation, 先找方法,没有则再找方法上的类 + * + * @param 泛型标记 + * @param method Method + * @param annotationType 注解类 + * @return {Annotation} + * @since 1.0.0 + */ + @Nullable + public static A getAnnotation(Method method, Class annotationType) { + return ClassUtils.getAnnotation(method, annotationType); + } + + /** + * 获取Annotation, 先找HandlerMethod,没有则再找对应的类 + * + * @param 泛型标记 + * @param handlerMethod HandlerMethod + * @param annotationType 注解类 + * @return {Annotation} + * @since 1.0.0 + */ + @Nullable + public static A getAnnotation(HandlerMethod handlerMethod, Class annotationType) { + return ClassUtils.getAnnotation(handlerMethod, annotationType); + } + + /** + * 实例化对象 + * + * @param 泛型标记 + * @param clazz 类 + * @return 对象 t + * @since 1.0.0 + */ + @NotNull + @SuppressWarnings("unchecked") + public static T newInstance(Class clazz) { + return (T) BeanUtils.instantiateClass(clazz); + } + + /** + * 实例化对象 + * + * @param 泛型标记 + * @param clazzStr 类名 + * @return 对象 t + * @since 1.0.0 + */ + @NotNull + public static T newInstance(String clazzStr) { + return BeanUtils.newInstance(clazzStr); + } + + /** + * 获取Bean的属性 + * + * @param bean bean + * @param propertyName 属性名 + * @return 属性值 property + * @since 1.0.0 + */ + public static Object getProperty(Object bean, String propertyName) { + return BeanUtils.getProperty(bean, propertyName); + } + + /** + * 设置Bean属性 + * + * @param bean bean + * @param propertyName 属性名 + * @param value 属性值 + * @since 1.0.0 + */ + public static void setProperty(Object bean, String propertyName, Object value) { + BeanUtils.setProperty(bean, propertyName, value); + } + + /** + * 深复制 + *

+ * 注意: 不支持链式Bean + * + * @param 泛型标记 + * @param source 源对象 + * @return T t + * @since 1.0.0 + */ + public static T clone(T source) { + return BeanUtils.clone(source); + } + + /** + * copy 对象属性到另一个对象,默认不使用Convert + *

+ * 注意: 不支持链式Bean,链式用 copyProperties + * + * @param 泛型标记 + * @param source 源对象 + * @param clazz 类名 + * @return T t + * @since 1.0.0 + */ + @Contract("null, _ -> null") + public static T copy(Object source, Class clazz) { + return BeanUtils.copy(source, clazz); + } + + /** + * 拷贝对象 + *

+ * 注意: 不支持链式Bean,链式用 copyProperties + * + * @param source 源对象 + * @param targetBean 需要赋值的对象 + * @since 1.0.0 + */ + public static void copy(Object source, Object targetBean) { + BeanUtils.copy(source, targetBean); + } + + /** + * 拷贝列表对象,并对不同类型属性进行转换 + *

+ * 支持 map bean copy + *

+ * + * @param 泛型标记 + * @param sourceList 源对象列表 + * @param targetClazz 转换成的类 + * @return List list + * @since 1.0.0 + */ + @Contract("null, _ -> !null") + public static @NotNull List copyWithConvert(@Nullable Collection sourceList, Class targetClazz) { + return BeanUtils.copyWithConvert(sourceList, targetClazz); + } + + /** + * Copy the property values of the given source bean into the target class. + *

Note: The source and target classes do not have to match or even be derived + * from each other, as long as the properties match. Any bean properties that the + * source bean exposes but the target bean does not will silently be ignored. + *

This is just a convenience method. For more complex transfer needs, + * + * @param 泛型标记 + * @param source the source bean + * @param clazz the target bean class + * @return T t + * @throws BeansException if the copying failed + * @since 1.0.0 + */ + @Contract("null, _ -> null") + public static T copyProperties(Object source, Class clazz) throws BeansException { + return BeanUtils.copyProperties(source, clazz); + } + + /** + * 将对象装成map形式 + * + * @param bean 源对象 + * @return {Map} + * @since 1.0.0 + */ + @Contract("null -> new") + public static Map toMap(Object bean) { + return BeanUtils.toMap(bean); + } + + /** + * 将map 转为 bean + * + * @param 泛型标记 + * @param beanMap map + * @param valueType 对象类型 + * @return {T} + * @since 1.0.0 + */ + public static @NotNull T toBean(Map beanMap, Class valueType) { + return BeanUtils.toBean(beanMap, valueType); + } + + /** + * 初始化 map 容量 + * + * @param expectedSize the expected size + * @return the int + * @since 1.0.0 + */ + @Contract(pure = true) + @SuppressWarnings("PMD.UndefineMagicConstantRule") + public static int capacity(int expectedSize) { + if (expectedSize < 3) { + if (expectedSize < 0) { + throw new IllegalArgumentException("expectedSize cannot be negative but was: " + expectedSize); + } + return expectedSize + 1; + } + if (expectedSize < Ints.MAX_POWER_OF_TWO) { + // This is the calculation used in JDK8 to resize when a putAll + // happens; it seems to be the most conservative calculation we + // can make. 0.75 is the default load factor. + return (int) ((float) expectedSize / 0.75F + 1.0F); + } + // any large value + return Integer.MAX_VALUE; + } + + /** + * 重复执行代码 + * + * @param count count 执行次数 + * @param runnable runnable + * @since 1.0.0 + */ + @SneakyThrows + public static void repeat(int count, CheckedRunnable runnable) { + for (int i = 0; i < count; i++) { + runnable.run(); + } + } + + /** + * 重复执行代码, 下标从 0 开始 + * + * @param count count + * @param consumer consumer + * @since 1.6.0 + */ + @SneakyThrows + public static void repeat(int count, CheckedConsumer consumer) { + for (int i = 0; i < count; i++) { + consumer.accept(i); + } + } + + + /** + * Properties -> map + * + * @param properties properties + * @return the map from properties + * @since 1.0.0 + */ + public static @NotNull Map getMapFromProperties(@NotNull Properties properties) { + Map map = Maps.newHashMap(); + for (Object key : Collections.list(properties.propertyNames())) { + map.put((String) key, properties.get(key)); + } + return map; + } + + /** + * Record stop watch + * + * @param runnable runnable + * @return the stop watch + * @since 1.0.0 + */ + @SneakyThrows + public static @NotNull StopWatch record(@NotNull CheckedRunnable runnable) { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + runnable.run(); + + stopWatch.stop(); + return stopWatch; + } +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/Unchecked.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/Unchecked.java new file mode 100644 index 0000000..abc331a --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/Unchecked.java @@ -0,0 +1,150 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.starter.base.utils.Exceptions; +import com.aivfo.el.starter.core.function.*; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Comparator; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * 受检异常处理 + * https://segmentfault.com/a/1190000007832130 + * https://github.com/jOOQ/jOOL + */ +@UtilityClass +public class Unchecked { + + /** + * Function function. + * + * @param the type parameter + * @param the type parameter + * @param mapper the mapper + * @return the function + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static Function function(CheckedFunction mapper) { + Objects.requireNonNull(mapper); + return t -> { + try { + return mapper.apply(t); + } catch (Throwable e) { + throw Exceptions.unchecked(e); + } + }; + } + + /** + * Consumer consumer. + * + * @param the type parameter + * @param mapper the mapper + * @return the consumer + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static Consumer consumer(CheckedConsumer mapper) { + Objects.requireNonNull(mapper); + return t -> { + try { + mapper.accept(t); + } catch (Throwable e) { + throw Exceptions.unchecked(e); + } + }; + } + + /** + * Supplier supplier. + * + * @param the type parameter + * @param mapper the mapper + * @return the supplier + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static Supplier supplier(CheckedSupplier mapper) { + Objects.requireNonNull(mapper); + return () -> { + try { + return mapper.get(); + } catch (Throwable e) { + throw Exceptions.unchecked(e); + } + }; + } + + /** + * Runnable runnable. + * + * @param runnable the runnable + * @return the runnable + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static Runnable runnable(CheckedRunnable runnable) { + Objects.requireNonNull(runnable); + return () -> { + try { + runnable.run(); + } catch (Throwable e) { + throw Exceptions.unchecked(e); + } + }; + } + + /** + * Callable callable. + * + * @param the type parameter + * @param callable the callable + * @return the callable + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static Callable callable(CheckedCallable callable) { + Objects.requireNonNull(callable); + return () -> { + try { + return callable.call(); + } catch (Throwable e) { + throw Exceptions.unchecked(e); + } + }; + } + + /** + * Comparator comparator. + * + * @param the type parameter + * @param comparator the comparator + * @return the comparator + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static Comparator comparator(CheckedComparator comparator) { + Objects.requireNonNull(comparator); + return (T o1, T o2) -> { + try { + return comparator.compare(o1, o2); + } catch (Throwable e) { + throw Exceptions.unchecked(e); + } + }; + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/XmlUtil.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/XmlUtil.java new file mode 100644 index 0000000..1fce518 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/util/XmlUtil.java @@ -0,0 +1,343 @@ +package com.aivfo.el.start.core.util; + +import com.aivfo.el.starter.base.utils.Exceptions; +import com.aivfo.el.starter.base.utils.IoUtils; +import com.google.common.collect.Maps; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.lang.Nullable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.util.Map; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: xpath解析xml + */ +public final class XmlUtil { + /** + * preventedXXE + */ + private static volatile boolean preventedXXE = false; + /** + * Path + */ + private final XPath path; + /** + * Doc + */ + private final Document doc; + + /** + * Xml util + * + * @param inputSource input source + * @throws ParserConfigurationException parser configuration exception + * @throws SAXException sax exception + * @throws IOException io exception + * @since 1.0.0 + */ + private XmlUtil(InputSource inputSource) throws ParserConfigurationException, SAXException, IOException { + DocumentBuilderFactory dbf = getDocumentBuilderFactory(); + DocumentBuilder db = dbf.newDocumentBuilder(); + doc = db.parse(inputSource); + path = getPathFactory().newXPath(); + } + + /** + * Gets document builder factory * + * + * @return the document builder factory + * @throws ParserConfigurationException parser configuration exception + * @since 1.0.0 + */ + private static DocumentBuilderFactory getDocumentBuilderFactory() throws ParserConfigurationException { + DocumentBuilderFactory dbf = XmlHelperHolder.DOCUMENT_BUILDER_FACTORY; + if (!preventedXXE) { + preventXxe(dbf); + } + return dbf; + } + + /** + * Gets x path factory * + * + * @return the x path factory + * @since 1.0.0 + */ + @Contract(pure = true) + private static XPathFactory getPathFactory() { + return XmlHelperHolder.X_PATH_FACTORY; + } + + /** + * preventXxe + * + * @param dbf dbf + * @throws ParserConfigurationException parser configuration exception + * @since 1.0.0 + */ + private static void preventXxe(@NotNull DocumentBuilderFactory dbf) throws ParserConfigurationException { + // This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented + // Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl + dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + + // If you can't completely disable DTDs, then at least do the following: + // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities + // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities + + // JDK7+ - http://xml.org/sax/features/external-general-entities + dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); + + // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities + // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities + + // JDK7+ - http://xml.org/sax/features/external-parameter-entities + dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + + // Disable external DTDs as well + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + + // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks" + dbf.setXIncludeAware(false); + dbf.setExpandEntityReferences(false); + preventedXXE = true; + } + + /** + * Of xml util. + * + * @param is the is + * @return the xml util + * @since 1.0.0 + */ + @NotNull + public static XmlUtil of(InputStream is) { + InputSource inputSource = new InputSource(is); + return create(inputSource); + } + + /** + * Create xml util + * + * @param inputSource input source + * @return the xml util + * @since 1.0.0 + */ + @NotNull + @Contract("_ -> new") + private static XmlUtil create(InputSource inputSource) { + try { + return new XmlUtil(inputSource); + } catch (ParserConfigurationException | SAXException | IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Of xml util. + * + * @param xmlStr the xml str + * @return the xml util + * @since 1.0.0 + */ + public static XmlUtil of(@NotNull String xmlStr) { + StringReader sr = new StringReader(xmlStr.trim()); + InputSource inputSource = new InputSource(sr); + XmlUtil xmlUtil = create(inputSource); + IoUtils.closeQuietly(sr); + return xmlUtil; + } + + /** + * 获取String + * + * @param expression 路径 + * @return String string + * @since 1.0.0 + */ + public String getString(String expression) { + return (String) evalPath(expression, null, XPathConstants.STRING); + } + + /** + * Eval x path object + * + * @param expression expression + * @param item item + * @param returnType return type + * @return the object + * @since 1.0.0 + */ + private Object evalPath(String expression, @Nullable Object item, QName returnType) { + item = null == item ? doc : item; + try { + return path.evaluate(expression, item, returnType); + } catch (XPathExpressionException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * 获取Boolean + * + * @param expression 路径 + * @return String boolean + * @since 1.0.0 + */ + public Boolean getBoolean(String expression) { + return (Boolean) evalPath(expression, null, XPathConstants.BOOLEAN); + } + + /** + * 获取Number + * + * @param expression 路径 + * @return {Number} + * @since 1.0.0 + */ + public Number getNumber(String expression) { + return (Number) evalPath(expression, null, XPathConstants.NUMBER); + } + + /** + * 获取某个节点 + * + * @param expression 路径 + * @return {Node} + * @since 1.0.0 + */ + public Node getNode(String expression) { + return (Node) evalPath(expression, null, XPathConstants.NODE); + } + + /** + * 获取子节点 + * + * @param expression 路径 + * @return NodeList node list + * @since 1.0.0 + */ + public NodeList getNodeList(String expression) { + return (NodeList) evalPath(expression, null, XPathConstants.NODESET); + } + + /** + * 获取String + * + * @param node 节点 + * @param expression 相对于node的路径 + * @return String string + * @since 1.0.0 + */ + public String getString(Object node, String expression) { + return (String) evalPath(expression, node, XPathConstants.STRING); + } + + /** + * 获取 + * + * @param node 节点 + * @param expression 相对于node的路径 + * @return String boolean + * @since 1.0.0 + */ + public Boolean getBoolean(Object node, String expression) { + return (Boolean) evalPath(expression, node, XPathConstants.BOOLEAN); + } + + /** + * 获取 + * + * @param node 节点 + * @param expression 相对于node的路径 + * @return {Number} + * @since 1.0.0 + */ + public Number getNumber(Object node, String expression) { + return (Number) evalPath(expression, node, XPathConstants.NUMBER); + } + + /** + * 获取某个节点 + * + * @param node 节点 + * @param expression 路径 + * @return {Node} + * @since 1.0.0 + */ + public Node getNode(Object node, String expression) { + return (Node) evalPath(expression, node, XPathConstants.NODE); + } + + /** + * 获取子节点 + * + * @param node 节点 + * @param expression 相对于node的路径 + * @return NodeList node list + * @since 1.0.0 + */ + public NodeList getNodeList(Object node, String expression) { + return (NodeList) evalPath(expression, node, XPathConstants.NODESET); + } + + /** + * 针对没有嵌套节点的简单处理 + * + * @return map集合 map + * @since 1.0.0 + */ + public Map toMap() { + Element root = doc.getDocumentElement(); + Map params = Maps.newHashMap(); + + // 将节点封装成map形式 + NodeList list = root.getChildNodes(); + for (int i = 0; i < list.getLength(); i++) { + Node node = list.item(i); + if (node instanceof Element) { + params.put(node.getNodeName(), node.getTextContent()); + } + } + return params; + } + + /** + * 内部类单例 + * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2020.01.27 18:27 + * @since 1.0.0 + */ + private static class XmlHelperHolder { + /** + * DOCUMENT_BUILDER_FACTORY + */ + private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); + /** + * X_PATH_FACTORY + */ + private static final XPathFactory X_PATH_FACTORY = XPathFactory.newInstance(); + } + +} diff --git a/band/aivfo-element/aivfo-element-web/pom.xml b/band/aivfo-element/aivfo-element-web/pom.xml new file mode 100644 index 0000000..f40b45c --- /dev/null +++ b/band/aivfo-element/aivfo-element-web/pom.xml @@ -0,0 +1,39 @@ + + + + aivfo-project-build + com.aivfo + 1.0.0-SNAPSHOT + ../../../ability/aivfo-framework-build/aivfo-project-build/pom.xml + + 4.0.0 + aivfo-element-web + + + org.springframework.boot + spring-boot-autoconfigure + provided + + + org.springframework + spring-webmvc + provided + + + javax.servlet + javax.servlet-api + provided + + + com.fasterxml.jackson.core + jackson-databind + + + com.aivfo + aivfo-element-common + 1.0.0-SNAPSHOT + + + \ No newline at end of file diff --git a/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/autoconfigure/servlet/WebProperties.java b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/autoconfigure/servlet/WebProperties.java new file mode 100644 index 0000000..bd44172 --- /dev/null +++ b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/autoconfigure/servlet/WebProperties.java @@ -0,0 +1,28 @@ +package com.aivfo.el.web.autoconfigure.servlet; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + + +@Data +@ConfigurationProperties(prefix = WebProperties.PREFIX) +public class WebProperties { + /** + * PREFIX + */ + public static final String PREFIX = "aivfo.web"; + + /** + * Enable global cache filter + */ + private boolean enableGlobalCacheFilter = Boolean.TRUE; + /** + * filter 异常处理器 + */ + private boolean enableExceptionFilter = Boolean.TRUE; + /** + * 不需要缓存 request 的 url (正则) + */ + private String ignoreCacheRequestUrl; + +} diff --git a/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/exception/ServletGlobalExceptionHandler.java b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/exception/ServletGlobalExceptionHandler.java new file mode 100644 index 0000000..47edea5 --- /dev/null +++ b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/exception/ServletGlobalExceptionHandler.java @@ -0,0 +1,12 @@ +package com.aivfo.el.web.exception; + +import com.aivfo.start.common.exception.GlobalExceptionHandler; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +@Order(Ordered.HIGHEST_PRECEDENCE + 1000) +public class ServletGlobalExceptionHandler extends GlobalExceptionHandler { + +} diff --git a/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/filter/ExceptionFilter.java b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/filter/ExceptionFilter.java new file mode 100644 index 0000000..376ef0c --- /dev/null +++ b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/filter/ExceptionFilter.java @@ -0,0 +1,68 @@ +package com.aivfo.el.web.filter; + +import com.aivfo.el.starter.base.constant.BasicConstant; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.autoconfigure.web.ServerProperties; + +import javax.servlet.*; +import java.io.IOException; +import java.util.Objects; + +/** + *

Description: 由于在 Filter 中抛出的异常无法被 controller 处理, 为了将异常结果返回到 controller 层, 这里将异常信息保存到 Request, + * 然后在 {@link ServletErrorController} 处理 Filter 异常 + * + *

+ */ +@Slf4j +public class ExceptionFilter implements Filter { + + /** + * Server properties + */ + private final ServerProperties serverProperties; + + /** + * Exception filter + * + * @param serverProperties server properties + * @since 1.0.0 + */ + @Contract(pure = true) + public ExceptionFilter(ServerProperties serverProperties) { + this.serverProperties = serverProperties; + } + + /** + * Do filter * + * + * @param request request + * @param response response + * @param chain chain + * @throws IOException io exception + * @throws ServletException servlet exception + * @since 1.0.0 + */ + @Override + public void doFilter(@NotNull ServletRequest request, + ServletResponse response, + FilterChain chain) throws IOException, ServletException { + // 是否已经放有异常栈, 避免循环 + boolean isRethrow = !Objects.isNull(request.getAttribute(BasicConstant.REQUEST_EXCEPTION_INFO_ATTR)); + if (isRethrow) { + chain.doFilter(request, response); + return; + } + try { + chain.doFilter(request, response); + } catch (Exception e) { + // 发生异常,保存异常栈 + request.setAttribute(BasicConstant.REQUEST_EXCEPTION_INFO_ATTR, e); + // 转发到 /error , 会由 ServletErrorController 抛出异常, 最后转交给全局异常处理器处理 + request.getRequestDispatcher(this.serverProperties.getError().getPath()).forward(request, response); + } + } + +} diff --git a/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/filter/ServletGlobalCacheFilter.java b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/filter/ServletGlobalCacheFilter.java new file mode 100644 index 0000000..ee52dbb --- /dev/null +++ b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/filter/ServletGlobalCacheFilter.java @@ -0,0 +1,66 @@ +package com.aivfo.el.web.filter; + +import com.aivfo.el.starter.base.AbstractSkipFilter; +import com.aivfo.el.web.support.CacheRequestEnhanceWrapper; +import com.aivfo.el.web.support.CacheResponseEnhanceWrapper; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.web.util.ContentCachingRequestWrapper; +import org.springframework.web.util.ContentCachingResponseWrapper; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + *

Description: request 缓存过滤器, 优先级最高

+ */ +@Slf4j +public class ServletGlobalCacheFilter extends AbstractSkipFilter { + + /** + * Servlet global cache filter + * + * @param skipUrl skip url + * @since 2022.1.1 + */ + public ServletGlobalCacheFilter(String skipUrl) { + super(skipUrl); + } + + /** + * Do filter internal * + * + * @param request request + * @param response response + * @param chain chain + * @throws ServletException servlet exception + * @throws IOException io exception + * @since 1.0.0 + */ + @Override + public void doFilterInternal(@NotNull HttpServletRequest request, + @NotNull HttpServletResponse response, + @NotNull FilterChain chain) throws ServletException, IOException { + CacheRequestEnhanceWrapper cacheRequest; + CacheResponseEnhanceWrapper cacheResponse; + // 缓存 request, 解决二次读取问题 + if (request instanceof CacheRequestEnhanceWrapper) { + cacheRequest = (CacheRequestEnhanceWrapper) request; + } else { + cacheRequest = new CacheRequestEnhanceWrapper(new ContentCachingRequestWrapper(request)); + } + // 缓存 response, 解决二次读取问题 + if (response instanceof CacheResponseEnhanceWrapper) { + cacheResponse = (CacheResponseEnhanceWrapper) response; + } else { + cacheResponse = new CacheResponseEnhanceWrapper(new ContentCachingResponseWrapper(response)); + } + chain.doFilter(cacheRequest, cacheResponse); + // 在 filter 执行完成后, 将 response 拷贝回去, 避免二次读取时没有数据 + cacheResponse.copyBodyToResponse(); + } + +} diff --git a/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/handler/ServletErrorController.java b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/handler/ServletErrorController.java new file mode 100644 index 0000000..91da1bd --- /dev/null +++ b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/handler/ServletErrorController.java @@ -0,0 +1,119 @@ +package com.aivfo.el.web.handler; + +import com.aivfo.el.start.core.exception.BaseException; +import com.aivfo.el.starter.base.constant.BasicConstant; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Builder; +import lombok.Data; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.autoconfigure.web.ErrorProperties; +import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController; +import org.springframework.boot.web.servlet.error.ErrorAttributes; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.view.json.MappingJackson2JsonView; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Map; + +public class ServletErrorController extends BasicErrorController { + /** + * Object mapper + */ + @Resource + private ObjectMapper objectMapper; + + /** + * Servlet error controller + * + * @param errorAttributes error attributes + * @param errorProperties error properties + * @since 1.0.0 + */ + public ServletErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) { + super(errorAttributes, errorProperties); + } + + /** + * Error response entity + * + * @param request request + * @return the response entity + * @since 1.0.0 + */ + @Override + @RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity> error(HttpServletRequest request) { + this.checkException(request); + Map body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL)); + ResponseData data = this.processBody(request, body); + return new ResponseEntity<>(data.getBody(), data.getStatus()); + } + + /** + * 统一将 MediaType.TEXT_HTML_VALUE 转换成 json 返回 + * + * @param request request + * @param response response + * @return the model and view + * @since 1.0.0 + */ + @Override + public ModelAndView errorHtml(HttpServletRequest request, @NotNull HttpServletResponse response) { + this.checkException(request); + + Map body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL)); + ResponseData data = this.processBody(request, body); + response.setStatus(data.getStatus().value()); + MappingJackson2JsonView view = new MappingJackson2JsonView(); + view.setObjectMapper(this.objectMapper); + view.setContentType(MediaType.APPLICATION_JSON_VALUE); + return new ModelAndView(view, data.getBody()); + } + + /** + * Check exception + * + * @param request request + * @since 1.7.0 + */ + private void checkException(HttpServletRequest request) { + Object attribute = request.getAttribute(BasicConstant.REQUEST_EXCEPTION_INFO_ATTR); + if (attribute instanceof BaseException) { + throw (BaseException) attribute; + } + } + + /** + * Process body map + * + * @param request request + * @param body body + * @return the map + * @since 1.0.0 + */ + @Contract("_, _ -> new") + private ResponseData processBody(@NotNull HttpServletRequest request, Map body) { + return ResponseData.builder().body(body).status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + + @Data + @Builder + private static class ResponseData { + /** + * Body + */ + private Map body; + /** + * Status + */ + private HttpStatus status; + } + +} diff --git a/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/support/CacheRequestEnhanceWrapper.java b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/support/CacheRequestEnhanceWrapper.java new file mode 100644 index 0000000..be39e65 --- /dev/null +++ b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/support/CacheRequestEnhanceWrapper.java @@ -0,0 +1,383 @@ +package com.aivfo.el.web.support; + +import com.aivfo.el.starter.base.utils.Charsets; +import com.aivfo.el.starter.base.utils.IoUtils; +import com.aivfo.el.starter.core.util.WebUtils; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Contract; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver; +import org.springframework.web.util.ContentCachingRequestWrapper; + +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description: request 包装类, 用于自定义 header, body, params

+ * 从 body 中获取数据分 2 种情况 + * 1. content-type: application/x-www-form-urlencoded + * 2. content-type: application/json + * 将分别使用 {@link CacheRequestEnhanceWrapper#getParameter(String)} + * 和 {@link CacheRequestEnhanceWrapper#getInputStream()} 获取请求参数, 都会封装到实体或者字段中 + * 具体看 controller 使用哪种方式接受参数 + * getParameter() --> @RequestParam + * 1. url 参数 + * 2. form-data + * 3. x-www-form-urlencoded + * getInputStream() --> @RequestBody + * 1. json + * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.11.27 22:31 + * @since 1.0.0 + */ +@Slf4j +@EqualsAndHashCode(callSuper = true) +public class CacheRequestEnhanceWrapper extends HttpServletRequestWrapper { + /** + * 保存 io stream + */ + @Getter + @Setter + protected byte[] body; + /** + * Body string + */ + @Getter + protected String bodyString; + /** + * Content type + */ + protected String contentType; + /** + * Header map + */ + protected Map headerMap = new HashMap<>(16); + /** + * Params + */ + protected Map params = new HashMap<>(16); + /** + * Request + */ + @Getter + protected ContentCachingRequestWrapper cachingRequestWrapper; + + /** + * Instantiates a new Request wrapper. + * + * @param request the request + * @since 1.0.0 + */ + public CacheRequestEnhanceWrapper(ContentCachingRequestWrapper request) { + super(request); + try { + request.setCharacterEncoding(Charsets.UTF_8.name()); + } catch (UnsupportedEncodingException ignore) { + // nothing to do + } + this.cachingRequestWrapper = request; + this.params.putAll(request.getParameterMap()); + + if (request.getContentAsByteArray().length == 0) { + try (ServletInputStream inputStream = request.getInputStream()) { + this.body = IoUtils.toByteArray(inputStream); + } catch (IOException e) { + log.error("读取 inputStream 错误", e); + } + } else { + this.body = request.getContentAsByteArray(); + } + + this.bodyString = new String(this.body, StandardCharsets.UTF_8); + } + + /** + * Gets reader * + * + * @return the reader + * @since 1.0.0 + */ + @Override + public BufferedReader getReader() { + return new BufferedReader(new InputStreamReader(this.getInputStream())); + } + + /** + * 通过判断 content-type 类型来从不同的地方获取数据 + * 1. application/x-www-form-urlencoded --> RequestParameters + * 2. application/json --> inputstream + * + * @return the input streams + * @see ServletServerHttpRequest#getBody() ServletServerHttpRequest#getBody() + * @since 1.0.0 + */ + @Override + public ServletInputStream getInputStream() { + return WebUtils.getCacheInputStream(this.body); + } + + /** + * add a header with given name and value + * + * @param name the name + * @param value the value + * @since 1.0.0 + */ + public void addHeader(String name, String value) { + this.headerMap.put(name, value); + } + + /** + * Gets header * + * + * @param name name + * @return the header + * @since 1.0.0 + */ + @Override + public String getHeader(String name) { + String headerValue = super.getHeader(name); + if (this.headerMap.containsKey(name)) { + headerValue = this.headerMap.get(name); + } + return headerValue; + } + + /** + * Gets header names * + * + * @return the header names + * @since 1.0.0 + */ + @Override + public Enumeration getHeaderNames() { + List names = Collections.list(super.getHeaderNames()); + names.addAll(this.headerMap.keySet()); + return Collections.enumeration(names); + } + + /** + * Gets headers * + * + * @param name name + * @return the headers + * @since 1.0.0 + */ + @Override + public Enumeration getHeaders(String name) { + List values = Collections.list(super.getHeaders(name)); + if (this.headerMap.containsKey(name)) { + values = Collections.singletonList(this.headerMap.get(name)); + } + return Collections.enumeration(values); + } + + /** + * 通过判断 content-type 类型来从不同的地方获取数据 + * 1. application/x-www-form-urlencoded --> RequestParameters + * 2. application/json --> inputstream + * + * @param name the name + * @return the parameters + * @see ServletServerHttpRequest#getBody() + * @since 1.0.0 + */ + @Override + public String getParameter(String name) { + String[] values = this.params.get(name); + return values == null || values.length == 0 ? null : values[0]; + } + + /** + * Get parameter values string [ ] + * + * @param key key + * @return the string [ ] + * @since 1.0.0 + */ + @Override + public String[] getParameterValues(String key) { + return this.params.get(key); + } + + /** + * Sets parameter. + * + * @param key the key + * @param value the value + * @since 1.0.0 + */ + public void setParameter(String key, String value) { + if (value == null) { + return; + } + this.params.put(key, new String[]{value}); + } + + /** + * Sets parameter. + * + * @param key the key + * @param value the value + * @since 1.0.0 + */ + public void setParameter(String key, String[] value) { + if (value == null) { + return; + } + this.params.put(key, value); + } + + /** + * Sets parameter. + * + * @param key the key + * @param value the value + * @since 1.0.0 + */ + public void setParameter(String key, Object value) { + if (value == null) { + return; + } + this.params.put(key, new String[]{String.valueOf(value)}); + } + + /** + * Gets parameter map * + * + * @return the parameter map + * @since 1.0.0 + */ + @Override + public Map getParameterMap() { + return this.params; + } + + /** + * Gets parameter names * + * + * @return the parameter names + * @since 1.0.0 + */ + @Override + public Enumeration getParameterNames() { + return new MyEnumeration<>(this.params.keySet().iterator()); + } + + /** + * The type My enumeration. + * + * @param the type parameter + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.11.21 19:14 + * @since 1.0.0 + */ + static class MyEnumeration implements Enumeration { + + /** + * Iterator + */ + private final Iterator iterator; + + /** + * Instantiates a new My enumeration. + * + * @param iterator the iterator + * @since 1.0.0 + */ + @Contract(pure = true) + MyEnumeration(Iterator iterator) { + this.iterator = iterator; + } + + /** + * Has more elements boolean + * + * @return the boolean + * @since 1.0.0 + */ + @Override + public boolean hasMoreElements() { + return this.iterator.hasNext(); + } + + /** + * Next element e + * + * @return the e + * @since 1.0.0 + */ + @Override + public E nextElement() { + return this.iterator.next(); + } + + } + + /** + * Gets content type * + * + * @return the content type + * @since 1.0.0 + */ + @Override + public String getContentType() { + return this.contentType != null ? this.contentType : super.getContentType(); + } + + /** + * 修改请求类型 + * + * @param contentType the content type + * @see AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters + * @since 1.0.0 + */ + @SuppressWarnings("all") + public void setContentType(String contentType) { + this.addHeader(HttpHeaders.CONTENT_TYPE.toLowerCase(), MediaType.APPLICATION_JSON_VALUE); + this.contentType = contentType; + } + + /** + * 获取最原始的 request + * + * @param request request + * @return HttpServletRequest org request + * @since 1.0.0 + */ + @Contract("null -> null") + public static ServletRequest getOrgRequest(ServletRequest request) { + if (request instanceof CacheRequestEnhanceWrapper) { + return ((CacheRequestEnhanceWrapper) request).getOrgRequest(); + } + return request; + } + + /** + * 获取最原始的request + * + * @return HttpServletRequest org request + * @since 1.0.0 + */ + private ServletRequest getOrgRequest() { + return this.cachingRequestWrapper.getRequest(); + } +} diff --git a/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/support/CacheResponseEnhanceWrapper.java b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/support/CacheResponseEnhanceWrapper.java new file mode 100644 index 0000000..9218cc4 --- /dev/null +++ b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/support/CacheResponseEnhanceWrapper.java @@ -0,0 +1,70 @@ +package com.aivfo.el.web.support; + +import com.aivfo.el.start.core.exception.BaseException; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.util.ContentCachingResponseWrapper; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponseWrapper; +import java.io.IOException; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: + */ +@Slf4j +public class CacheResponseEnhanceWrapper extends HttpServletResponseWrapper { + /** + * Response + */ + @Getter + private final ContentCachingResponseWrapper response; + + /** + * Instantiates a new Response logging wrapper. + * + * @param response response from which we want to extract stream data + * @since 1.0.0 + */ + public CacheResponseEnhanceWrapper(ContentCachingResponseWrapper response) { + super(response); + this.response = response; + } + + /** + * Get content byte [ ] + * + * @return the byte [ ] + * @since 1.0.0 + */ + public byte[] getContent() { + return this.response.getContentAsByteArray(); + } + + /** + * Gets output stream * + * + * @return the output stream + * @throws IOException io exception + * @since 1.0.0 + */ + @Override + public ServletOutputStream getOutputStream() throws IOException { + return this.response.getOutputStream(); + } + + /** + * Copy body to response + * + * @since 1.0.0 + */ + public void copyBodyToResponse() { + try { + this.response.copyBodyToResponse(); + } catch (IOException e) { + throw new BaseException("缓存 Response 失败", e); + } + } +} diff --git a/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/util/InnerWebUtils.java b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/util/InnerWebUtils.java new file mode 100644 index 0000000..7176d2e --- /dev/null +++ b/band/aivfo-element/aivfo-element-web/src/main/java/com/aivfo/el/web/util/InnerWebUtils.java @@ -0,0 +1,66 @@ +package com.aivfo.el.web.util; + +import com.aivfo.el.starter.base.utils.StringPool; +import com.aivfo.el.starter.base.utils.StringUtils; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; +import org.springframework.boot.web.servlet.FilterRegistrationBean; + +import javax.servlet.Filter; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + *

Company: 成都返空汇网络技术有限公司

+ *

Description:

+ * + * @author dong4j + * @version 1.0.0 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2022.01.06 23:04 + * @since 2022.1.1 + */ +@UtilityClass +public class InnerWebUtils { + + /** + * Sets url patterns * + * + * @param filter logging rb + * @param order order + * @since 1.0.0 + */ + public void setUrlPatterns(@NotNull FilterRegistrationBean filter, int order) { + setUrlPatterns(filter, order, buildUrlPatterns()); + } + + /** + * Sets url patterns * + * + * @param filter filter + * @param order order + * @param urlPatterns url patterns + * @since 1.5.0 + */ + public void setUrlPatterns(@NotNull FilterRegistrationBean filter, int order, Collection urlPatterns) { + filter.setUrlPatterns(urlPatterns); + filter.setOrder(order); + } + + + /** + * Build url patterns + * + * @return the list + * @since 1.5.0 + */ + public @NotNull @Unmodifiable List buildUrlPatterns() { + //TODO + //String contextPath = ConfigKit.getProperty(ConfigKey.SpringConfigKey.SERVER_CONTEXT_PATH, StringPool.SLASH); + String contextPath = ""; + contextPath = StringUtils.removeSuffix(contextPath, StringPool.SLASH); + return Collections.singletonList(contextPath + StringPool.ANY_URL_PATTERNS); + } +} diff --git a/band/aivfo-element/pom.xml b/band/aivfo-element/pom.xml index f903750..8579761 100644 --- a/band/aivfo-element/pom.xml +++ b/band/aivfo-element/pom.xml @@ -3,16 +3,19 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - aivfo-framework + aivfo-project-build com.aivfo - stable - ../../pom.xml + 1.0.0-SNAPSHOT + ../../ability/aivfo-framework-build/aivfo-project-build/pom.xml 4.0.0 pom aivfo-element aivfo-element-base + aivfo-element-web + aivfo-element-core + aivfo-element-common \ No newline at end of file diff --git a/band/pom.xml b/band/pom.xml index 51f1307..0280bf4 100644 --- a/band/pom.xml +++ b/band/pom.xml @@ -10,4 +10,18 @@ aivfo-element + + + + nexus + releases + http://192.168.31.90:8081/repository/maven-releases/ + + + nexus + snapshots + http://192.168.31.90:8081/repository/maven-snapshots/ + + + \ No newline at end of file diff --git a/module/aivfo-dependencies-spring-boot-module/pom.xml b/module/aivfo-dependencies-spring-boot-module/pom.xml index 850d95f..43acc4d 100644 --- a/module/aivfo-dependencies-spring-boot-module/pom.xml +++ b/module/aivfo-dependencies-spring-boot-module/pom.xml @@ -28,6 +28,11 @@ aivfo-fast-dfs-client 1.0.0-SNAPSHOT + + com.aivfo + aivfo-kafka-spring-boot + 1.0.0-SNAPSHOT + \ No newline at end of file diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/pom.xml b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/pom.xml index 51980dd..6ea54b8 100644 --- a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/pom.xml +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/pom.xml @@ -15,6 +15,27 @@ com.aivfo aivfo-dfs-client-spring-boot-core + + org.springframework + spring-context + + + org.springframework.boot + spring-boot + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter + \ No newline at end of file diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/pom.xml b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/pom.xml index 8cfce83..b374878 100644 --- a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/pom.xml +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/pom.xml @@ -16,5 +16,9 @@ com.aivfo aivfo-fast-dfs-client + + cn.hutool + hutool-core + \ No newline at end of file diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/callback/DownloadCallBack.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/callback/DownloadCallBack.java new file mode 100644 index 0000000..356f38f --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/callback/DownloadCallBack.java @@ -0,0 +1,22 @@ +package com.aivfo.dfs.client.core.callback; + +import java.io.IOException; + +/** + * @author: wangyl + * @date: 2023/5/10 + * @description: 下再文件回调 + */ +@FunctionalInterface +public interface DownloadCallBack { + /** + * 接收数据 + * + * @param data 数据内容 + * @param dataSize 数据大小 + * @return java.lang.Boolean true成功 false失败 + * @Date 2023/5/10 + * @Author wangyl + */ + int receiveData(byte[] data, int dataSize); +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/callback/FileDownLoadCallBack.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/callback/FileDownLoadCallBack.java new file mode 100644 index 0000000..0a2bb64 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/callback/FileDownLoadCallBack.java @@ -0,0 +1,25 @@ +package com.aivfo.dfs.client.core.fastDFS.callback; + +import com.aivfo.dfs.client.core.callback.DownloadCallBack; +import com.csource.fastdfs.DownloadCallback; + +public class FileDownLoadCallBack implements DownloadCallback { + DownloadCallBack downloadCallBack; + + public FileDownLoadCallBack(DownloadCallBack downloadCallBack) { + this.downloadCallBack = downloadCallBack; + } + + /** + * recv file content callback function, may be called more than once when the file downloaded + * + * @param file_size file size + * @param data data buff + * @param bytes data bytes + * @return 0 success, return none zero(errno) if fail + */ + @Override + public int recv(long file_size, byte[] data, int bytes) { + return downloadCallBack.receiveData(data, bytes); + } +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/properties/FastDFSProperties.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/properties/FastDFSProperties.java deleted file mode 100644 index b47cd7b..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/properties/FastDFSProperties.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.aivfo.dfs.client.core.properties; - -import com.sun.java.swing.ui.ToggleActionPropertyChangeListener; -import lombok.Data; -import org.csource.fastdfs.ClientGlobal; - -import java.util.Map; -import java.util.Properties; - - -/** - * @author: wangyl - * @date: 2023/4/27 - * @description: FastDFS配置 - */ -@Data -public class FastDFSProperties { - /** - * 默认开启 - */ - private boolean enable = true; - /** - * Clients - */ - private FastDFSConnectorProperties properties; - - /** - * @author: wangyl - * @date: 2023/4/28 - * @description: FastDFS 连接配置 - */ - @Data - public static class FastDFSConnectorProperties { - /** - * 连接超时时间 - */ - private String connectTimeoutInSeconds; - /** - * 传输超时时间 - */ - private String networkTimeoutInSeconds; - /** - * 字符集编码 - */ - private String charset; - /** - * HTTP防盗链令牌 - */ - private String httpAntiStealToken; - /** - * HTTP加密密钥 - */ - private String httpSecretKey; - /** - * HTTP跟踪器端口 - */ - private String httpTrackerHttpPort; - /** - * tracker服务器 多个用,分割 - */ - private String trackerServers; - /** - * 连接池信息 - */ - private ConnectionPool connectionPool; - - } - - /** - * @author: wangyl - * @date: 2023/4/28 - * @description: 连接池配置 - */ - @Data - public static class ConnectionPool { - /** - * 是否启用连接池 默认启用 - */ - private Boolean enabled = true; - /** - * 最大数量 - */ - private String maxCountPerEntry; - /** - * 最大闲置时间 - */ - private String maxIdleTime; - /** - * 最大等待时间(毫秒) - */ - private String maxWaitTimeInMs; - } - - /** - * 转换成Properties对象初始化 - * - * @param - * @return java.util.Properties - * @Date 2023/4/28 - * @Author wangyl - */ - public Properties ConversionToProperties() { - Properties properties = new Properties(12); - properties.setProperty(ClientGlobal.PROP_KEY_CONNECT_TIMEOUT_IN_SECONDS, this.properties.getConnectTimeoutInSeconds()); - properties.setProperty(ClientGlobal.PROP_KEY_NETWORK_TIMEOUT_IN_SECONDS, this.properties.getNetworkTimeoutInSeconds()); - properties.setProperty(ClientGlobal.PROP_KEY_CHARSET, this.properties.getCharset()); - properties.setProperty(ClientGlobal.PROP_KEY_HTTP_ANTI_STEAL_TOKEN, this.properties.getHttpAntiStealToken()); - properties.setProperty(ClientGlobal.PROP_KEY_HTTP_SECRET_KEY, this.properties.getHttpSecretKey()); - properties.setProperty(ClientGlobal.PROP_KEY_HTTP_TRACKER_HTTP_PORT, this.properties.getTrackerServers()); - properties.setProperty(ClientGlobal.PROP_KEY_TRACKER_SERVERS, this.properties.getTrackerServers()); - properties.setProperty(ClientGlobal.PROP_KEY_CONNECTION_POOL_ENABLED, this.properties.getConnectionPool().getEnabled().toString()); - properties.setProperty(ClientGlobal.PROP_KEY_CONNECTION_POOL_MAX_COUNT_PER_ENTRY, this.properties.getConnectionPool().getMaxCountPerEntry()); - properties.setProperty(ClientGlobal.PROP_KEY_CONNECTION_POOL_MAX_IDLE_TIME, this.properties.getConnectionPool().getMaxIdleTime()); - properties.setProperty(ClientGlobal.PROP_KEY_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS, this.properties.getConnectionPool().getMaxWaitTimeInMs()); - return properties; - } - - -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/properties/client/Client.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/properties/client/Client.java deleted file mode 100644 index 82ec99f..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/properties/client/Client.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.aivfo.dfs.client.core.properties.client; - -import java.util.Map; - -/** - * @author: wangyl - * @date: 2023/4/27 - * @description: 客户端接口 - */ -public interface Client { - - /** - * 通过文件路径上传文件 - * - * @param filePath 文件全路径 - * @param fileName 文件名称 - * @return java.lang.String 文件结果路径 - * @Date 2023/4/27 - * @Author wangyl - */ - String uploadFile(String filePath, String fileName); - - /** - * 通过文件路径上传文件 可配置文件元数据以及上传组信息 - * - * @param filePath 文件全路径 - * @param fileName 文件名称 - * @param fileMetaList 文件元数据 - * @return java.lang.String 文件结果路径 - * @Date 2023/4/27 - * @Author wangyl - */ - String uploadFile(String filePath, String fileName, Map fileMetaList); - - /** - * 二进制流上传文件 - * - * @param fileData 文件内容 - * @param fileName 文件名称 - * @return java.lang.String - * @Date 2023/4/28 - * @Author wangyl - */ - String uploadFile(byte[] fileData, String fileName); - - /** - * 二进制流上传文件,附加文件属性 - * - * @param fileData - * @param fileName - * @param fileMetaList - * @return java.lang.String - * @Date 2023/4/28 - * @Author wangyl - */ - String uploadFile(byte[] fileData, String fileName, Map fileMetaList); - -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/properties/client/FastDFSClient.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/properties/client/FastDFSClient.java deleted file mode 100644 index 69347d9..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/properties/client/FastDFSClient.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.aivfo.dfs.client.core.properties.client; - -import com.aivfo.dfs.client.core.properties.FastDFSProperties; -import csource.fastdfs.*; -import csource.fastdfs.pool.Connection; -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; -import java.util.Map; -import java.util.Properties; - -/** - * @author: wangyl - * @date: 2023/4/28 - * @description: fastDFS客户端 - */ -@Slf4j -public class FastDFSClient implements Client { - - public FastDFSClient(FastDFSProperties fastDFSProperties) { - Properties properties = fastDFSProperties.ConversionToProperties(); - try { - ClientGlobal.initByProperties(properties); - } catch (Exception e) { - log.error("fastDFS初始化失败", e); - System.exit(-1); - } - } - - /** - * 初始化相关对象 - * - * @param - * @return org.csource.fastdfs.StorageClient - * @Date 2023/4/28 - * @Author wangyl - * @see csource.fastdfs.StorageClient#releaseConnection(Connection, boolean) - * 对象都是非线程安全的 所以每次调用的时候都需要初始化 storageServer 这个对象源码在每次操作后都会被设置为null - */ - private StorageClient getStorageClient() throws IOException { - TrackerClient tracker = new TrackerClient(); - TrackerServer trackerServer = tracker.getTrackerServer(); - StorageServer storageServer = null; - StorageClient storageClient = new StorageClient(trackerServer, storageServer); - return storageClient; - } - - @Override - public String uploadFile(String filePath, String fileName) { - return null; - } - - @Override - public String uploadFile(String filePath, String fileName, Map fileMetaList) { - return null; - } - - @Override - public String uploadFile(byte[] fileData, String fileName) { - return null; - } - - @Override - public String uploadFile(byte[] fileData, String fileName, Map fileMetaList) { - return null; - } - - private String updateFile() throws IOException { - //TODO 处理上传文件最底层接口 - StorageClient storageClient = getStorageClient(); - return ""; - } - -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-starter/pom.xml b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-starter/pom.xml index a17178a..a596228 100644 --- a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-starter/pom.xml +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-starter/pom.xml @@ -8,13 +8,11 @@ 1.0.0-SNAPSHOT 4.0.0 - - aivfo-dfs-client-spring-boot-starter - com.aivfo aivfo-dfs-client-spring-boot-autoconfigure + aivfo-dfs-client-spring-boot-starter \ No newline at end of file diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/common/Base64.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/common/Base64.java deleted file mode 100644 index 431abe6..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/common/Base64.java +++ /dev/null @@ -1,491 +0,0 @@ -package csource.common; - -import java.io.IOException; - -/** - * Freeware from: - * Roedy Green - * Canadian Mind Products - * #327 - 964 Heywood Avenue - * Victoria, BC Canada V8V 2Y5 - * tel:(250) 361-9093 - * mailto:roedy@mindprod.com - */ - -/** - * Encode arbitrary binary into printable ASCII using BASE64 encoding. - * very loosely based on the Base64 Reader by - * Dr. Mark Thornton - * Optrak Distribution Software Ltd. - * http://www.optrak.co.uk - * and Kevin Kelley's http://www.ruralnet.net/~kelley/java/Base64.java - *

- * Base64 is a way of encoding 8-bit characters using only ASCII printable - * characters similar to UUENCODE. UUENCODE includes a filename where BASE64 does not. - * The spec is described in RFC 2045. Base64 is a scheme where - * 3 bytes are concatenated, then split to form 4 groups of 6-bits each; and - * each 6-bits gets translated to an encoded printable ASCII character, via a - * table lookup. An encoded string is therefore longer than the original by - * about 1/3. The "=" character is used to pad the end. Base64 is used, - * among other things, to encode the user:password string in an - * Authorization: header for HTTP. Don't confuse Base64 with - * x-www-form-urlencoded which is handled by - * Java.net.URLEncoder.encode/decode - * If you don't like this code, there is another implementation at http://www.ruffboy.com/download.htm - * Sun has an undocumented method called sun.misc.Base64Encoder.encode. - * You could use hex, simpler to code, but not as compact. - *

- * If you wanted to encode a giant file, you could do it in large chunks that - * are even multiples of 3 bytes, except for the last chunk, and append the outputs. - *

- * To encode a string, rather than binary data java.net.URLEncoder may be better. See - * printable characters in the Java glossary for a discussion of the differences. - *

- * version 1.4 2002 February 15 -- correct bugs with uneven line lengths, - * allow you to configure line separator. - * now need Base64 object and instance methods. - * new mailing address. - * version 1.3 2000 September 12 -- fix problems with estimating output length in encode - * version 1.2 2000 September 09 -- now handles decode as well. - * version 1.1 1999 December 04 -- more symmetrical encoding algorithm. - * more accurate StringBuffer allocation size. - * version 1.0 1999 December 03 -- posted in comp.lang.java.programmer. - * Futures Streams or files. - */ - -public class Base64 { - - /** - * Marker value for chars we just ignore, e.g. \n \r high ascii - */ - static final int IGNORE = -1; - /** - * Marker for = trailing pad - */ - static final int PAD = -2; - /** - * used to disable test driver - */ - private static final boolean debug = true; - /** - * how we separate lines, e.g. \n, \r\n, \r etc. - */ - private String lineSeparator = System.getProperty("line.separator"); - /** - * max chars per line, excluding lineSeparator. A multiple of 4. - */ - private int lineLength = 72; - private char[] valueToChar = new char[64]; - /** - * binary value encoded by a given letter of the alphabet 0..63 - */ - private int[] charToValue = new int[256]; - private int[] charToPad = new int[4]; - - /* constructor */ - public Base64() { - this.init('+', '/', '='); - } - - /* constructor */ - public Base64(char chPlus, char chSplash, char chPad, int lineLength) { - this.init(chPlus, chSplash, chPad); - this.lineLength = lineLength; - } - - public Base64(int lineLength) { - this.lineLength = lineLength; - } - - /** - * debug display array - */ - public static void show(byte[] b) { - int count = 0; - int rows = 0; - - - for (int i = 0; i < b.length; i++) { - if (count == 8) { - System.out.print(" "); - } else if (count == 16) { - System.out.println(""); - count = 0; - continue; - } - System.out.print(Integer.toHexString(b[i] & 0xFF).toUpperCase() + " "); - count++; - - } - System.out.println(); - } - - /** - * debug display array - */ - public static void display(byte[] b) { - for (int i = 0; i < b.length; i++) { - System.out.print((char) b[i]); - } - System.out.println(); - } - - /** - * test driver - */ - public static void main(String[] args) { - test(); - System.exit(1); - - if (debug) { - try { - Base64 b64 = new Base64(); - String str = "agfrtu¿¦etʲ1234¼Ù´óerty¿Õ234·¢¿¦2344ʲµÄ"; - String str64 = ""; - - //encode - str64 = b64.encode(str.getBytes()); - System.out.println(str64); - - //decode - byte[] theBytes = b64.decode(str64); - show(theBytes); - String rst = new String(theBytes); - System.out.println(rst); - System.out.println(str); - } catch (Exception e) { - e.printStackTrace(); - } - //getBytes(String charsetName); -/* - byte[] a = { (byte)0xfc, (byte)0x0f, (byte)0xc0}; - byte[] b = { (byte)0x03, (byte)0xf0, (byte)0x3f}; - byte[] c = { (byte)0x00, (byte)0x00, (byte)0x00}; - byte[] d = { (byte)0xff, (byte)0xff, (byte)0xff}; - byte[] e = { (byte)0xfc, (byte)0x0f, (byte)0xc0, (byte)1}; - byte[] f = { (byte)0xfc, (byte)0x0f, (byte)0xc0, (byte)1, (byte)2}; - byte[] g = { (byte)0xfc, (byte)0x0f, (byte)0xc0, (byte)1, (byte)2, (byte)3}; - byte[] h = "AAAAAAAAAAB".getBytes(); - - - - show(a); - show(b); - show(c); - show(d); - show(e); - show(f); - show(g); - show(h); - Base64 b64 = new Base64(); - show(b64.decode(b64.encode(a))); - show(b64.decode(b64.encode(b))); - show(b64.decode(b64.encode(c))); - show(b64.decode(b64.encode(d))); - show(b64.decode(b64.encode(e))); - show(b64.decode(b64.encode(f))); - show(b64.decode(b64.encode(g))); - show(b64.decode(b64.encode(h))); - b64.setLineLength(8); - show((b64.encode(h)).getBytes()); -*/ - } - }// end main - - public static void test() { - try { - Base64 b64 = new Base64(); - - //encode - //str64 = b64.encode(str.getBytes()); - //System.out.println(str64); - - String str64 = "CwUEFYoAAAADjQMC7ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267EI="; - //decode - byte[] theBytes = b64.decode(str64); - show(theBytes); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /* initialise defaultValueToChar and defaultCharToValue tables */ - private void init(char chPlus, char chSplash, char chPad) { - int index = 0; - // build translate this.valueToChar table only once. - // 0..25 -> 'A'..'Z' - for (int i = 'A'; i <= 'Z'; i++) { - this.valueToChar[index++] = (char) i; - } - - // 26..51 -> 'a'..'z' - for (int i = 'a'; i <= 'z'; i++) { - this.valueToChar[index++] = (char) i; - } - - // 52..61 -> '0'..'9' - for (int i = '0'; i <= '9'; i++) { - this.valueToChar[index++] = (char) i; - } - - this.valueToChar[index++] = chPlus; - this.valueToChar[index++] = chSplash; - - // build translate defaultCharToValue table only once. - for (int i = 0; i < 256; i++) { - this.charToValue[i] = IGNORE; // default is to ignore - } - - for (int i = 0; i < 64; i++) { - this.charToValue[this.valueToChar[i]] = i; - } - - this.charToValue[chPad] = PAD; - java.util.Arrays.fill(this.charToPad, chPad); - } - - /** - * Encode an arbitrary array of bytes as Base64 printable ASCII. - * It will be broken into lines of 72 chars each. The last line is not - * terminated with a line separator. - * The output will always have an even multiple of data characters, - * exclusive of \n. It is padded out with =. - */ - public String encode(byte[] b) throws IOException { - // Each group or partial group of 3 bytes becomes four chars - // covered quotient - int outputLength = ((b.length + 2) / 3) * 4; - - // account for trailing newlines, on all but the very last line - if (lineLength != 0) { - int lines = (outputLength + lineLength - 1) / lineLength - 1; - if (lines > 0) { - outputLength += lines * lineSeparator.length(); - } - } - - // must be local for recursion to work. - StringBuffer sb = new StringBuffer(outputLength); - - // must be local for recursion to work. - int linePos = 0; - - // first deal with even multiples of 3 bytes. - int len = (b.length / 3) * 3; - int leftover = b.length - len; - for (int i = 0; i < len; i += 3) { - // Start a new line if next 4 chars won't fit on the current line - // We can't encapsulete the following code since the variable need to - // be local to this incarnation of encode. - linePos += 4; - if (linePos > lineLength) { - if (lineLength != 0) { - sb.append(lineSeparator); - } - linePos = 4; - } - - // get next three bytes in unsigned form lined up, - // in big-endian order - int combined = b[i + 0] & 0xff; - combined <<= 8; - combined |= b[i + 1] & 0xff; - combined <<= 8; - combined |= b[i + 2] & 0xff; - - // break those 24 bits into a 4 groups of 6 bits, - // working LSB to MSB. - int c3 = combined & 0x3f; - combined >>>= 6; - int c2 = combined & 0x3f; - combined >>>= 6; - int c1 = combined & 0x3f; - combined >>>= 6; - int c0 = combined & 0x3f; - - // Translate into the equivalent alpha character - // emitting them in big-endian order. - sb.append(valueToChar[c0]); - sb.append(valueToChar[c1]); - sb.append(valueToChar[c2]); - sb.append(valueToChar[c3]); - } - - // deal with leftover bytes - switch (leftover) { - case 0: - default: - // nothing to do - break; - - case 1: - // One leftover byte generates xx== - // Start a new line if next 4 chars won't fit on the current line - linePos += 4; - if (linePos > lineLength) { - - if (lineLength != 0) { - sb.append(lineSeparator); - } - linePos = 4; - } - - // Handle this recursively with a faked complete triple. - // Throw away last two chars and replace with == - sb.append(encode(new byte[]{b[len], 0, 0} - ).substring(0, 2)); - sb.append("=="); - break; - - case 2: - // Two leftover bytes generates xxx= - // Start a new line if next 4 chars won't fit on the current line - linePos += 4; - if (linePos > lineLength) { - if (lineLength != 0) { - sb.append(lineSeparator); - } - linePos = 4; - } - // Handle this recursively with a faked complete triple. - // Throw away last char and replace with = - sb.append(encode(new byte[]{b[len], b[len + 1], 0} - ).substring(0, 3)); - sb.append("="); - break; - - } // end switch; - - if (outputLength != sb.length()) { - System.out.println("oops: minor program flaw: output length mis-estimated"); - System.out.println("estimate:" + outputLength); - System.out.println("actual:" + sb.length()); - } - return sb.toString(); - }// end encode - - /** - * decode a well-formed complete Base64 string back into an array of bytes. - * It must have an even multiple of 4 data characters (not counting \n), - * padded out with = as needed. - */ - public byte[] decodeAuto(String s) { - int nRemain = s.length() % 4; - if (nRemain == 0) { - return this.decode(s); - } else { - return this.decode(s + new String(this.charToPad, 0, 4 - nRemain)); - } - } - - /** - * decode a well-formed complete Base64 string back into an array of bytes. - * It must have an even multiple of 4 data characters (not counting \n), - * padded out with = as needed. - */ - public byte[] decode(String s) { - - // estimate worst case size of output array, no embedded newlines. - byte[] b = new byte[(s.length() / 4) * 3]; - - // tracks where we are in a cycle of 4 input chars. - int cycle = 0; - - // where we combine 4 groups of 6 bits and take apart as 3 groups of 8. - int combined = 0; - - // how many bytes we have prepared. - int j = 0; - // will be an even multiple of 4 chars, plus some embedded \n - int len = s.length(); - int dummies = 0; - for (int i = 0; i < len; i++) { - - int c = s.charAt(i); - int value = (c <= 255) ? charToValue[c] : IGNORE; - // there are two magic values PAD (=) and IGNORE. - switch (value) { - case IGNORE: - // e.g. \n, just ignore it. - break; - - case PAD: - value = 0; - dummies++; - // fallthrough - default: - /* regular value character */ - switch (cycle) { - case 0: - combined = value; - cycle = 1; - break; - - case 1: - combined <<= 6; - combined |= value; - cycle = 2; - break; - - case 2: - combined <<= 6; - combined |= value; - cycle = 3; - break; - - case 3: - combined <<= 6; - combined |= value; - // we have just completed a cycle of 4 chars. - // the four 6-bit values are in combined in big-endian order - // peel them off 8 bits at a time working lsb to msb - // to get our original 3 8-bit bytes back - - b[j + 2] = (byte) combined; - combined >>>= 8; - b[j + 1] = (byte) combined; - combined >>>= 8; - b[j] = (byte) combined; - j += 3; - cycle = 0; - break; - } - break; - } - } // end for - if (cycle != 0) { - throw new ArrayIndexOutOfBoundsException("Input to decode not an even multiple of 4 characters; pad with =."); - } - j -= dummies; - if (b.length != j) { - byte[] b2 = new byte[j]; - System.arraycopy(b, 0, b2, 0, j); - b = b2; - } - return b; - - }// end decode - - /** - * determines how long the lines are that are generated by encode. - * Ignored by decode. - * - * @param length 0 means no newlines inserted. Must be a multiple of 4. - */ - public void setLineLength(int length) { - this.lineLength = (length / 4) * 4; - } - - /** - * How lines are separated. - * Ignored by decode. - * - * @param lineSeparator may be "" but not null. - * Usually contains only a combination of chars \n and \r. - * Could be any chars not in set A-Z a-z 0-9 + /. - */ - public void setLineSeparator(String lineSeparator) { - this.lineSeparator = lineSeparator; - } -} // end Base64 - diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/common/IniFileReader.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/common/IniFileReader.java deleted file mode 100644 index 17bb36f..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/common/IniFileReader.java +++ /dev/null @@ -1,215 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - **/ - -package csource.common; - -import java.io.*; -import java.util.ArrayList; -import java.util.Hashtable; - -/** - * ini file reader / parser - * - * @author Happy Fish / YuQing - * @version Version 1.0 - */ -public class IniFileReader { - private Hashtable paramTable; - private String conf_filename; - - /** - * @param conf_filename config filename - */ - public IniFileReader(String conf_filename) throws IOException { - this.conf_filename = conf_filename; - loadFromFile(conf_filename); - } - - public static ClassLoader classLoader() { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - if (loader == null) { - loader = ClassLoader.getSystemClassLoader(); - } - return loader; - } - - public static InputStream loadFromOsFileSystemOrClasspathAsStream(String filePath) { - InputStream in = null; - try { - // 优先从文件系统路径加载 - if (new File(filePath).exists()) { - in = new FileInputStream(filePath); - //System.out.println("loadFrom...file path done"); - } - // 从类路径加载 - else { - in = classLoader().getResourceAsStream(filePath); - //System.out.println("loadFrom...class path done"); - } - } catch (Exception ex) { - ex.printStackTrace(); - } - return in; - } - - /** - * get the config filename - * - * @return config filename - */ - public String getConfFilename() { - return this.conf_filename; - } - - /** - * get string value from config file - * - * @param name item name in config file - * @return string value - */ - public String getStrValue(String name) { - Object obj; - obj = this.paramTable.get(name); - if (obj == null) { - return null; - } - - if (obj instanceof String) { - return (String) obj; - } - - return (String) ((ArrayList) obj).get(0); - } - - /** - * get int value from config file - * - * @param name item name in config file - * @param default_value the default value - * @return int value - */ - public int getIntValue(String name, int default_value) { - String szValue = this.getStrValue(name); - if (szValue == null) { - return default_value; - } - - return Integer.parseInt(szValue); - } - - /** - * get boolean value from config file - * - * @param name item name in config file - * @param default_value the default value - * @return boolean value - */ - public boolean getBoolValue(String name, boolean default_value) { - String szValue = this.getStrValue(name); - if (szValue == null) { - return default_value; - } - - return szValue.equalsIgnoreCase("yes") || szValue.equalsIgnoreCase("on") || - szValue.equalsIgnoreCase("true") || szValue.equals("1"); - } - - /** - * get all values from config file - * - * @param name item name in config file - * @return string values (array) - */ - public String[] getValues(String name) { - Object obj; - String[] values; - - obj = this.paramTable.get(name); - if (obj == null) { - return null; - } - - if (obj instanceof String) { - values = new String[1]; - values[0] = (String) obj; - return values; - } - - Object[] objs = ((ArrayList) obj).toArray(); - values = new String[objs.length]; - System.arraycopy(objs, 0, values, 0, objs.length); - return values; - } - - private void loadFromFile(String confFilePath) throws IOException { - InputStream in = loadFromOsFileSystemOrClasspathAsStream(confFilePath); - try { - readToParamTable(in); - } catch (Exception ex) { - ex.printStackTrace(); - } finally { - try { - if (in != null) in.close(); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - } - - @SuppressWarnings("unchecked") - private void readToParamTable(InputStream in) throws IOException { - this.paramTable = new Hashtable(); - if (in == null) return; - String line; - String[] parts; - String name; - String value; - Object obj; - ArrayList valueList; - InputStreamReader inReader = null; - BufferedReader bufferedReader = null; - try { - inReader = new InputStreamReader(in); - bufferedReader = new BufferedReader(inReader); - while ((line = bufferedReader.readLine()) != null) { - line = line.trim(); - if (line.length() == 0 || line.charAt(0) == '#') { - continue; - } - parts = line.split("=", 2); - if (parts.length != 2) { - continue; - } - name = parts[0].trim(); - value = parts[1].trim(); - obj = this.paramTable.get(name); - if (obj == null) { - this.paramTable.put(name, value); - } else if (obj instanceof String) { - valueList = new ArrayList(); - valueList.add(obj); - valueList.add(value); - this.paramTable.put(name, valueList); - } else { - valueList = (ArrayList) obj; - valueList.add(value); - } - } - } catch (Exception ex) { - ex.printStackTrace(); - } finally { - try { - if (bufferedReader != null) bufferedReader.close(); - if (inReader != null) inReader.close(); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - } - -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/common/MyException.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/common/MyException.java deleted file mode 100644 index 1b57a1d..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/common/MyException.java +++ /dev/null @@ -1,24 +0,0 @@ -/* -* Copyright (C) 2008 Happy Fish / YuQing -* -* FastDFS Java Client may be copied only under the terms of the GNU Lesser -* General Public License (LGPL). -* Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. -*/ - -package csource.common; - -/** - * My Exception - * - * @author Happy Fish / YuQing - * @version Version 1.0 - */ -public class MyException extends Exception { - public MyException(String s, Exception e) { - } - - public MyException(String message) { - super(message); - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/common/NameValuePair.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/common/NameValuePair.java deleted file mode 100644 index 9ce3e28..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/common/NameValuePair.java +++ /dev/null @@ -1,48 +0,0 @@ -/* -* Copyright (C) 2008 Happy Fish / YuQing -* -* FastDFS Java Client may be copied only under the terms of the GNU Lesser -* General Public License (LGPL). -* Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. -*/ - -package csource.common; - -/** - * name(key) and value pair model - * - * @author Happy Fish / YuQing - * @version Version 1.0 - */ -public class NameValuePair { - protected String name; - protected String value; - - public NameValuePair() { - } - - public NameValuePair(String name) { - this.name = name; - } - - public NameValuePair(String name, String value) { - this.name = name; - this.value = value; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/ClientGlobal.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/ClientGlobal.java deleted file mode 100644 index a219bdb..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/ClientGlobal.java +++ /dev/null @@ -1,357 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - **/ - -package csource.fastdfs; - -import csource.common.IniFileReader; -import csource.common.MyException; - -import java.io.IOException; -import java.io.InputStream; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; - -/** - * Global variables - * - * @author Happy Fish / YuQing - * @version Version 1.11 - */ -public class ClientGlobal { - - public static final String CONF_KEY_CONNECT_TIMEOUT = "connect_timeout"; - public static final String CONF_KEY_NETWORK_TIMEOUT = "network_timeout"; - public static final String CONF_KEY_CHARSET = "charset"; - public static final String CONF_KEY_HTTP_ANTI_STEAL_TOKEN = "http.anti_steal_token"; - public static final String CONF_KEY_HTTP_SECRET_KEY = "http.secret_key"; - public static final String CONF_KEY_HTTP_TRACKER_HTTP_PORT = "http.tracker_http_port"; - public static final String CONF_KEY_TRACKER_SERVER = "tracker_server"; - public static final String PROP_KEY_CONNECT_TIMEOUT_IN_SECONDS = "fastdfs.connect_timeout_in_seconds"; - public static final String PROP_KEY_NETWORK_TIMEOUT_IN_SECONDS = "fastdfs.network_timeout_in_seconds"; - public static final String PROP_KEY_CHARSET = "fastdfs.charset"; - public static final String PROP_KEY_HTTP_ANTI_STEAL_TOKEN = "fastdfs.http_anti_steal_token"; - public static final String PROP_KEY_HTTP_SECRET_KEY = "fastdfs.http_secret_key"; - public static final String PROP_KEY_HTTP_TRACKER_HTTP_PORT = "fastdfs.http_tracker_http_port"; - public static final String PROP_KEY_TRACKER_SERVERS = "fastdfs.tracker_servers"; - - - public static final String PROP_KEY_CONNECTION_POOL_ENABLED = "fastdfs.connection_pool.enabled"; - public static final String PROP_KEY_CONNECTION_POOL_MAX_COUNT_PER_ENTRY = "fastdfs.connection_pool.max_count_per_entry"; - public static final String PROP_KEY_CONNECTION_POOL_MAX_IDLE_TIME = "fastdfs.connection_pool.max_idle_time"; - public static final String PROP_KEY_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS = "fastdfs.connection_pool.max_wait_time_in_ms"; - - public static final int DEFAULT_CONNECT_TIMEOUT = 5; //second - public static final int DEFAULT_NETWORK_TIMEOUT = 30; //second - public static final String DEFAULT_CHARSET = "UTF-8"; - public static final boolean DEFAULT_HTTP_ANTI_STEAL_TOKEN = false; - public static final String DEFAULT_HTTP_SECRET_KEY = "FastDFS1234567890"; - public static final int DEFAULT_HTTP_TRACKER_HTTP_PORT = 80; - - public static final boolean DEFAULT_CONNECTION_POOL_ENABLED = true; - public static final int DEFAULT_CONNECTION_POOL_MAX_COUNT_PER_ENTRY = 100; - public static final int DEFAULT_CONNECTION_POOL_MAX_IDLE_TIME = 3600;//second - public static final int DEFAULT_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS = 1000;//millisecond - - public static int g_connect_timeout = DEFAULT_CONNECT_TIMEOUT * 1000; //millisecond - public static int g_network_timeout = DEFAULT_NETWORK_TIMEOUT * 1000; //millisecond - public static String g_charset = DEFAULT_CHARSET; - public static boolean g_anti_steal_token = DEFAULT_HTTP_ANTI_STEAL_TOKEN; //if anti-steal token - public static String g_secret_key = DEFAULT_HTTP_SECRET_KEY; //generage token secret key - public static int g_tracker_http_port = DEFAULT_HTTP_TRACKER_HTTP_PORT; - - public static boolean g_connection_pool_enabled = DEFAULT_CONNECTION_POOL_ENABLED; - public static int g_connection_pool_max_count_per_entry = DEFAULT_CONNECTION_POOL_MAX_COUNT_PER_ENTRY; - public static int g_connection_pool_max_idle_time = DEFAULT_CONNECTION_POOL_MAX_IDLE_TIME * 1000; //millisecond - public static int g_connection_pool_max_wait_time_in_ms = DEFAULT_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS; //millisecond - - public static TrackerGroup g_tracker_group; - - private ClientGlobal() { - } - - /** - * load global variables - * - * @param conf_filename config filename - */ - public static void init(String conf_filename) throws IOException, MyException { - IniFileReader iniReader; - String[] szTrackerServers; - String[] parts; - - iniReader = new IniFileReader(conf_filename); - - g_connect_timeout = iniReader.getIntValue("connect_timeout", DEFAULT_CONNECT_TIMEOUT); - if (g_connect_timeout < 0) { - g_connect_timeout = DEFAULT_CONNECT_TIMEOUT; - } - g_connect_timeout *= 1000; //millisecond - - g_network_timeout = iniReader.getIntValue("network_timeout", DEFAULT_NETWORK_TIMEOUT); - if (g_network_timeout < 0) { - g_network_timeout = DEFAULT_NETWORK_TIMEOUT; - } - g_network_timeout *= 1000; //millisecond - - g_charset = iniReader.getStrValue("charset"); - if (g_charset == null || g_charset.length() == 0) { - g_charset = "ISO8859-1"; - } - - szTrackerServers = iniReader.getValues("tracker_server"); - if (szTrackerServers == null) { - throw new MyException("item \"tracker_server\" in " + conf_filename + " not found"); - } - - InetSocketAddress[] tracker_servers = new InetSocketAddress[szTrackerServers.length]; - for (int i = 0; i < szTrackerServers.length; i++) { - parts = szTrackerServers[i].split("\\:", 2); - if (parts.length != 2) { - throw new MyException("the value of item \"tracker_server\" is invalid, the correct format is host:port"); - } - - tracker_servers[i] = new InetSocketAddress(parts[0].trim(), Integer.parseInt(parts[1].trim())); - } - g_tracker_group = new TrackerGroup(tracker_servers); - - g_tracker_http_port = iniReader.getIntValue("http.tracker_http_port", 80); - g_anti_steal_token = iniReader.getBoolValue("http.anti_steal_token", false); - if (g_anti_steal_token) { - g_secret_key = iniReader.getStrValue("http.secret_key"); - } - g_connection_pool_enabled = iniReader.getBoolValue("connection_pool.enabled", DEFAULT_CONNECTION_POOL_ENABLED); - g_connection_pool_max_count_per_entry = iniReader.getIntValue("connection_pool.max_count_per_entry", DEFAULT_CONNECTION_POOL_MAX_COUNT_PER_ENTRY); - g_connection_pool_max_idle_time = iniReader.getIntValue("connection_pool.max_idle_time", DEFAULT_CONNECTION_POOL_MAX_IDLE_TIME); - if (g_connection_pool_max_idle_time < 0) { - g_connection_pool_max_idle_time = DEFAULT_CONNECTION_POOL_MAX_IDLE_TIME; - } - g_connection_pool_max_idle_time *= 1000; - g_connection_pool_max_wait_time_in_ms = iniReader.getIntValue("connection_pool.max_wait_time_in_ms", DEFAULT_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS); - if (g_connection_pool_max_wait_time_in_ms < 0) { - g_connection_pool_max_wait_time_in_ms = DEFAULT_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS; - } - } - - /** - * load from properties file - * - * @param propsFilePath properties file path, eg: - * "fastdfs-client.properties" - * "config/fastdfs-client.properties" - * "/opt/fastdfs-client.properties" - * "C:\\Users\\James\\config\\fastdfs-client.properties" - * properties文件至少包含一个配置项 fastdfs.tracker_servers 例如: - * fastdfs.tracker_servers = 10.0.11.245:22122,10.0.11.246:22122 - * server的IP和端口用冒号':'分隔 - * server之间用逗号','分隔 - */ - public static void initByProperties(String propsFilePath) throws IOException, MyException { - Properties props = new Properties(); - InputStream in = IniFileReader.loadFromOsFileSystemOrClasspathAsStream(propsFilePath); - if (in != null) { - props.load(in); - } - initByProperties(props); - } - - public static void initByProperties(Properties props) throws IOException, MyException { - String trackerServersConf = props.getProperty(PROP_KEY_TRACKER_SERVERS); - if (trackerServersConf == null || trackerServersConf.trim().length() == 0) { - throw new MyException(String.format("configure item %s is required", PROP_KEY_TRACKER_SERVERS)); - } - initByTrackers(trackerServersConf.trim()); - - String connectTimeoutInSecondsConf = props.getProperty(PROP_KEY_CONNECT_TIMEOUT_IN_SECONDS); - String networkTimeoutInSecondsConf = props.getProperty(PROP_KEY_NETWORK_TIMEOUT_IN_SECONDS); - String charsetConf = props.getProperty(PROP_KEY_CHARSET); - String httpAntiStealTokenConf = props.getProperty(PROP_KEY_HTTP_ANTI_STEAL_TOKEN); - String httpSecretKeyConf = props.getProperty(PROP_KEY_HTTP_SECRET_KEY); - String httpTrackerHttpPortConf = props.getProperty(PROP_KEY_HTTP_TRACKER_HTTP_PORT); - String poolEnabled = props.getProperty(PROP_KEY_CONNECTION_POOL_ENABLED); - String poolMaxCountPerEntry = props.getProperty(PROP_KEY_CONNECTION_POOL_MAX_COUNT_PER_ENTRY); - String poolMaxIdleTime = props.getProperty(PROP_KEY_CONNECTION_POOL_MAX_IDLE_TIME); - String poolMaxWaitTimeInMS = props.getProperty(PROP_KEY_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS); - if (connectTimeoutInSecondsConf != null && connectTimeoutInSecondsConf.trim().length() != 0) { - g_connect_timeout = Integer.parseInt(connectTimeoutInSecondsConf.trim()) * 1000; - } - if (networkTimeoutInSecondsConf != null && networkTimeoutInSecondsConf.trim().length() != 0) { - g_network_timeout = Integer.parseInt(networkTimeoutInSecondsConf.trim()) * 1000; - } - if (charsetConf != null && charsetConf.trim().length() != 0) { - g_charset = charsetConf.trim(); - } - if (httpAntiStealTokenConf != null && httpAntiStealTokenConf.trim().length() != 0) { - g_anti_steal_token = Boolean.parseBoolean(httpAntiStealTokenConf); - } - if (httpSecretKeyConf != null && httpSecretKeyConf.trim().length() != 0) { - g_secret_key = httpSecretKeyConf.trim(); - } - if (httpTrackerHttpPortConf != null && httpTrackerHttpPortConf.trim().length() != 0) { - g_tracker_http_port = Integer.parseInt(httpTrackerHttpPortConf); - } - if (poolEnabled != null && poolEnabled.trim().length() != 0) { - g_connection_pool_enabled = Boolean.parseBoolean(poolEnabled); - } - if (poolMaxCountPerEntry != null && poolMaxCountPerEntry.trim().length() != 0) { - g_connection_pool_max_count_per_entry = Integer.parseInt(poolMaxCountPerEntry); - } - if (poolMaxIdleTime != null && poolMaxIdleTime.trim().length() != 0) { - g_connection_pool_max_idle_time = Integer.parseInt(poolMaxIdleTime) * 1000; - } - if (poolMaxWaitTimeInMS != null && poolMaxWaitTimeInMS.trim().length() != 0) { - g_connection_pool_max_wait_time_in_ms = Integer.parseInt(poolMaxWaitTimeInMS); - } - } - - /** - * load from properties file - * - * @param trackerServers 例如:"10.0.11.245:22122,10.0.11.246:22122" - * server的IP和端口用冒号':'分隔 - * server之间用逗号','分隔 - */ - public static void initByTrackers(String trackerServers) throws IOException, MyException { - List list = new ArrayList(); - String spr1 = ","; - String spr2 = ":"; - String[] arr1 = trackerServers.trim().split(spr1); - for (String addrStr : arr1) { - String[] arr2 = addrStr.trim().split(spr2); - String host = arr2[0].trim(); - int port = Integer.parseInt(arr2[1].trim()); - list.add(new InetSocketAddress(host, port)); - } - InetSocketAddress[] trackerAddresses = list.toArray(new InetSocketAddress[list.size()]); - initByTrackers(trackerAddresses); - } - - public static void initByTrackers(InetSocketAddress[] trackerAddresses) throws IOException, MyException { - g_tracker_group = new TrackerGroup(trackerAddresses); - } - - /** - * construct Socket object - * - * @param ip_addr ip address or hostname - * @param port port number - * @return connected Socket object - */ - public static Socket getSocket(String ip_addr, int port) throws IOException { - Socket sock = new Socket(); - sock.setSoTimeout(ClientGlobal.g_network_timeout); - sock.connect(new InetSocketAddress(ip_addr, port), ClientGlobal.g_connect_timeout); - return sock; - } - - /** - * construct Socket object - * - * @param addr InetSocketAddress object, including ip address and port - * @return connected Socket object - */ - public static Socket getSocket(InetSocketAddress addr) throws IOException { - Socket sock = new Socket(); - sock.setReuseAddress(true); - sock.setSoTimeout(ClientGlobal.g_network_timeout); - sock.connect(addr, ClientGlobal.g_connect_timeout); - return sock; - } - - public static int getG_connect_timeout() { - return g_connect_timeout; - } - - public static void setG_connect_timeout(int connect_timeout) { - ClientGlobal.g_connect_timeout = connect_timeout; - } - - public static int getG_network_timeout() { - return g_network_timeout; - } - - public static void setG_network_timeout(int network_timeout) { - ClientGlobal.g_network_timeout = network_timeout; - } - - public static String getG_charset() { - return g_charset; - } - - public static void setG_charset(String charset) { - ClientGlobal.g_charset = charset; - } - - public static int getG_tracker_http_port() { - return g_tracker_http_port; - } - - public static void setG_tracker_http_port(int tracker_http_port) { - ClientGlobal.g_tracker_http_port = tracker_http_port; - } - - public static boolean getG_anti_steal_token() { - return g_anti_steal_token; - } - - public static boolean isG_anti_steal_token() { - return g_anti_steal_token; - } - - public static void setG_anti_steal_token(boolean anti_steal_token) { - ClientGlobal.g_anti_steal_token = anti_steal_token; - } - - public static String getG_secret_key() { - return g_secret_key; - } - - public static void setG_secret_key(String secret_key) { - ClientGlobal.g_secret_key = secret_key; - } - - public static TrackerGroup getG_tracker_group() { - return g_tracker_group; - } - - public static void setG_tracker_group(TrackerGroup tracker_group) { - ClientGlobal.g_tracker_group = tracker_group; - } - - public static boolean isG_connection_pool_enabled() { - return g_connection_pool_enabled; - } - - public static String configInfo() { - String trackerServers = ""; - if (g_tracker_group != null) { - InetSocketAddress[] trackerAddresses = g_tracker_group.tracker_servers; - for (InetSocketAddress inetSocketAddress : trackerAddresses) { - if (trackerServers.length() > 0) trackerServers += ","; - String address = inetSocketAddress.toString(); - trackerServers += address.startsWith("/") ? address.substring(1) : address; - } - } - return "{" - + "\n g_connect_timeout(ms) = " + g_connect_timeout - + "\n g_network_timeout(ms) = " + g_network_timeout - + "\n g_charset = " + g_charset - + "\n g_anti_steal_token = " + g_anti_steal_token - + "\n g_secret_key = " + g_secret_key - + "\n g_tracker_http_port = " + g_tracker_http_port - + "\n g_connection_pool_enabled = " + g_connection_pool_enabled - + "\n g_connection_pool_max_count_per_entry = " + g_connection_pool_max_count_per_entry - + "\n g_connection_pool_max_idle_time(ms) = " + g_connection_pool_max_idle_time - + "\n g_connection_pool_max_wait_time_in_ms(ms) = " + g_connection_pool_max_wait_time_in_ms - + "\n trackerServers = " + trackerServers - + "\n}"; - } - -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/DownloadCallback.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/DownloadCallback.java deleted file mode 100644 index 2d08268..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/DownloadCallback.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - -/** - * Download file callback interface - * - * @author Happy Fish / YuQing - * @version Version 1.4 - */ -public interface DownloadCallback { - /** - * recv file content callback function, may be called more than once when the file downloaded - * - * @param file_size file size - * @param data data buff - * @param bytes data bytes - * @return 0 success, return none zero(errno) if fail - */ - int recv(long file_size, byte[] data, int bytes); -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/DownloadStream.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/DownloadStream.java deleted file mode 100644 index b3628b8..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/DownloadStream.java +++ /dev/null @@ -1,45 +0,0 @@ -package csource.fastdfs; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * Download file by stream (download callback class) - * - * @author zhouzezhong & Happy Fish / YuQing - * @version Version 1.11 - */ -public class DownloadStream implements DownloadCallback { - private OutputStream out; - private long currentBytes = 0; - - public DownloadStream(OutputStream out) { - super(); - this.out = out; - } - - /** - * recv file content callback function, may be called more than once when the file downloaded - * - * @param fileSize file size - * @param data data buff - * @param bytes data bytes - * @return 0 success, return none zero(errno) if fail - */ - @Override - public int recv(long fileSize, byte[] data, int bytes) { - try { - out.write(data, 0, bytes); - } catch (IOException ex) { - ex.printStackTrace(); - return -1; - } - - currentBytes += bytes; - if (this.currentBytes == fileSize) { - this.currentBytes = 0; - } - - return 0; - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/FileInfo.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/FileInfo.java deleted file mode 100644 index 4466a77..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/FileInfo.java +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - -import java.text.SimpleDateFormat; -import java.util.Date; - -/** - * Server Info - * - * @author Happy Fish / YuQing - * @version Version 1.23 - */ -public class FileInfo { - public static final short FILE_TYPE_NORMAL = 1; - public static final short FILE_TYPE_APPENDER = 2; - public static final short FILE_TYPE_SLAVE = 4; - - protected boolean fetch_from_server; - protected short file_type; - protected String source_ip_addr; - protected long file_size; - protected Date create_timestamp; - protected int crc32; - - /** - * Constructor - * - * @param fetch_from_server if fetch from server flag - * @param file_type the file type - * @param file_size the file size - * @param create_timestamp create timestamp in seconds - * @param crc32 the crc32 signature - * @param source_ip_addr the source storage ip address - */ - public FileInfo(boolean fetch_from_server, short file_type, long file_size, - int create_timestamp, int crc32, String source_ip_addr) { - this.fetch_from_server = fetch_from_server; - this.file_type = file_type; - this.file_size = file_size; - this.create_timestamp = new Date(create_timestamp * 1000L); - this.crc32 = crc32; - this.source_ip_addr = source_ip_addr; - } - - /** - * get the fetch_from_server flag - * - * @return the fetch_from_server flag - */ - public boolean getFetchFromServer() { - return this.fetch_from_server; - } - - /** - * set the fetch_from_server flag - * - * @param fetch_from_server the fetch from server flag - */ - public void setFetchFromServer(boolean fetch_from_server) { - this.fetch_from_server = fetch_from_server; - } - - /** - * get the file type - * - * @return the file type - */ - public short getFileType() { - return this.file_type; - } - - /** - * set the file type - * - * @param file_type the file type - */ - public void setFileType(short file_type) { - this.file_type = file_type; - } - - /** - * get the source ip address of the file uploaded to - * - * @return the source ip address of the file uploaded to - */ - public String getSourceIpAddr() { - return this.source_ip_addr; - } - - /** - * set the source ip address of the file uploaded to - * - * @param source_ip_addr the source ip address - */ - public void setSourceIpAddr(String source_ip_addr) { - this.source_ip_addr = source_ip_addr; - } - - /** - * get the file size - * - * @return the file size - */ - public long getFileSize() { - return this.file_size; - } - - /** - * set the file size - * - * @param file_size the file size - */ - public void setFileSize(long file_size) { - this.file_size = file_size; - } - - /** - * get the create timestamp of the file - * - * @return the create timestamp of the file - */ - public Date getCreateTimestamp() { - return this.create_timestamp; - } - - /** - * set the create timestamp of the file - * - * @param create_timestamp create timestamp in seconds - */ - public void setCreateTimestamp(int create_timestamp) { - this.create_timestamp = new Date(create_timestamp * 1000L); - } - - /** - * get the file CRC32 signature - * - * @return the file CRC32 signature - */ - public long getCrc32() { - return this.crc32; - } - - /** - * set the create timestamp of the file - * - * @param crc32 the crc32 signature - */ - public void setCrc32(int crc32) { - this.crc32 = crc32; - } - - /** - * to string - * - * @return string - */ - @Override - public String toString() { - SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - return "fetch_from_server = " + this.fetch_from_server + ", " + - "file_type = " + this.file_type + ", " + - "source_ip_addr = " + this.source_ip_addr + ", " + - "file_size = " + this.file_size + ", " + - "create_timestamp = " + df.format(this.create_timestamp) + ", " + - "crc32 = " + this.crc32; - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/ProtoCommon.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/ProtoCommon.java deleted file mode 100644 index e206225..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/ProtoCommon.java +++ /dev/null @@ -1,506 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - **/ - -package csource.fastdfs; - - -import csource.common.MyException; -import csource.common.NameValuePair; - -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.Socket; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -/** - * protocol common functions - * - * @author Happy Fish / YuQing - * @version Version 1.18 - */ -public class ProtoCommon { - public static final byte FDFS_PROTO_CMD_QUIT = 82; - public static final byte TRACKER_PROTO_CMD_SERVER_LIST_GROUP = 91; - public static final byte TRACKER_PROTO_CMD_SERVER_LIST_STORAGE = 92; - public static final byte TRACKER_PROTO_CMD_SERVER_DELETE_STORAGE = 93; - public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE = 101; - public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE = 102; - public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE = 103; - public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE = 104; - public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL = 105; - public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ALL = 106; - public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ALL = 107; - public static final byte TRACKER_PROTO_CMD_RESP = 100; - public static final byte FDFS_PROTO_CMD_ACTIVE_TEST = 111; - public static final byte STORAGE_PROTO_CMD_UPLOAD_FILE = 11; - public static final byte STORAGE_PROTO_CMD_DELETE_FILE = 12; - public static final byte STORAGE_PROTO_CMD_SET_METADATA = 13; - public static final byte STORAGE_PROTO_CMD_DOWNLOAD_FILE = 14; - public static final byte STORAGE_PROTO_CMD_GET_METADATA = 15; - public static final byte STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE = 21; - public static final byte STORAGE_PROTO_CMD_QUERY_FILE_INFO = 22; - public static final byte STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE = 23; //create appender file - public static final byte STORAGE_PROTO_CMD_APPEND_FILE = 24; //append file - public static final byte STORAGE_PROTO_CMD_MODIFY_FILE = 34; //modify appender file - public static final byte STORAGE_PROTO_CMD_TRUNCATE_FILE = 36; //truncate appender file - public static final byte STORAGE_PROTO_CMD_REGENERATE_APPENDER_FILENAME = 38; //rename appender file to normal file - - public static final byte STORAGE_PROTO_CMD_RESP = TRACKER_PROTO_CMD_RESP; - public static final byte FDFS_STORAGE_STATUS_INIT = 0; - public static final byte FDFS_STORAGE_STATUS_WAIT_SYNC = 1; - public static final byte FDFS_STORAGE_STATUS_SYNCING = 2; - public static final byte FDFS_STORAGE_STATUS_IP_CHANGED = 3; - public static final byte FDFS_STORAGE_STATUS_DELETED = 4; - public static final byte FDFS_STORAGE_STATUS_OFFLINE = 5; - public static final byte FDFS_STORAGE_STATUS_ONLINE = 6; - public static final byte FDFS_STORAGE_STATUS_ACTIVE = 7; - public static final byte FDFS_STORAGE_STATUS_NONE = 99; - /** - * for overwrite all old metadata - */ - public static final byte STORAGE_SET_METADATA_FLAG_OVERWRITE = 'O'; - /** - * for replace, insert when the meta item not exist, otherwise update it - */ - public static final byte STORAGE_SET_METADATA_FLAG_MERGE = 'M'; - public static final int FDFS_PROTO_PKG_LEN_SIZE = 8; - public static final int FDFS_PROTO_CMD_SIZE = 1; - public static final int FDFS_GROUP_NAME_MAX_LEN = 16; - public static final int FDFS_IPADDR_SIZE = 16; - public static final int FDFS_DOMAIN_NAME_MAX_SIZE = 128; - public static final int FDFS_VERSION_SIZE = 6; - public static final int FDFS_STORAGE_ID_MAX_SIZE = 16; - public static final String FDFS_RECORD_SEPERATOR = "\u0001"; - public static final String FDFS_FIELD_SEPERATOR = "\u0002"; - public static final int TRACKER_QUERY_STORAGE_FETCH_BODY_LEN = FDFS_GROUP_NAME_MAX_LEN - + FDFS_IPADDR_SIZE - 1 + FDFS_PROTO_PKG_LEN_SIZE; - public static final int TRACKER_QUERY_STORAGE_STORE_BODY_LEN = FDFS_GROUP_NAME_MAX_LEN - + FDFS_IPADDR_SIZE + FDFS_PROTO_PKG_LEN_SIZE; - public static final byte FDFS_FILE_EXT_NAME_MAX_LEN = 6; - public static final byte FDFS_FILE_PREFIX_MAX_LEN = 16; - public static final byte FDFS_FILE_PATH_LEN = 10; - public static final byte FDFS_FILENAME_BASE64_LENGTH = 27; - public static final byte FDFS_TRUNK_FILE_INFO_LEN = 16; - public static final byte ERR_NO_ENOENT = 2; - public static final byte ERR_NO_EIO = 5; - public static final byte ERR_NO_EBUSY = 16; - public static final byte ERR_NO_EINVAL = 22; - public static final byte ERR_NO_ENOSPC = 28; - public static final byte ECONNREFUSED = 61; - public static final byte ERR_NO_EALREADY = 114; - public static final long INFINITE_FILE_SIZE = 256 * 1024L * 1024 * 1024 * 1024 * 1024L; - public static final long APPENDER_FILE_SIZE = INFINITE_FILE_SIZE; - public static final long TRUNK_FILE_MARK_SIZE = 512 * 1024L * 1024 * 1024 * 1024 * 1024L; - public static final long NORMAL_LOGIC_FILENAME_LENGTH = FDFS_FILE_PATH_LEN + FDFS_FILENAME_BASE64_LENGTH + FDFS_FILE_EXT_NAME_MAX_LEN + 1; - public static final long TRUNK_LOGIC_FILENAME_LENGTH = NORMAL_LOGIC_FILENAME_LENGTH + FDFS_TRUNK_FILE_INFO_LEN; - protected static final int PROTO_HEADER_CMD_INDEX = FDFS_PROTO_PKG_LEN_SIZE; - protected static final int PROTO_HEADER_STATUS_INDEX = FDFS_PROTO_PKG_LEN_SIZE + 1; - - private ProtoCommon() { - } - - public static String getStorageStatusCaption(byte status) { - switch (status) { - case FDFS_STORAGE_STATUS_INIT: - return "INIT"; - case FDFS_STORAGE_STATUS_WAIT_SYNC: - return "WAIT_SYNC"; - case FDFS_STORAGE_STATUS_SYNCING: - return "SYNCING"; - case FDFS_STORAGE_STATUS_IP_CHANGED: - return "IP_CHANGED"; - case FDFS_STORAGE_STATUS_DELETED: - return "DELETED"; - case FDFS_STORAGE_STATUS_OFFLINE: - return "OFFLINE"; - case FDFS_STORAGE_STATUS_ONLINE: - return "ONLINE"; - case FDFS_STORAGE_STATUS_ACTIVE: - return "ACTIVE"; - case FDFS_STORAGE_STATUS_NONE: - return "NONE"; - default: - return "UNKOWN"; - } - } - - /** - * pack header by FastDFS transfer protocol - * - * @param cmd which command to send - * @param pkg_len package body length - * @param errno status code, should be (byte)0 - * @return packed byte buffer - */ - public static byte[] packHeader(byte cmd, long pkg_len, byte errno) throws UnsupportedEncodingException { - byte[] header; - byte[] hex_len; - - header = new byte[FDFS_PROTO_PKG_LEN_SIZE + 2]; - Arrays.fill(header, (byte) 0); - - hex_len = ProtoCommon.long2buff(pkg_len); - System.arraycopy(hex_len, 0, header, 0, hex_len.length); - header[PROTO_HEADER_CMD_INDEX] = cmd; - header[PROTO_HEADER_STATUS_INDEX] = errno; - return header; - } - - /** - * receive pack header - * - * @param in input stream - * @param expect_cmd expect response command - * @param expect_body_len expect response package body length - * @return RecvHeaderInfo: errno and pkg body length - */ - public static RecvHeaderInfo recvHeader(InputStream in, byte expect_cmd, long expect_body_len) throws IOException { - byte[] header; - int bytes; - long pkg_len; - - header = new byte[FDFS_PROTO_PKG_LEN_SIZE + 2]; - - if ((bytes = in.read(header)) != header.length) { - throw new IOException("recv package size " + bytes + " != " + header.length); - } - - if (header[PROTO_HEADER_CMD_INDEX] != expect_cmd) { - throw new IOException("recv cmd: " + header[PROTO_HEADER_CMD_INDEX] + " is not correct, expect cmd: " + expect_cmd); - } - - if (header[PROTO_HEADER_STATUS_INDEX] != 0) { - return new RecvHeaderInfo(header[PROTO_HEADER_STATUS_INDEX], 0); - } - - pkg_len = ProtoCommon.buff2long(header, 0); - if (pkg_len < 0) { - throw new IOException("recv body length: " + pkg_len + " < 0!"); - } - - if (expect_body_len >= 0 && pkg_len != expect_body_len) { - throw new IOException("recv body length: " + pkg_len + " is not correct, expect length: " + expect_body_len); - } - - return new RecvHeaderInfo((byte) 0, pkg_len); - } - - /** - * receive whole pack - * - * @param in input stream - * @param expect_cmd expect response command - * @param expect_body_len expect response package body length - * @return RecvPackageInfo: errno and reponse body(byte buff) - */ - public static RecvPackageInfo recvPackage(InputStream in, byte expect_cmd, long expect_body_len) throws IOException { - RecvHeaderInfo header = recvHeader(in, expect_cmd, expect_body_len); - if (header.errno != 0) { - return new RecvPackageInfo(header.errno, null); - } - - byte[] body = new byte[(int) header.body_len]; - int totalBytes = 0; - int remainBytes = (int) header.body_len; - int bytes; - - while (totalBytes < header.body_len) { - if ((bytes = in.read(body, totalBytes, remainBytes)) < 0) { - break; - } - - totalBytes += bytes; - remainBytes -= bytes; - } - - if (totalBytes != header.body_len) { - throw new IOException("recv package size " + totalBytes + " != " + header.body_len); - } - - return new RecvPackageInfo((byte) 0, body); - } - - /** - * split metadata to name value pair array - * - * @param meta_buff metadata - * @return name value pair array - */ - public static NameValuePair[] split_metadata(String meta_buff) { - return split_metadata(meta_buff, FDFS_RECORD_SEPERATOR, FDFS_FIELD_SEPERATOR); - } - - /** - * split metadata to name value pair array - * - * @param meta_buff metadata - * @param recordSeperator record/row seperator - * @param filedSeperator field/column seperator - * @return name value pair array - */ - public static NameValuePair[] split_metadata(String meta_buff, - String recordSeperator, String filedSeperator) { - String[] rows; - String[] cols; - NameValuePair[] meta_list; - - rows = meta_buff.split(recordSeperator); - meta_list = new NameValuePair[rows.length]; - for (int i = 0; i < rows.length; i++) { - cols = rows[i].split(filedSeperator, 2); - meta_list[i] = new NameValuePair(cols[0]); - if (cols.length == 2) { - meta_list[i].setValue(cols[1]); - } - } - - return meta_list; - } - - /** - * pack metadata array to string - * - * @param meta_list metadata array - * @return packed metadata - */ - public static String pack_metadata(NameValuePair[] meta_list) { - if (meta_list.length == 0) { - return ""; - } - - StringBuffer sb = new StringBuffer(32 * meta_list.length); - sb.append(meta_list[0].getName()).append(FDFS_FIELD_SEPERATOR).append(meta_list[0].getValue()); - for (int i = 1; i < meta_list.length; i++) { - sb.append(FDFS_RECORD_SEPERATOR); - sb.append(meta_list[i].getName()).append(FDFS_FIELD_SEPERATOR).append(meta_list[i].getValue()); - } - - return sb.toString(); - } - - /** - * send quit command to server and close socket - * - * @param sock the Socket object - */ - public static void closeSocket(Socket sock) throws IOException { - byte[] header; - header = packHeader(FDFS_PROTO_CMD_QUIT, 0, (byte) 0); - sock.getOutputStream().write(header); - sock.close(); - } - - /** - * send ACTIVE_TEST command to server, test if network is ok and the server is alive - * - * @param sock the Socket object - */ - public static boolean activeTest(Socket sock) throws IOException { - byte[] header; - header = packHeader(FDFS_PROTO_CMD_ACTIVE_TEST, 0, (byte) 0); - sock.getOutputStream().write(header); - - RecvHeaderInfo headerInfo = recvHeader(sock.getInputStream(), TRACKER_PROTO_CMD_RESP, 0); - return headerInfo.errno == 0 ? true : false; - } - - /** - * long convert to buff (big-endian) - * - * @param n long number - * @return 8 bytes buff - */ - public static byte[] long2buff(long n) { - byte[] bs; - - bs = new byte[8]; - bs[0] = (byte) ((n >> 56) & 0xFF); - bs[1] = (byte) ((n >> 48) & 0xFF); - bs[2] = (byte) ((n >> 40) & 0xFF); - bs[3] = (byte) ((n >> 32) & 0xFF); - bs[4] = (byte) ((n >> 24) & 0xFF); - bs[5] = (byte) ((n >> 16) & 0xFF); - bs[6] = (byte) ((n >> 8) & 0xFF); - bs[7] = (byte) (n & 0xFF); - - return bs; - } - - /** - * buff convert to long - * - * @param bs the buffer (big-endian) - * @param offset the start position based 0 - * @return long number - */ - public static long buff2long(byte[] bs, int offset) { - return (((long) (bs[offset] >= 0 ? bs[offset] : 256 + bs[offset])) << 56) | - (((long) (bs[offset + 1] >= 0 ? bs[offset + 1] : 256 + bs[offset + 1])) << 48) | - (((long) (bs[offset + 2] >= 0 ? bs[offset + 2] : 256 + bs[offset + 2])) << 40) | - (((long) (bs[offset + 3] >= 0 ? bs[offset + 3] : 256 + bs[offset + 3])) << 32) | - (((long) (bs[offset + 4] >= 0 ? bs[offset + 4] : 256 + bs[offset + 4])) << 24) | - (((long) (bs[offset + 5] >= 0 ? bs[offset + 5] : 256 + bs[offset + 5])) << 16) | - (((long) (bs[offset + 6] >= 0 ? bs[offset + 6] : 256 + bs[offset + 6])) << 8) | - ((long) (bs[offset + 7] >= 0 ? bs[offset + 7] : 256 + bs[offset + 7])); - } - - /** - * buff convert to int - * - * @param bs the buffer (big-endian) - * @param offset the start position based 0 - * @return int number - */ - public static int buff2int(byte[] bs, int offset) { - return (((int) (bs[offset] >= 0 ? bs[offset] : 256 + bs[offset])) << 24) | - (((int) (bs[offset + 1] >= 0 ? bs[offset + 1] : 256 + bs[offset + 1])) << 16) | - (((int) (bs[offset + 2] >= 0 ? bs[offset + 2] : 256 + bs[offset + 2])) << 8) | - ((int) (bs[offset + 3] >= 0 ? bs[offset + 3] : 256 + bs[offset + 3])); - } - - /** - * buff convert to ip address - * - * @param bs the buffer (big-endian) - * @param offset the start position based 0 - * @return ip address - */ - public static String getIpAddress(byte[] bs, int offset) { - if (bs[0] == 0 || bs[3] == 0) //storage server ID - { - return ""; - } - - int n; - StringBuilder sbResult = new StringBuilder(16); - for (int i = offset; i < offset + 4; i++) { - n = (bs[i] >= 0) ? bs[i] : 256 + bs[i]; - if (sbResult.length() > 0) { - sbResult.append("."); - } - sbResult.append(String.valueOf(n)); - } - - return sbResult.toString(); - } - - /** - * md5 function - * - * @param source the input buffer - * @return md5 string - */ - public static String md5(byte[] source) throws NoSuchAlgorithmException { - char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5"); - md.update(source); - byte tmp[] = md.digest(); - char str[] = new char[32]; - int k = 0; - for (int i = 0; i < 16; i++) { - str[k++] = hexDigits[tmp[i] >>> 4 & 0xf]; - str[k++] = hexDigits[tmp[i] & 0xf]; - } - - return new String(str); - } - - /** - * get token for file URL - * - * @param remote_filename the filename return by FastDFS server - * @param ts unix timestamp, unit: second - * @param secret_key the secret key - * @return token string - */ - public static String getToken(String remote_filename, int ts, String secret_key) throws UnsupportedEncodingException, NoSuchAlgorithmException, MyException { - byte[] bsFilename = remote_filename.getBytes(ClientGlobal.g_charset); - byte[] bsKey = secret_key.getBytes(ClientGlobal.g_charset); - byte[] bsTimestamp = (new Integer(ts)).toString().getBytes(ClientGlobal.g_charset); - - byte[] buff = new byte[bsFilename.length + bsKey.length + bsTimestamp.length]; - System.arraycopy(bsFilename, 0, buff, 0, bsFilename.length); - System.arraycopy(bsKey, 0, buff, bsFilename.length, bsKey.length); - System.arraycopy(bsTimestamp, 0, buff, bsFilename.length + bsKey.length, bsTimestamp.length); - - return md5(buff); - } - - /** - * generate slave filename - * - * @param master_filename the master filename to generate the slave filename - * @param prefix_name the prefix name to generate the slave filename - * @param ext_name the extension name of slave filename, null for same as the master extension name - * @return slave filename string - */ - public static String genSlaveFilename(String master_filename, - String prefix_name, String ext_name) throws MyException { - String true_ext_name; - int dotIndex; - - if (master_filename.length() < 28 + FDFS_FILE_EXT_NAME_MAX_LEN) { - throw new MyException("master filename \"" + master_filename + "\" is invalid"); - } - - dotIndex = master_filename.indexOf('.', master_filename.length() - (FDFS_FILE_EXT_NAME_MAX_LEN + 1)); - if (ext_name != null) { - if (ext_name.length() == 0) { - true_ext_name = ""; - } else if (ext_name.charAt(0) == '.') { - true_ext_name = ext_name; - } else { - true_ext_name = "." + ext_name; - } - } else { - if (dotIndex < 0) { - true_ext_name = ""; - } else { - true_ext_name = master_filename.substring(dotIndex); - } - } - - if (true_ext_name.length() == 0 && prefix_name.equals("-m")) { - throw new MyException("prefix_name \"" + prefix_name + "\" is invalid"); - } - - if (dotIndex < 0) { - return master_filename + prefix_name + true_ext_name; - } else { - return master_filename.substring(0, dotIndex) + prefix_name + true_ext_name; - } - } - - /** - * receive package info - */ - public static class RecvPackageInfo { - public byte errno; - public byte[] body; - - public RecvPackageInfo(byte errno, byte[] body) { - this.errno = errno; - this.body = body; - } - } - - /** - * receive header info - */ - public static class RecvHeaderInfo { - public byte errno; - public long body_len; - - public RecvHeaderInfo(byte errno, long body_len) { - this.errno = errno; - this.body_len = body_len; - } - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/ProtoStructDecoder.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/ProtoStructDecoder.java deleted file mode 100644 index dc7b8d4..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/ProtoStructDecoder.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - -import java.io.IOException; -import java.lang.reflect.Array; - -/** - * C struct body decoder - * - * @author Happy Fish / YuQing - * @version Version 1.17 - */ -public class ProtoStructDecoder { - /** - * Constructor - */ - public ProtoStructDecoder() { - } - - /** - * decode byte buffer - */ - @SuppressWarnings("unchecked") - public T[] decode(byte[] bs, Class clazz, int fieldsTotalSize) throws Exception { - if (bs.length % fieldsTotalSize != 0) { - throw new IOException("byte array length: " + bs.length + " is invalid!"); - } - - int count = bs.length / fieldsTotalSize; - int offset; - T[] results = (T[]) Array.newInstance(clazz, count); - - offset = 0; - for (int i = 0; i < results.length; i++) { - results[i] = clazz.newInstance(); - results[i].setFields(bs, offset); - offset += fieldsTotalSize; - } - - return results; - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/ServerInfo.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/ServerInfo.java deleted file mode 100644 index 148db48..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/ServerInfo.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; - -/** - * Server Info - * - * @author Happy Fish / YuQing - * @version Version 1.7 - */ -public class ServerInfo { - protected String ip_addr; - protected int port; - - /** - * Constructor - * - * @param ip_addr address of the server - * @param port the port of the server - */ - public ServerInfo(String ip_addr, int port) { - this.ip_addr = ip_addr; - this.port = port; - } - - /** - * return the ip address - * - * @return the ip address - */ - public String getIpAddr() { - return this.ip_addr; - } - - /** - * return the port of the server - * - * @return the port of the server - */ - public int getPort() { - return this.port; - } - - /** - * connect to server - * - * @return connected Socket object - */ - public Socket connect() throws IOException { - Socket sock = new Socket(); - sock.setReuseAddress(true); - sock.setSoTimeout(ClientGlobal.g_network_timeout); - sock.connect(new InetSocketAddress(this.ip_addr, this.port), ClientGlobal.g_connect_timeout); - return sock; - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StorageClient.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StorageClient.java deleted file mode 100644 index 2406f6a..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StorageClient.java +++ /dev/null @@ -1,1807 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - -import csource.common.Base64; -import csource.common.MyException; -import csource.common.NameValuePair; -import csource.fastdfs.pool.Connection; - -import java.io.*; -import java.util.Arrays; - -/** - * Storage client for 2 fields file id: group name and filename - * Note: the instance of this class is NOT thread safe !!! - * if not necessary, do NOT set storage server instance - * - * @author Happy Fish / YuQing - * @version Version 1.27 - */ -public class StorageClient { - public final static Base64 base64 = new Base64('-', '_', '.', 0); - protected TrackerServer trackerServer; - protected StorageServer storageServer; - protected byte errno; - - /** - * constructor using global settings in class ClientGlobal - */ - public StorageClient() { - this.trackerServer = null; - this.storageServer = null; - } - - /** - * constructor with tracker server - * - * @param trackerServer the tracker server, can be null - */ - public StorageClient(TrackerServer trackerServer) { - this.trackerServer = trackerServer; - this.storageServer = null; - } - - /** - * constructor with tracker server and storage server - * NOTE: if not necessary, do NOT set storage server instance - * - * @param trackerServer the tracker server, can be null - * @param storageServer the storage server, can be null - */ - public StorageClient(TrackerServer trackerServer, StorageServer storageServer) { - this.trackerServer = trackerServer; - this.storageServer = storageServer; - } - - /** - * get the error code of last call - * - * @return the error code of last call - */ - public byte getErrorCode() { - return this.errno; - } - - /** - * upload file to storage server (by file name) - * - * @param local_filename local filename to upload - * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename - * @param meta_list meta info array - * @return 2 elements string array if success:
- *

  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_file(String local_filename, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - final String group_name = null; - return this.upload_file(group_name, local_filename, file_ext_name, meta_list); - } - - /** - * upload file to storage server (by file name) - * - * @param group_name the group name to upload file to, can be empty - * @param local_filename local filename to upload - * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - protected String[] upload_file(String group_name, String local_filename, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - final byte cmd = ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE; - return this.upload_file(cmd, group_name, local_filename, file_ext_name, meta_list); - } - - /** - * upload file to storage server (by file name) - * - * @param cmd the command - * @param group_name the group name to upload file to, can be empty - * @param local_filename local filename to upload - * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - protected String[] upload_file(byte cmd, String group_name, String local_filename, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - File f = new File(local_filename); - FileInputStream fis = new FileInputStream(f); - - if (file_ext_name == null) { - int nPos = local_filename.lastIndexOf('.'); - if (nPos > 0 && local_filename.length() - nPos <= ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) { - file_ext_name = local_filename.substring(nPos + 1); - } - } - - try { - return this.do_upload_file(cmd, group_name, null, null, file_ext_name, - f.length(), new UploadStream(fis, f.length()), meta_list); - } finally { - fis.close(); - } - } - - /** - * upload file to storage server (by file buff) - * - * @param file_buff file content/buff - * @param offset start offset of the buff - * @param length the length of buff to upload - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_file(byte[] file_buff, int offset, int length, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - final String group_name = null; - return this.upload_file(group_name, file_buff, offset, length, file_ext_name, meta_list); - } - - /** - * upload file to storage server (by file buff) - * - * @param group_name the group name to upload file to, can be empty - * @param file_buff file content/buff - * @param offset start offset of the buff - * @param length the length of buff to upload - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_file(String group_name, byte[] file_buff, int offset, int length, - String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException { - return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE, group_name, null, null, file_ext_name, - length, new UploadBuff(file_buff, offset, length), meta_list); - } - - /** - * upload file to storage server (by file buff) - * - * @param file_buff file content/buff - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_file(byte[] file_buff, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - final String group_name = null; - return this.upload_file(group_name, file_buff, 0, file_buff.length, file_ext_name, meta_list); - } - - /** - * upload file to storage server (by file buff) - * - * @param group_name the group name to upload file to, can be empty - * @param file_buff file content/buff - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_file(String group_name, byte[] file_buff, - String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException { - return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE, group_name, null, null, file_ext_name, - file_buff.length, new UploadBuff(file_buff, 0, file_buff.length), meta_list); - } - - /** - * upload file to storage server (by callback) - * - * @param group_name the group name to upload file to, can be empty - * @param file_size the file size - * @param callback the write data callback object - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_file(String group_name, long file_size, UploadCallback callback, - String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException { - final String master_filename = null; - final String prefix_name = null; - - return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE, group_name, master_filename, prefix_name, - file_ext_name, file_size, callback, meta_list); - } - - /** - * upload file to storage server (by file name, slave file mode) - * - * @param group_name the group name of master file - * @param master_filename the master file name to generate the slave file - * @param prefix_name the prefix name to generate the slave file - * @param local_filename local filename to upload - * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_file(String group_name, String master_filename, String prefix_name, - String local_filename, String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException { - if ((group_name == null || group_name.length() == 0) || - (master_filename == null || master_filename.length() == 0) || - (prefix_name == null)) { - throw new MyException("invalid arguement"); - } - - File f = new File(local_filename); - FileInputStream fis = new FileInputStream(f); - - if (file_ext_name == null) { - int nPos = local_filename.lastIndexOf('.'); - if (nPos > 0 && local_filename.length() - nPos <= ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) { - file_ext_name = local_filename.substring(nPos + 1); - } - } - - try { - return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name, - file_ext_name, f.length(), new UploadStream(fis, f.length()), meta_list); - } finally { - fis.close(); - } - } - - /** - * upload file to storage server (by file buff, slave file mode) - * - * @param group_name the group name of master file - * @param master_filename the master file name to generate the slave file - * @param prefix_name the prefix name to generate the slave file - * @param file_buff file content/buff - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_file(String group_name, String master_filename, String prefix_name, - byte[] file_buff, String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException { - if ((group_name == null || group_name.length() == 0) || - (master_filename == null || master_filename.length() == 0) || - (prefix_name == null)) { - throw new MyException("invalid arguement"); - } - - return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name, - file_ext_name, file_buff.length, new UploadBuff(file_buff, 0, file_buff.length), meta_list); - } - - /** - * upload file to storage server (by file buff, slave file mode) - * - * @param group_name the group name of master file - * @param master_filename the master file name to generate the slave file - * @param prefix_name the prefix name to generate the slave file - * @param file_buff file content/buff - * @param offset start offset of the buff - * @param length the length of buff to upload - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_file(String group_name, String master_filename, String prefix_name, - byte[] file_buff, int offset, int length, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - if ((group_name == null || group_name.length() == 0) || - (master_filename == null || master_filename.length() == 0) || - (prefix_name == null)) { - throw new MyException("invalid arguement"); - } - - return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name, - file_ext_name, length, new UploadBuff(file_buff, offset, length), meta_list); - } - - /** - * upload file to storage server (by callback, slave file mode) - * - * @param group_name the group name to upload file to, can be empty - * @param master_filename the master file name to generate the slave file - * @param prefix_name the prefix name to generate the slave file - * @param file_size the file size - * @param callback the write data callback object - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_file(String group_name, String master_filename, - String prefix_name, long file_size, UploadCallback callback, - String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException { - return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name, - file_ext_name, file_size, callback, meta_list); - } - - /** - * upload appender file to storage server (by file name) - * - * @param local_filename local filename to upload - * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_appender_file(String local_filename, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - final String group_name = null; - return this.upload_appender_file(group_name, local_filename, file_ext_name, meta_list); - } - - /** - * upload appender file to storage server (by file name) - * - * @param group_name the group name to upload file to, can be empty - * @param local_filename local filename to upload - * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - protected String[] upload_appender_file(String group_name, String local_filename, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - final byte cmd = ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE; - return this.upload_file(cmd, group_name, local_filename, file_ext_name, meta_list); - } - - /** - * upload appender file to storage server (by file buff) - * - * @param file_buff file content/buff - * @param offset start offset of the buff - * @param length the length of buff to upload - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_appender_file(byte[] file_buff, int offset, int length, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - final String group_name = null; - return this.upload_appender_file(group_name, file_buff, offset, length, file_ext_name, meta_list); - } - - /** - * upload appender file to storage server (by file buff) - * - * @param group_name the group name to upload file to, can be empty - * @param file_buff file content/buff - * @param offset start offset of the buff - * @param length the length of buff to upload - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_appender_file(String group_name, byte[] file_buff, int offset, int length, - String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException { - return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, group_name, null, null, file_ext_name, - length, new UploadBuff(file_buff, offset, length), meta_list); - } - - /** - * upload appender file to storage server (by file buff) - * - * @param file_buff file content/buff - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_appender_file(byte[] file_buff, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - final String group_name = null; - return this.upload_appender_file(group_name, file_buff, 0, file_buff.length, file_ext_name, meta_list); - } - - /** - * upload appender file to storage server (by file buff) - * - * @param group_name the group name to upload file to, can be empty - * @param file_buff file content/buff - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_appender_file(String group_name, byte[] file_buff, - String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException { - return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, group_name, null, null, file_ext_name, - file_buff.length, new UploadBuff(file_buff, 0, file_buff.length), meta_list); - } - - /** - * upload appender file to storage server (by callback) - * - * @param group_name the group name to upload file to, can be empty - * @param file_size the file size - * @param callback the write data callback object - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] upload_appender_file(String group_name, long file_size, UploadCallback callback, - String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException { - final String master_filename = null; - final String prefix_name = null; - - return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, group_name, master_filename, prefix_name, - file_ext_name, file_size, callback, meta_list); - } - - /** - * append file to storage server (by file name) - * - * @param group_name the group name of appender file - * @param appender_filename the appender filename - * @param local_filename local filename to append - * @return 0 for success, != 0 for error (error no) - */ - public int append_file(String group_name, String appender_filename, String local_filename) throws IOException, MyException { - File f = new File(local_filename); - FileInputStream fis = new FileInputStream(f); - - try { - return this.do_append_file(group_name, appender_filename, f.length(), new UploadStream(fis, f.length())); - } finally { - fis.close(); - } - } - - /** - * append file to storage server (by file buff) - * - * @param group_name the group name of appender file - * @param appender_filename the appender filename - * @param file_buff file content/buff - * @return 0 for success, != 0 for error (error no) - */ - public int append_file(String group_name, String appender_filename, byte[] file_buff) throws IOException, MyException { - return this.do_append_file(group_name, appender_filename, file_buff.length, new UploadBuff(file_buff, 0, file_buff.length)); - } - - /** - * append file to storage server (by file buff) - * - * @param group_name the group name of appender file - * @param appender_filename the appender filename - * @param file_buff file content/buff - * @param offset start offset of the buff - * @param length the length of buff to append - * @return 0 for success, != 0 for error (error no) - */ - public int append_file(String group_name, String appender_filename, - byte[] file_buff, int offset, int length) throws IOException, MyException { - return this.do_append_file(group_name, appender_filename, length, new UploadBuff(file_buff, offset, length)); - } - - /** - * append file to storage server (by callback) - * - * @param group_name the group name to append file to - * @param appender_filename the appender filename - * @param file_size the file size - * @param callback the write data callback object - * @return 0 for success, != 0 for error (error no) - */ - public int append_file(String group_name, String appender_filename, - long file_size, UploadCallback callback) throws IOException, MyException { - return this.do_append_file(group_name, appender_filename, file_size, callback); - } - - /** - * modify appender file to storage server (by file name) - * - * @param group_name the group name of appender file - * @param appender_filename the appender filename - * @param file_offset the offset of appender file - * @param local_filename local filename to append - * @return 0 for success, != 0 for error (error no) - */ - public int modify_file(String group_name, String appender_filename, - long file_offset, String local_filename) throws IOException, MyException { - File f = new File(local_filename); - FileInputStream fis = new FileInputStream(f); - - try { - return this.do_modify_file(group_name, appender_filename, file_offset, - f.length(), new UploadStream(fis, f.length())); - } finally { - fis.close(); - } - } - - /** - * modify appender file to storage server (by file buff) - * - * @param group_name the group name of appender file - * @param appender_filename the appender filename - * @param file_offset the offset of appender file - * @param file_buff file content/buff - * @return 0 for success, != 0 for error (error no) - */ - public int modify_file(String group_name, String appender_filename, - long file_offset, byte[] file_buff) throws IOException, MyException { - return this.do_modify_file(group_name, appender_filename, file_offset, - file_buff.length, new UploadBuff(file_buff, 0, file_buff.length)); - } - - /** - * modify appender file to storage server (by file buff) - * - * @param group_name the group name of appender file - * @param appender_filename the appender filename - * @param file_offset the offset of appender file - * @param file_buff file content/buff - * @param buffer_offset start offset of the buff - * @param buffer_length the length of buff to modify - * @return 0 for success, != 0 for error (error no) - */ - public int modify_file(String group_name, String appender_filename, - long file_offset, byte[] file_buff, int buffer_offset, int buffer_length) throws IOException, MyException { - return this.do_modify_file(group_name, appender_filename, file_offset, - buffer_length, new UploadBuff(file_buff, buffer_offset, buffer_length)); - } - - /** - * modify appender file to storage server (by callback) - * - * @param group_name the group name to modify file to - * @param appender_filename the appender filename - * @param file_offset the offset of appender file - * @param modify_size the modify size - * @param callback the write data callback object - * @return 0 for success, != 0 for error (error no) - */ - public int modify_file(String group_name, String appender_filename, - long file_offset, long modify_size, UploadCallback callback) throws IOException, MyException { - return this.do_modify_file(group_name, appender_filename, file_offset, - modify_size, callback); - } - - /** - * regenerate filename for appender file - * - * @param group_name the group name of appender file - * @param appender_filename the appender filename - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] regenerate_appender_filename(String group_name, String appender_filename) throws IOException, MyException { - byte[] header; - boolean bNewStorageServer; - Connection connection = null; - byte[] hexLenBytes; - byte[] appenderFilenameBytes; - int offset; - long body_len; - - if ((group_name == null || group_name.length() == 0) || - (appender_filename == null || appender_filename.length() == 0)) { - this.errno = ProtoCommon.ERR_NO_EINVAL; - return null; - } - - bNewStorageServer = this.newUpdatableStorageConnection(group_name, appender_filename); - - try { - connection = this.storageServer.getConnection(); - - appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset); - body_len = appenderFilenameBytes.length; - - header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_REGENERATE_APPENDER_FILENAME, body_len, (byte) 0); - byte[] wholePkg = new byte[(int) (header.length + body_len)]; - System.arraycopy(header, 0, wholePkg, 0, header.length); - offset = header.length; - - System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length); - offset += appenderFilenameBytes.length; - - OutputStream out = connection.getOutputStream(); - out.write(wholePkg); - - ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); - this.errno = pkgInfo.errno; - if (pkgInfo.errno != 0) { - return null; - } - - if (pkgInfo.body.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) { - throw new MyException("body length: " + pkgInfo.body.length + " <= " + ProtoCommon.FDFS_GROUP_NAME_MAX_LEN); - } - - String new_group_name = new String(pkgInfo.body, 0, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN).trim(); - String remote_filename = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, - pkgInfo.body.length - ProtoCommon.FDFS_GROUP_NAME_MAX_LEN); - String[] results = new String[2]; - results[0] = new_group_name; - results[1] = remote_filename; - - return results; - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } finally { - releaseConnection(connection, bNewStorageServer); - } - } - - /** - * upload file to storage server - * - * @param cmd the command code - * @param group_name the group name to upload file to, can be empty - * @param master_filename the master file name to generate the slave file - * @param prefix_name the prefix name to generate the slave file - * @param file_ext_name file ext name, do not include dot(.) - * @param file_size the file size - * @param callback the write data callback object - * @param meta_list meta info array - * @return 2 elements string array if success:
- *
  • results[0]: the group name to store the file
- *
  • results[1]: the new created filename
- * return null if fail - */ - public String[] do_upload_file(byte cmd, String group_name, String master_filename, - String prefix_name, String file_ext_name, long file_size, UploadCallback callback, - NameValuePair[] meta_list) throws IOException, MyException { - byte[] header; - byte[] ext_name_bs; - String new_group_name; - String remote_filename; - boolean bNewStorageServer; - Connection connection = null; - byte[] sizeBytes; - byte[] hexLenBytes; - byte[] masterFilenameBytes; - boolean bUploadSlave; - int offset; - long body_len; - - bUploadSlave = ((group_name != null && group_name.length() > 0) && - (master_filename != null && master_filename.length() > 0) && - (prefix_name != null)); - if (bUploadSlave) { - bNewStorageServer = this.newUpdatableStorageConnection(group_name, master_filename); - } else { - bNewStorageServer = this.newWritableStorageConnection(group_name); - } - - try { - connection = this.storageServer.getConnection(); - ext_name_bs = new byte[ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN]; - Arrays.fill(ext_name_bs, (byte) 0); - if (file_ext_name != null && file_ext_name.length() > 0) { - byte[] bs = file_ext_name.getBytes(ClientGlobal.g_charset); - int ext_name_len = bs.length; - if (ext_name_len > ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN) { - ext_name_len = ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN; - } - System.arraycopy(bs, 0, ext_name_bs, 0, ext_name_len); - } - - if (bUploadSlave) { - masterFilenameBytes = master_filename.getBytes(ClientGlobal.g_charset); - - sizeBytes = new byte[2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE]; - body_len = sizeBytes.length + ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN + ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN - + masterFilenameBytes.length + file_size; - - hexLenBytes = ProtoCommon.long2buff(master_filename.length()); - System.arraycopy(hexLenBytes, 0, sizeBytes, 0, hexLenBytes.length); - offset = hexLenBytes.length; - } else { - masterFilenameBytes = null; - sizeBytes = new byte[1 + 1 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE]; - body_len = sizeBytes.length + ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + file_size; - - sizeBytes[0] = (byte) this.storageServer.getStorePathIndex(); - offset = 1; - } - - hexLenBytes = ProtoCommon.long2buff(file_size); - System.arraycopy(hexLenBytes, 0, sizeBytes, offset, hexLenBytes.length); - - OutputStream out = connection.getOutputStream(); - header = ProtoCommon.packHeader(cmd, body_len, (byte) 0); - byte[] wholePkg = new byte[(int) (header.length + body_len - file_size)]; - System.arraycopy(header, 0, wholePkg, 0, header.length); - System.arraycopy(sizeBytes, 0, wholePkg, header.length, sizeBytes.length); - offset = header.length + sizeBytes.length; - if (bUploadSlave) { - byte[] prefix_name_bs = new byte[ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN]; - byte[] bs = prefix_name.getBytes(ClientGlobal.g_charset); - int prefix_name_len = bs.length; - Arrays.fill(prefix_name_bs, (byte) 0); - if (prefix_name_len > ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN) { - prefix_name_len = ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN; - } - if (prefix_name_len > 0) { - System.arraycopy(bs, 0, prefix_name_bs, 0, prefix_name_len); - } - - System.arraycopy(prefix_name_bs, 0, wholePkg, offset, prefix_name_bs.length); - offset += prefix_name_bs.length; - } - - System.arraycopy(ext_name_bs, 0, wholePkg, offset, ext_name_bs.length); - offset += ext_name_bs.length; - - if (bUploadSlave) { - System.arraycopy(masterFilenameBytes, 0, wholePkg, offset, masterFilenameBytes.length); - offset += masterFilenameBytes.length; - } - - out.write(wholePkg); - - if ((this.errno = (byte) callback.send(out)) != 0) { - return null; - } - - ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); - this.errno = pkgInfo.errno; - if (pkgInfo.errno != 0) { - return null; - } - - if (pkgInfo.body.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) { - throw new MyException("body length: " + pkgInfo.body.length + " <= " + ProtoCommon.FDFS_GROUP_NAME_MAX_LEN); - } - - new_group_name = new String(pkgInfo.body, 0, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN).trim(); - remote_filename = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, pkgInfo.body.length - ProtoCommon.FDFS_GROUP_NAME_MAX_LEN); - String[] results = new String[2]; - results[0] = new_group_name; - results[1] = remote_filename; - - if (meta_list == null || meta_list.length == 0) { - return results; - } - - int result = 0; - try { - result = this.set_metadata(new_group_name, remote_filename, - meta_list, ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE); - } catch (IOException ex) { - result = 5; - throw ex; - } finally { - if (result != 0) { - this.errno = (byte) result; - this.delete_file(new_group_name, remote_filename); - return null; - } - } - return results; - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } finally { - releaseConnection(connection, bNewStorageServer); - - } - } - - /** - * append file to storage server - * - * @param group_name the group name of appender file - * @param appender_filename the appender filename - * @param file_size the file size - * @param callback the write data callback object - * @return return true for success, false for fail - */ - protected int do_append_file(String group_name, String appender_filename, - long file_size, UploadCallback callback) throws IOException, MyException { - byte[] header; - boolean bNewStorageServer; - Connection connection = null; - byte[] hexLenBytes; - byte[] appenderFilenameBytes; - int offset; - long body_len; - - if ((group_name == null || group_name.length() == 0) || - (appender_filename == null || appender_filename.length() == 0)) { - this.errno = ProtoCommon.ERR_NO_EINVAL; - return this.errno; - } - - bNewStorageServer = this.newUpdatableStorageConnection(group_name, appender_filename); - - try { - connection = this.storageServer.getConnection(); - - appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset); - body_len = 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + appenderFilenameBytes.length + file_size; - - header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_APPEND_FILE, body_len, (byte) 0); - byte[] wholePkg = new byte[(int) (header.length + body_len - file_size)]; - System.arraycopy(header, 0, wholePkg, 0, header.length); - offset = header.length; - - hexLenBytes = ProtoCommon.long2buff(appender_filename.length()); - System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); - offset += hexLenBytes.length; - - hexLenBytes = ProtoCommon.long2buff(file_size); - System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); - offset += hexLenBytes.length; - - OutputStream out = connection.getOutputStream(); - - System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length); - offset += appenderFilenameBytes.length; - - out.write(wholePkg); - if ((this.errno = (byte) callback.send(out)) != 0) { - return this.errno; - } - - ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); - this.errno = pkgInfo.errno; - if (pkgInfo.errno != 0) { - return this.errno; - } - - return 0; - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } finally { - releaseConnection(connection, bNewStorageServer); - } - } - - private void releaseConnection(Connection connection, boolean bNewStorageServer) { - try { - if (connection != null) { - connection.release(); - } - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - if (bNewStorageServer) { - this.storageServer = null; - } - } - } - - /** - * modify appender file to storage server - * - * @param group_name the group name of appender file - * @param appender_filename the appender filename - * @param file_offset the offset of appender file - * @param modify_size the modify size - * @param callback the write data callback object - * @return return true for success, false for fail - */ - protected int do_modify_file(String group_name, String appender_filename, - long file_offset, long modify_size, UploadCallback callback) throws IOException, MyException { - byte[] header; - boolean bNewStorageServer; - Connection connection = null; - byte[] hexLenBytes; - byte[] appenderFilenameBytes; - int offset; - long body_len; - - if ((group_name == null || group_name.length() == 0) || - (appender_filename == null || appender_filename.length() == 0)) { - this.errno = ProtoCommon.ERR_NO_EINVAL; - return this.errno; - } - - bNewStorageServer = this.newUpdatableStorageConnection(group_name, appender_filename); - - try { - connection = this.storageServer.getConnection(); - - appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset); - body_len = 3 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + appenderFilenameBytes.length + modify_size; - - header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_MODIFY_FILE, body_len, (byte) 0); - byte[] wholePkg = new byte[(int) (header.length + body_len - modify_size)]; - System.arraycopy(header, 0, wholePkg, 0, header.length); - offset = header.length; - - hexLenBytes = ProtoCommon.long2buff(appender_filename.length()); - System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); - offset += hexLenBytes.length; - - hexLenBytes = ProtoCommon.long2buff(file_offset); - System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); - offset += hexLenBytes.length; - - hexLenBytes = ProtoCommon.long2buff(modify_size); - System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); - offset += hexLenBytes.length; - - OutputStream out = connection.getOutputStream(); - - System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length); - offset += appenderFilenameBytes.length; - - out.write(wholePkg); - if ((this.errno = (byte) callback.send(out)) != 0) { - return this.errno; - } - - ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); - this.errno = pkgInfo.errno; - if (pkgInfo.errno != 0) { - return this.errno; - } - - return 0; - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - - throw ex; - } finally { - releaseConnection(connection, bNewStorageServer); - } - } - - /** - * delete file from storage server - * - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - * @return 0 for success, none zero for fail (error code) - */ - public int delete_file(String group_name, String remote_filename) throws IOException, MyException { - boolean bNewStorageServer = this.newUpdatableStorageConnection(group_name, remote_filename); - Connection connection = this.storageServer.getConnection(); - - try { - this.send_package(ProtoCommon.STORAGE_PROTO_CMD_DELETE_FILE, group_name, remote_filename, connection); - ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); - - this.errno = pkgInfo.errno; - return pkgInfo.errno; - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } finally { - releaseConnection(connection, bNewStorageServer); - } - } - - /** - * truncate appender file to size 0 from storage server - * - * @param group_name the group name of storage server - * @param appender_filename the appender filename - * @return 0 for success, none zero for fail (error code) - */ - public int truncate_file(String group_name, String appender_filename) throws IOException, MyException { - final long truncated_file_size = 0; - return this.truncate_file(group_name, appender_filename, truncated_file_size); - } - - /** - * truncate appender file from storage server - * - * @param group_name the group name of storage server - * @param appender_filename the appender filename - * @param truncated_file_size truncated file size - * @return 0 for success, none zero for fail (error code) - */ - public int truncate_file(String group_name, String appender_filename, - long truncated_file_size) throws IOException, MyException { - byte[] header; - boolean bNewStorageServer; - Connection connection = null; - byte[] hexLenBytes; - byte[] appenderFilenameBytes; - int offset; - int body_len; - - if ((group_name == null || group_name.length() == 0) || - (appender_filename == null || appender_filename.length() == 0)) { - this.errno = ProtoCommon.ERR_NO_EINVAL; - return this.errno; - } - - bNewStorageServer = this.newUpdatableStorageConnection(group_name, appender_filename); - - try { - connection = this.storageServer.getConnection(); - - appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset); - body_len = 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + appenderFilenameBytes.length; - - header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_TRUNCATE_FILE, body_len, (byte) 0); - byte[] wholePkg = new byte[header.length + body_len]; - System.arraycopy(header, 0, wholePkg, 0, header.length); - offset = header.length; - - hexLenBytes = ProtoCommon.long2buff(appender_filename.length()); - System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); - offset += hexLenBytes.length; - - hexLenBytes = ProtoCommon.long2buff(truncated_file_size); - System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); - offset += hexLenBytes.length; - - OutputStream out = connection.getOutputStream(); - - System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length); - offset += appenderFilenameBytes.length; - - out.write(wholePkg); - ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); - this.errno = pkgInfo.errno; - return pkgInfo.errno; - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } finally { - releaseConnection(connection, bNewStorageServer); - } - } - - /** - * download file from storage server - * - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - * @return file content/buff, return null if fail - */ - public byte[] download_file(String group_name, String remote_filename) throws IOException, MyException { - final long file_offset = 0; - final long download_bytes = 0; - - return this.download_file(group_name, remote_filename, file_offset, download_bytes); - } - - /** - * download file from storage server - * - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - * @param file_offset the start offset of the file - * @param download_bytes download bytes, 0 for remain bytes from offset - * @return file content/buff, return null if fail - */ - public byte[] download_file(String group_name, String remote_filename, long file_offset, long download_bytes) throws IOException, MyException { - boolean bNewStorageServer = this.newReadableStorageConnection(group_name, remote_filename); - Connection connection = this.storageServer.getConnection(); - - try { - ProtoCommon.RecvPackageInfo pkgInfo; - - this.send_download_package(group_name, remote_filename, file_offset, download_bytes, connection); - pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); - - this.errno = pkgInfo.errno; - if (pkgInfo.errno != 0) { - return null; - } - - return pkgInfo.body; - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } finally { - releaseConnection(connection, bNewStorageServer); - } - } - - /** - * download file from storage server - * - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - * @param local_filename filename on local - * @return 0 success, return none zero errno if fail - */ - public int download_file(String group_name, String remote_filename, - String local_filename) throws IOException, MyException { - final long file_offset = 0; - final long download_bytes = 0; - return this.download_file(group_name, remote_filename, - file_offset, download_bytes, local_filename); - } - - /** - * download file from storage server - * - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - * @param file_offset the start offset of the file - * @param download_bytes download bytes, 0 for remain bytes from offset - * @param local_filename filename on local - * @return 0 success, return none zero errno if fail - */ - public int download_file(String group_name, String remote_filename, - long file_offset, long download_bytes, - String local_filename) throws IOException, MyException { - boolean bNewStorageServer = this.newReadableStorageConnection(group_name, remote_filename); - Connection connection = this.storageServer.getConnection(); - try { - ProtoCommon.RecvHeaderInfo header; - FileOutputStream out = new FileOutputStream(local_filename); - try { - this.errno = 0; - this.send_download_package(group_name, remote_filename, file_offset, download_bytes, connection); - - InputStream in = connection.getInputStream(); - header = ProtoCommon.recvHeader(in, ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); - this.errno = header.errno; - if (header.errno != 0) { - return header.errno; - } - - byte[] buff = new byte[256 * 1024]; - long remainBytes = header.body_len; - int bytes; - - //System.out.println("expect_body_len=" + header.body_len); - - while (remainBytes > 0) { - if ((bytes = in.read(buff, 0, remainBytes > buff.length ? buff.length : (int) remainBytes)) < 0) { - throw new IOException("recv package size " + (header.body_len - remainBytes) + " != " + header.body_len); - } - - out.write(buff, 0, bytes); - remainBytes -= bytes; - - //System.out.println("totalBytes=" + (header.body_len - remainBytes)); - } - - return 0; - } catch (IOException ex) { - if (this.errno == 0) { - this.errno = ProtoCommon.ERR_NO_EIO; - } - - throw ex; - } finally { - out.close(); - if (this.errno != 0) { - (new File(local_filename)).delete(); - } - } - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } finally { - releaseConnection(connection, bNewStorageServer); - } - } - - /** - * download file from storage server - * - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - * @param callback call callback.recv() when data arrive - * @return 0 success, return none zero errno if fail - */ - public int download_file(String group_name, String remote_filename, - DownloadCallback callback) throws IOException, MyException { - final long file_offset = 0; - final long download_bytes = 0; - return this.download_file(group_name, remote_filename, - file_offset, download_bytes, callback); - } - - /** - * download file from storage server - * - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - * @param file_offset the start offset of the file - * @param download_bytes download bytes, 0 for remain bytes from offset - * @param callback call callback.recv() when data arrive - * @return 0 success, return none zero errno if fail - */ - public int download_file(String group_name, String remote_filename, - long file_offset, long download_bytes, - DownloadCallback callback) throws IOException, MyException { - int result; - boolean bNewStorageServer = this.newReadableStorageConnection(group_name, remote_filename); - Connection connection = this.storageServer.getConnection(); - - try { - ProtoCommon.RecvHeaderInfo header; - this.send_download_package(group_name, remote_filename, file_offset, download_bytes, connection); - - InputStream in = connection.getInputStream(); - header = ProtoCommon.recvHeader(in, ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); - this.errno = header.errno; - if (header.errno != 0) { - return header.errno; - } - - byte[] buff = new byte[2 * 1024]; - long remainBytes = header.body_len; - int bytes; - - //System.out.println("expect_body_len=" + header.body_len); - - while (remainBytes > 0) { - if ((bytes = in.read(buff, 0, remainBytes > buff.length ? buff.length : (int) remainBytes)) < 0) { - throw new IOException("recv package size " + (header.body_len - remainBytes) + " != " + header.body_len); - } - - if ((result = callback.recv(header.body_len, buff, bytes)) != 0) { - this.errno = (byte) result; - return result; - } - - remainBytes -= bytes; - //System.out.println("totalBytes=" + (header.body_len - remainBytes)); - } - - return 0; - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } finally { - releaseConnection(connection, bNewStorageServer); - } - } - - /** - * get all metadata items from storage server - * - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - * @return meta info array, return null if fail - */ - public NameValuePair[] get_metadata(String group_name, String remote_filename) throws IOException, MyException { - boolean bNewStorageServer = this.newUpdatableStorageConnection(group_name, remote_filename); - Connection connection = this.storageServer.getConnection(); - try { - ProtoCommon.RecvPackageInfo pkgInfo; - - this.send_package(ProtoCommon.STORAGE_PROTO_CMD_GET_METADATA, group_name, remote_filename, connection); - pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); - - this.errno = pkgInfo.errno; - if (pkgInfo.errno != 0) { - return null; - } - - return ProtoCommon.split_metadata(new String(pkgInfo.body, ClientGlobal.g_charset)); - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } finally { - releaseConnection(connection, bNewStorageServer); - } - } - - /** - * set metadata items to storage server - * - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - * @param meta_list meta item array - * @param op_flag flag, can be one of following values:
- *
  • ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE: overwrite all old - * metadata items
- *
  • ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE: merge, insert when - * the metadata item not exist, otherwise update it
- * @return 0 for success, !=0 fail (error code) - */ - public int set_metadata(String group_name, String remote_filename, - NameValuePair[] meta_list, byte op_flag) throws IOException, MyException { - boolean bNewStorageServer = this.newUpdatableStorageConnection(group_name, remote_filename); - Connection connection = this.storageServer.getConnection(); - try { - byte[] header; - byte[] groupBytes; - byte[] filenameBytes; - byte[] meta_buff; - byte[] bs; - int groupLen; - byte[] sizeBytes; - ProtoCommon.RecvPackageInfo pkgInfo; - - if (meta_list == null) { - meta_buff = new byte[0]; - } else { - meta_buff = ProtoCommon.pack_metadata(meta_list).getBytes(ClientGlobal.g_charset); - } - - filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset); - sizeBytes = new byte[2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE]; - Arrays.fill(sizeBytes, (byte) 0); - - bs = ProtoCommon.long2buff(filenameBytes.length); - System.arraycopy(bs, 0, sizeBytes, 0, bs.length); - bs = ProtoCommon.long2buff(meta_buff.length); - System.arraycopy(bs, 0, sizeBytes, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE, bs.length); - - groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; - bs = group_name.getBytes(ClientGlobal.g_charset); - - Arrays.fill(groupBytes, (byte) 0); - if (bs.length <= groupBytes.length) { - groupLen = bs.length; - } else { - groupLen = groupBytes.length; - } - System.arraycopy(bs, 0, groupBytes, 0, groupLen); - - header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_SET_METADATA, - 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + 1 + groupBytes.length - + filenameBytes.length + meta_buff.length, (byte) 0); - OutputStream out = connection.getOutputStream(); - byte[] wholePkg = new byte[header.length + sizeBytes.length + 1 + groupBytes.length + filenameBytes.length]; - System.arraycopy(header, 0, wholePkg, 0, header.length); - System.arraycopy(sizeBytes, 0, wholePkg, header.length, sizeBytes.length); - wholePkg[header.length + sizeBytes.length] = op_flag; - System.arraycopy(groupBytes, 0, wholePkg, header.length + sizeBytes.length + 1, groupBytes.length); - System.arraycopy(filenameBytes, 0, wholePkg, header.length + sizeBytes.length + 1 + groupBytes.length, filenameBytes.length); - out.write(wholePkg); - if (meta_buff.length > 0) { - out.write(meta_buff); - } - - pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); - - this.errno = pkgInfo.errno; - return pkgInfo.errno; - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } finally { - releaseConnection(connection, bNewStorageServer); - } - } - - /** - * get file info decoded from the filename, fetch from the storage if necessary - * - * @param group_name the group name - * @param remote_filename the filename - * @return FileInfo object for success, return null for fail - */ - public FileInfo get_file_info(String group_name, String remote_filename) throws IOException, MyException { - if (remote_filename.length() < ProtoCommon.FDFS_FILE_PATH_LEN + ProtoCommon.FDFS_FILENAME_BASE64_LENGTH - + ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) { - this.errno = ProtoCommon.ERR_NO_EINVAL; - return null; - } - - byte[] buff = base64.decodeAuto(remote_filename.substring(ProtoCommon.FDFS_FILE_PATH_LEN, - ProtoCommon.FDFS_FILE_PATH_LEN + ProtoCommon.FDFS_FILENAME_BASE64_LENGTH)); - - short file_type; - long file_size = ProtoCommon.buff2long(buff, 4 * 2); - if (((file_size & ProtoCommon.APPENDER_FILE_SIZE) != 0)) { - file_type = FileInfo.FILE_TYPE_APPENDER; - } else if ((remote_filename.length() > ProtoCommon.TRUNK_LOGIC_FILENAME_LENGTH) || - ((remote_filename.length() > ProtoCommon.NORMAL_LOGIC_FILENAME_LENGTH) && - ((file_size & ProtoCommon.TRUNK_FILE_MARK_SIZE) == 0))) { - file_type = FileInfo.FILE_TYPE_SLAVE; - } else { - file_type = FileInfo.FILE_TYPE_NORMAL; - } - - if (file_type == FileInfo.FILE_TYPE_SLAVE || - file_type == FileInfo.FILE_TYPE_APPENDER) { //slave file or appender file - FileInfo fi = this.query_file_info(group_name, remote_filename); - if (fi == null) { - return null; - } - - fi.setFileType(file_type); - return fi; - } - - int create_timestamp = ProtoCommon.buff2int(buff, 4); - if ((file_size >> 63) != 0) { - file_size &= 0xFFFFFFFFL; //low 32 bits is file size - } - int crc32 = ProtoCommon.buff2int(buff, 4 * 4); - - return new FileInfo(false, file_type, file_size, create_timestamp, - crc32, ProtoCommon.getIpAddress(buff, 0)); - } - - /** - * get file info from storage server - * - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - * @return FileInfo object for success, return null for fail - */ - public FileInfo query_file_info(String group_name, String remote_filename) throws IOException, MyException { - boolean bNewStorageServer = this.newUpdatableStorageConnection(group_name, remote_filename); - Connection connection = this.storageServer.getConnection(); - try { - byte[] header; - byte[] groupBytes; - byte[] filenameBytes; - byte[] bs; - int groupLen; - ProtoCommon.RecvPackageInfo pkgInfo; - - filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset); - groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; - bs = group_name.getBytes(ClientGlobal.g_charset); - - Arrays.fill(groupBytes, (byte) 0); - if (bs.length <= groupBytes.length) { - groupLen = bs.length; - } else { - groupLen = groupBytes.length; - } - System.arraycopy(bs, 0, groupBytes, 0, groupLen); - - header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_QUERY_FILE_INFO, - +groupBytes.length + filenameBytes.length, (byte) 0); - OutputStream out = connection.getOutputStream(); - byte[] wholePkg = new byte[header.length + groupBytes.length + filenameBytes.length]; - System.arraycopy(header, 0, wholePkg, 0, header.length); - System.arraycopy(groupBytes, 0, wholePkg, header.length, groupBytes.length); - System.arraycopy(filenameBytes, 0, wholePkg, header.length + groupBytes.length, filenameBytes.length); - out.write(wholePkg); - - pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.STORAGE_PROTO_CMD_RESP, - 3 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + - ProtoCommon.FDFS_IPADDR_SIZE); - - this.errno = pkgInfo.errno; - if (pkgInfo.errno != 0) { - return null; - } - - long file_size = ProtoCommon.buff2long(pkgInfo.body, 0); - int create_timestamp = (int) ProtoCommon.buff2long(pkgInfo.body, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - int crc32 = (int) ProtoCommon.buff2long(pkgInfo.body, 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - String source_ip_addr = (new String(pkgInfo.body, 3 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE, ProtoCommon.FDFS_IPADDR_SIZE)).trim(); - return new FileInfo(true, FileInfo.FILE_TYPE_NORMAL, file_size, - create_timestamp, crc32, source_ip_addr); - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } finally { - releaseConnection(connection, bNewStorageServer); - } - } - - /** - * check storage socket, if null create a new connection - * - * @param group_name the group name to upload file to, can be empty - * @return true if create a new connection - */ - protected boolean newWritableStorageConnection(String group_name) throws IOException, MyException { - if (this.storageServer != null) { - return false; - } else { - TrackerClient tracker = new TrackerClient(); - this.storageServer = tracker.getStoreStorage(this.trackerServer, group_name); - if (this.storageServer == null) { - throw new MyException("getStoreStorage fail, errno code: " + tracker.getErrorCode()); - } - return true; - } - } - - /** - * check storage socket, if null create a new connection - * - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - * @return true if create a new connection - */ - protected boolean newReadableStorageConnection(String group_name, String remote_filename) throws IOException, MyException { - if (this.storageServer != null) { - return false; - } else { - TrackerClient tracker = new TrackerClient(); - this.storageServer = tracker.getFetchStorage(this.trackerServer, group_name, remote_filename); - if (this.storageServer == null) { - throw new MyException("getStoreStorage fail, errno code: " + tracker.getErrorCode()); - } - return true; - } - } - - /** - * check storage socket, if null create a new connection - * - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - * @return true if create a new connection - */ - protected boolean newUpdatableStorageConnection(String group_name, String remote_filename) throws IOException, MyException { - if (this.storageServer != null) { - return false; - } else { - TrackerClient tracker = new TrackerClient(); - this.storageServer = tracker.getUpdateStorage(this.trackerServer, group_name, remote_filename); - if (this.storageServer == null) { - throw new MyException("getStoreStorage fail, errno code: " + tracker.getErrorCode()); - } - return true; - } - } - - /** - * send package to storage server - * - * @param cmd which command to send - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - */ - protected void send_package(byte cmd, String group_name, String remote_filename, Connection connection) throws IOException { - byte[] header; - byte[] groupBytes; - byte[] filenameBytes; - byte[] bs; - int groupLen; - - groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; - bs = group_name.getBytes(ClientGlobal.g_charset); - filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset); - - Arrays.fill(groupBytes, (byte) 0); - if (bs.length <= groupBytes.length) { - groupLen = bs.length; - } else { - groupLen = groupBytes.length; - } - System.arraycopy(bs, 0, groupBytes, 0, groupLen); - - header = ProtoCommon.packHeader(cmd, groupBytes.length + filenameBytes.length, (byte) 0); - byte[] wholePkg = new byte[header.length + groupBytes.length + filenameBytes.length]; - System.arraycopy(header, 0, wholePkg, 0, header.length); - System.arraycopy(groupBytes, 0, wholePkg, header.length, groupBytes.length); - System.arraycopy(filenameBytes, 0, wholePkg, header.length + groupBytes.length, filenameBytes.length); - connection.getOutputStream().write(wholePkg); - } - - /** - * send package to storage server - * - * @param group_name the group name of storage server - * @param remote_filename filename on storage server - * @param file_offset the start offset of the file - * @param download_bytes download bytes - */ - protected void send_download_package(String group_name, String remote_filename, long file_offset, long download_bytes, Connection connection) throws IOException { - byte[] header; - byte[] bsOffset; - byte[] bsDownBytes; - byte[] groupBytes; - byte[] filenameBytes; - byte[] bs; - int groupLen; - - bsOffset = ProtoCommon.long2buff(file_offset); - bsDownBytes = ProtoCommon.long2buff(download_bytes); - groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; - bs = group_name.getBytes(ClientGlobal.g_charset); - filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset); - - Arrays.fill(groupBytes, (byte) 0); - if (bs.length <= groupBytes.length) { - groupLen = bs.length; - } else { - groupLen = groupBytes.length; - } - System.arraycopy(bs, 0, groupBytes, 0, groupLen); - - header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_DOWNLOAD_FILE, - bsOffset.length + bsDownBytes.length + groupBytes.length + filenameBytes.length, (byte) 0); - byte[] wholePkg = new byte[header.length + bsOffset.length + bsDownBytes.length + groupBytes.length + filenameBytes.length]; - System.arraycopy(header, 0, wholePkg, 0, header.length); - System.arraycopy(bsOffset, 0, wholePkg, header.length, bsOffset.length); - System.arraycopy(bsDownBytes, 0, wholePkg, header.length + bsOffset.length, bsDownBytes.length); - System.arraycopy(groupBytes, 0, wholePkg, header.length + bsOffset.length + bsDownBytes.length, groupBytes.length); - System.arraycopy(filenameBytes, 0, wholePkg, header.length + bsOffset.length + bsDownBytes.length + groupBytes.length, filenameBytes.length); - connection.getOutputStream().write(wholePkg); - } - - public boolean isConnected() { - return trackerServer != null; - } - - public boolean isAvaliable() { - return trackerServer != null; - } - - public void close() throws IOException { - trackerServer = null; - } - - public TrackerServer getTrackerServer() { - return trackerServer; - } - - public void setTrackerServer(TrackerServer trackerServer) { - this.trackerServer = trackerServer; - } - - public StorageServer getStorageServer() { - return storageServer; - } - - public void setStorageServer(StorageServer storageServer) { - this.storageServer = storageServer; - } - - /** - * Upload file by file buff - * - * @author Happy Fish / YuQing - * @version Version 1.12 - */ - public static class UploadBuff implements UploadCallback { - private byte[] fileBuff; - private int offset; - private int length; - - /** - * constructor - * - * @param fileBuff the file buff for uploading - */ - public UploadBuff(byte[] fileBuff, int offset, int length) { - super(); - this.fileBuff = fileBuff; - this.offset = offset; - this.length = length; - } - - /** - * send file content callback function, be called only once when the file uploaded - * - * @param out output stream for writing file content - * @return 0 success, return none zero(errno) if fail - */ - @Override - public int send(OutputStream out) throws IOException { - out.write(this.fileBuff, this.offset, this.length); - - return 0; - } - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StorageClient1.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StorageClient1.java deleted file mode 100644 index 8364b8f..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StorageClient1.java +++ /dev/null @@ -1,766 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - - -import csource.common.MyException; -import csource.common.NameValuePair; - -import java.io.IOException; - -/** - * Storage client for 1 field file id: combined group name and filename - * Note: the instance of this class is NOT thread safe !!! - * if not necessary, do NOT set storage server instance - * - * @author Happy Fish / YuQing - * @version Version 1.27 - */ -public class StorageClient1 extends StorageClient { - public static final String SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR = "/"; - - /** - * constructor - */ - public StorageClient1() { - super(); - } - - /** - * constructor with trackerServer - * - * @param trackerServer the tracker server, can be null - */ - public StorageClient1(TrackerServer trackerServer) { - super(trackerServer); - } - - /** - * constructor with trackerServer and storageServer - * NOTE: if not necessary, do NOT set storage server instance - * - * @param trackerServer the tracker server, can be null - * @param storageServer the storage server, can be null - */ - public StorageClient1(TrackerServer trackerServer, StorageServer storageServer) { - super(trackerServer, storageServer); - } - - public static byte split_file_id(String file_id, String[] results) { - int pos = file_id.indexOf(SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR); - if ((pos <= 0) || (pos == file_id.length() - 1)) { - return ProtoCommon.ERR_NO_EINVAL; - } - - results[0] = file_id.substring(0, pos); //group name - results[1] = file_id.substring(pos + 1); //file name - return 0; - } - - /** - * upload file to storage server (by file name) - * - * @param local_filename local filename to upload - * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_file1(String local_filename, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - String parts[] = this.upload_file(local_filename, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * upload file to storage server (by file name) - * - * @param group_name the group name to upload file to, can be empty - * @param local_filename local filename to upload - * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_file1(String group_name, String local_filename, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - String parts[] = this.upload_file(group_name, local_filename, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * upload file to storage server (by file buff) - * - * @param file_buff file content/buff - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_file1(byte[] file_buff, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - String parts[] = this.upload_file(file_buff, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * upload file to storage server (by file buff) - * - * @param group_name the group name to upload file to, can be empty - * @param file_buff file content/buff - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_file1(String group_name, byte[] file_buff, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - String parts[] = this.upload_file(group_name, file_buff, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * upload file to storage server (by callback) - * - * @param group_name the group name to upload file to, can be empty - * @param file_size the file size - * @param callback the write data callback object - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_file1(String group_name, long file_size, - UploadCallback callback, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - String parts[] = this.upload_file(group_name, file_size, callback, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * upload appender file to storage server (by file name) - * - * @param local_filename local filename to upload - * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_appender_file1(String local_filename, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - String parts[] = this.upload_appender_file(local_filename, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * upload appender file to storage server (by file name) - * - * @param group_name the group name to upload file to, can be empty - * @param local_filename local filename to upload - * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_appender_file1(String group_name, String local_filename, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - String parts[] = this.upload_appender_file(group_name, local_filename, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * upload appender file to storage server (by file buff) - * - * @param file_buff file content/buff - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_appender_file1(byte[] file_buff, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - String parts[] = this.upload_appender_file(file_buff, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * upload appender file to storage server (by file buff) - * - * @param group_name the group name to upload file to, can be empty - * @param file_buff file content/buff - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_appender_file1(String group_name, byte[] file_buff, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - String parts[] = this.upload_appender_file(group_name, file_buff, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * upload appender file to storage server (by callback) - * - * @param group_name the group name to upload file to, can be empty - * @param file_size the file size - * @param callback the write data callback object - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_appender_file1(String group_name, long file_size, - UploadCallback callback, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - String parts[] = this.upload_appender_file(group_name, file_size, callback, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * upload file to storage server (by file name, slave file mode) - * - * @param master_file_id the master file id to generate the slave file - * @param prefix_name the prefix name to generate the slave file - * @param local_filename local filename to upload - * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_file1(String master_file_id, String prefix_name, - String local_filename, String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(master_file_id, parts); - if (this.errno != 0) { - return null; - } - - parts = this.upload_file(parts[0], parts[1], prefix_name, - local_filename, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * upload file to storage server (by file buff, slave file mode) - * - * @param master_file_id the master file id to generate the slave file - * @param prefix_name the prefix name to generate the slave file - * @param file_buff file content/buff - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_file1(String master_file_id, String prefix_name, - byte[] file_buff, String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(master_file_id, parts); - if (this.errno != 0) { - return null; - } - - parts = this.upload_file(parts[0], parts[1], prefix_name, file_buff, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * upload file to storage server (by file buff, slave file mode) - * - * @param master_file_id the master file id to generate the slave file - * @param prefix_name the prefix name to generate the slave file - * @param file_buff file content/buff - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_file1(String master_file_id, String prefix_name, - byte[] file_buff, int offset, int length, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(master_file_id, parts); - if (this.errno != 0) { - return null; - } - - parts = this.upload_file(parts[0], parts[1], prefix_name, file_buff, - offset, length, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * upload file to storage server (by callback) - * - * @param master_file_id the master file id to generate the slave file - * @param prefix_name the prefix name to generate the slave file - * @param file_size the file size - * @param callback the write data callback object - * @param file_ext_name file ext name, do not include dot(.) - * @param meta_list meta info array - * @return file id(including group name and filename) if success,
- * return null if fail - */ - public String upload_file1(String master_file_id, String prefix_name, long file_size, - UploadCallback callback, String file_ext_name, - NameValuePair[] meta_list) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(master_file_id, parts); - if (this.errno != 0) { - return null; - } - - parts = this.upload_file(parts[0], parts[1], prefix_name, file_size, callback, file_ext_name, meta_list); - if (parts != null) { - return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; - } else { - return null; - } - } - - /** - * append file to storage server (by file name) - * - * @param appender_file_id the appender file id - * @param local_filename local filename to append - * @return 0 for success, != 0 for error (error no) - */ - public int append_file1(String appender_file_id, String local_filename) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(appender_file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.append_file(parts[0], parts[1], local_filename); - } - - /** - * append file to storage server (by file buff) - * - * @param appender_file_id the appender file id - * @param file_buff file content/buff - * @return 0 for success, != 0 for error (error no) - */ - public int append_file1(String appender_file_id, byte[] file_buff) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(appender_file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.append_file(parts[0], parts[1], file_buff); - } - - /** - * append file to storage server (by file buff) - * - * @param appender_file_id the appender file id - * @param file_buff file content/buffer - * @param offset start offset of the buffer - * @param length the length of the buffer to append - * @return 0 for success, != 0 for error (error no) - */ - public int append_file1(String appender_file_id, byte[] file_buff, int offset, int length) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(appender_file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.append_file(parts[0], parts[1], file_buff, offset, length); - } - - /** - * append file to storage server (by callback) - * - * @param appender_file_id the appender file id - * @param file_size the file size - * @param callback the write data callback object - * @return 0 for success, != 0 for error (error no) - */ - public int append_file1(String appender_file_id, long file_size, UploadCallback callback) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(appender_file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.append_file(parts[0], parts[1], file_size, callback); - } - - /** - * modify appender file to storage server (by file name) - * - * @param appender_file_id the appender file id - * @param file_offset the offset of appender file - * @param local_filename local filename to append - * @return 0 for success, != 0 for error (error no) - */ - public int modify_file1(String appender_file_id, - long file_offset, String local_filename) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(appender_file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.modify_file(parts[0], parts[1], file_offset, local_filename); - } - - /** - * modify appender file to storage server (by file buff) - * - * @param appender_file_id the appender file id - * @param file_offset the offset of appender file - * @param file_buff file content/buff - * @return 0 for success, != 0 for error (error no) - */ - public int modify_file1(String appender_file_id, - long file_offset, byte[] file_buff) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(appender_file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.modify_file(parts[0], parts[1], file_offset, file_buff); - } - - /** - * modify appender file to storage server (by file buff) - * - * @param appender_file_id the appender file id - * @param file_offset the offset of appender file - * @param file_buff file content/buff - * @param buffer_offset start offset of the buff - * @param buffer_length the length of buff to modify - * @return 0 for success, != 0 for error (error no) - */ - public int modify_file1(String appender_file_id, - long file_offset, byte[] file_buff, int buffer_offset, int buffer_length) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(appender_file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.modify_file(parts[0], parts[1], file_offset, - file_buff, buffer_offset, buffer_length); - } - - /** - * modify appender file to storage server (by callback) - * - * @param appender_file_id the appender file id - * @param file_offset the offset of appender file - * @param modify_size the modify size - * @param callback the write data callback object - * @return 0 for success, != 0 for error (error no) - */ - public int modify_file1(String appender_file_id, - long file_offset, long modify_size, UploadCallback callback) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(appender_file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.modify_file(parts[0], parts[1], file_offset, modify_size, callback); - } - - /** - * regenerate filename for appender file - * - * @param appender_file_id the appender file id - * @return the regenerated file id, return null if fail - */ - public String regenerate_appender_filename1(String appender_file_id) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(appender_file_id, parts); - if (this.errno != 0) { - return null; - } - - String[] new_parts = this.regenerate_appender_filename(parts[0], parts[1]); - if (new_parts != null) { - return new_parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + new_parts[1]; - } else { - return null; - } - } - - /** - * delete file from storage server - * - * @param file_id the file id(including group name and filename) - * @return 0 for success, none zero for fail (error code) - */ - public int delete_file1(String file_id) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.delete_file(parts[0], parts[1]); - } - - /** - * truncate appender file to size 0 from storage server - * - * @param appender_file_id the appender file id - * @return 0 for success, none zero for fail (error code) - */ - public int truncate_file1(String appender_file_id) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(appender_file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.truncate_file(parts[0], parts[1]); - } - - /** - * truncate appender file from storage server - * - * @param appender_file_id the appender file id - * @param truncated_file_size truncated file size - * @return 0 for success, none zero for fail (error code) - */ - public int truncate_file1(String appender_file_id, long truncated_file_size) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(appender_file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.truncate_file(parts[0], parts[1], truncated_file_size); - } - - /** - * download file from storage server - * - * @param file_id the file id(including group name and filename) - * @return file content/buffer, return null if fail - */ - public byte[] download_file1(String file_id) throws IOException, MyException { - final long file_offset = 0; - final long download_bytes = 0; - - return this.download_file1(file_id, file_offset, download_bytes); - } - - /** - * download file from storage server - * - * @param file_id the file id(including group name and filename) - * @param file_offset the start offset of the file - * @param download_bytes download bytes, 0 for remain bytes from offset - * @return file content/buff, return null if fail - */ - public byte[] download_file1(String file_id, long file_offset, long download_bytes) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(file_id, parts); - if (this.errno != 0) { - return null; - } - - return this.download_file(parts[0], parts[1], file_offset, download_bytes); - } - - /** - * download file from storage server - * - * @param file_id the file id(including group name and filename) - * @param local_filename the filename on local - * @return 0 success, return none zero errno if fail - */ - public int download_file1(String file_id, String local_filename) throws IOException, MyException { - final long file_offset = 0; - final long download_bytes = 0; - - return this.download_file1(file_id, file_offset, download_bytes, local_filename); - } - - /** - * download file from storage server - * - * @param file_id the file id(including group name and filename) - * @param file_offset the start offset of the file - * @param download_bytes download bytes, 0 for remain bytes from offset - * @param local_filename the filename on local - * @return 0 success, return none zero errno if fail - */ - public int download_file1(String file_id, long file_offset, long download_bytes, String local_filename) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.download_file(parts[0], parts[1], file_offset, download_bytes, local_filename); - } - - /** - * download file from storage server - * - * @param file_id the file id(including group name and filename) - * @param callback the callback object, will call callback.recv() when data arrive - * @return 0 success, return none zero errno if fail - */ - public int download_file1(String file_id, DownloadCallback callback) throws IOException, MyException { - final long file_offset = 0; - final long download_bytes = 0; - - return this.download_file1(file_id, file_offset, download_bytes, callback); - } - - /** - * download file from storage server - * - * @param file_id the file id(including group name and filename) - * @param file_offset the start offset of the file - * @param download_bytes download bytes, 0 for remain bytes from offset - * @param callback the callback object, will call callback.recv() when data arrive - * @return 0 success, return none zero errno if fail - */ - public int download_file1(String file_id, long file_offset, long download_bytes, DownloadCallback callback) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.download_file(parts[0], parts[1], file_offset, download_bytes, callback); - } - - /** - * get all metadata items from storage server - * - * @param file_id the file id(including group name and filename) - * @return meta info array, return null if fail - */ - public NameValuePair[] get_metadata1(String file_id) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(file_id, parts); - if (this.errno != 0) { - return null; - } - - return this.get_metadata(parts[0], parts[1]); - } - - /** - * set metadata items to storage server - * - * @param file_id the file id(including group name and filename) - * @param meta_list meta item array - * @param op_flag flag, can be one of following values:
- *

  • ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE: overwrite all old - * metadata items
- *
  • ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE: merge, insert when - * the metadata item not exist, otherwise update it
- * @return 0 for success, !=0 fail (error code) - */ - public int set_metadata1(String file_id, NameValuePair[] meta_list, byte op_flag) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(file_id, parts); - if (this.errno != 0) { - return this.errno; - } - - return this.set_metadata(parts[0], parts[1], meta_list, op_flag); - } - - /** - * get file info from storage server - * - * @param file_id the file id(including group name and filename) - * @return FileInfo object for success, return null for fail - */ - public FileInfo query_file_info1(String file_id) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(file_id, parts); - if (this.errno != 0) { - return null; - } - - return this.query_file_info(parts[0], parts[1]); - } - - /** - * get file info decoded from filename - * - * @param file_id the file id(including group name and filename) - * @return FileInfo object for success, return null for fail - */ - public FileInfo get_file_info1(String file_id) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = this.split_file_id(file_id, parts); - if (this.errno != 0) { - return null; - } - - return this.get_file_info(parts[0], parts[1]); - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StorageServer.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StorageServer.java deleted file mode 100644 index a88191f..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StorageServer.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - -import java.io.IOException; -import java.net.InetSocketAddress; - -/** - * Storage Server Info - * - * @author Happy Fish / YuQing - * @version Version 1.11 - */ -public class StorageServer extends TrackerServer { - protected int store_path_index = 0; - - /** - * Constructor - * - * @param ip_addr the ip address of storage server - * @param port the port of storage server - * @param store_path the store path index on the storage server - */ - public StorageServer(String ip_addr, int port, int store_path) throws IOException { - super(new InetSocketAddress(ip_addr, port)); - this.store_path_index = store_path; - } - - /** - * Constructor - * - * @param ip_addr the ip address of storage server - * @param port the port of storage server - * @param store_path the store path index on the storage server - */ - public StorageServer(String ip_addr, int port, byte store_path) throws IOException { - super(new InetSocketAddress(ip_addr, port)); - if (store_path < 0) { - this.store_path_index = 256 + store_path; - } else { - this.store_path_index = store_path; - } - } - - /** - * @return the store path index on the storage server - */ - public int getStorePathIndex() { - return this.store_path_index; - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StructBase.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StructBase.java deleted file mode 100644 index 8962645..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StructBase.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - -import java.io.UnsupportedEncodingException; -import java.util.Date; - -/** - * C struct body decoder - * - * @author Happy Fish / YuQing - * @version Version 1.17 - */ -public abstract class StructBase { - /** - * set fields - * - * @param bs byte array - * @param offset start offset - */ - public abstract void setFields(byte[] bs, int offset); - - protected String stringValue(byte[] bs, int offset, FieldInfo filedInfo) { - try { - return (new String(bs, offset + filedInfo.offset, filedInfo.size, ClientGlobal.g_charset)).trim(); - } catch (UnsupportedEncodingException ex) { - ex.printStackTrace(); - return null; - } - } - - protected long longValue(byte[] bs, int offset, FieldInfo filedInfo) { - return ProtoCommon.buff2long(bs, offset + filedInfo.offset); - } - - protected int intValue(byte[] bs, int offset, FieldInfo filedInfo) { - return (int) ProtoCommon.buff2long(bs, offset + filedInfo.offset); - } - - protected int int32Value(byte[] bs, int offset, FieldInfo filedInfo) { - return ProtoCommon.buff2int(bs, offset + filedInfo.offset); - } - - protected byte byteValue(byte[] bs, int offset, FieldInfo filedInfo) { - return bs[offset + filedInfo.offset]; - } - - protected boolean booleanValue(byte[] bs, int offset, FieldInfo filedInfo) { - return bs[offset + filedInfo.offset] != 0; - } - - protected Date dateValue(byte[] bs, int offset, FieldInfo filedInfo) { - return new Date(ProtoCommon.buff2long(bs, offset + filedInfo.offset) * 1000); - } - - protected static class FieldInfo { - protected String name; - protected int offset; - protected int size; - - public FieldInfo(String name, int offset, int size) { - this.name = name; - this.offset = offset; - this.size = size; - } - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StructGroupStat.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StructGroupStat.java deleted file mode 100644 index df5ab69..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StructGroupStat.java +++ /dev/null @@ -1,225 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - -/** - * C struct body decoder - * - * @author Happy Fish / YuQing - * @version Version 1.18 - */ -public class StructGroupStat extends StructBase { - protected static final int FIELD_INDEX_GROUP_NAME = 0; - protected static final int FIELD_INDEX_TOTAL_MB = 1; - protected static final int FIELD_INDEX_FREE_MB = 2; - protected static final int FIELD_INDEX_TRUNK_FREE_MB = 3; - protected static final int FIELD_INDEX_STORAGE_COUNT = 4; - protected static final int FIELD_INDEX_STORAGE_PORT = 5; - protected static final int FIELD_INDEX_STORAGE_HTTP_PORT = 6; - protected static final int FIELD_INDEX_ACTIVE_COUNT = 7; - protected static final int FIELD_INDEX_CURRENT_WRITE_SERVER = 8; - protected static final int FIELD_INDEX_STORE_PATH_COUNT = 9; - protected static final int FIELD_INDEX_SUBDIR_COUNT_PER_PATH = 10; - protected static final int FIELD_INDEX_CURRENT_TRUNK_FILE_ID = 11; - - protected static int fieldsTotalSize; - protected static FieldInfo[] fieldsArray = new FieldInfo[12]; - - static { - int offset = 0; - fieldsArray[FIELD_INDEX_GROUP_NAME] = new FieldInfo("groupName", offset, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1); - offset += ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1; - - fieldsArray[FIELD_INDEX_TOTAL_MB] = new FieldInfo("totalMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_FREE_MB] = new FieldInfo("freeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TRUNK_FREE_MB] = new FieldInfo("trunkFreeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_STORAGE_COUNT] = new FieldInfo("storageCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_STORAGE_PORT] = new FieldInfo("storagePort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT] = new FieldInfo("storageHttpPort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_ACTIVE_COUNT] = new FieldInfo("activeCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_CURRENT_WRITE_SERVER] = new FieldInfo("currentWriteServer", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_STORE_PATH_COUNT] = new FieldInfo("storePathCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH] = new FieldInfo("subdirCountPerPath", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_CURRENT_TRUNK_FILE_ID] = new FieldInfo("currentTrunkFileId", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsTotalSize = offset; - } - - protected String groupName; //name of this group - protected long totalMB; //total disk storage in MB - protected long freeMB; //free disk space in MB - protected long trunkFreeMB; //trunk free space in MB - protected int storageCount; //storage server count - protected int storagePort; //storage server port - protected int storageHttpPort; //storage server HTTP port - protected int activeCount; //active storage server count - protected int currentWriteServer; //current storage server index to upload file - protected int storePathCount; //store base path count of each storage server - protected int subdirCountPerPath; //sub dir count per store path - protected int currentTrunkFileId; //current trunk file id - - /** - * get fields total size - * - * @return fields total size - */ - public static int getFieldsTotalSize() { - return fieldsTotalSize; - } - - /** - * get group name - * - * @return group name - */ - public String getGroupName() { - return this.groupName; - } - - /** - * get total disk space in MB - * - * @return total disk space in MB - */ - public long getTotalMB() { - return this.totalMB; - } - - /** - * get free disk space in MB - * - * @return free disk space in MB - */ - public long getFreeMB() { - return this.freeMB; - } - - /** - * get trunk free space in MB - * - * @return trunk free space in MB - */ - public long getTrunkFreeMB() { - return this.trunkFreeMB; - } - - /** - * get storage server count in this group - * - * @return storage server count in this group - */ - public int getStorageCount() { - return this.storageCount; - } - - /** - * get active storage server count in this group - * - * @return active storage server count in this group - */ - public int getActiveCount() { - return this.activeCount; - } - - /** - * get storage server port - * - * @return storage server port - */ - public int getStoragePort() { - return this.storagePort; - } - - /** - * get storage server HTTP port - * - * @return storage server HTTP port - */ - public int getStorageHttpPort() { - return this.storageHttpPort; - } - - /** - * get current storage server index to upload file - * - * @return current storage server index to upload file - */ - public int getCurrentWriteServer() { - return this.currentWriteServer; - } - - /** - * get store base path count of each storage server - * - * @return store base path count of each storage server - */ - public int getStorePathCount() { - return this.storePathCount; - } - - /** - * get sub dir count per store path - * - * @return sub dir count per store path - */ - public int getSubdirCountPerPath() { - return this.subdirCountPerPath; - } - - /** - * get current trunk file id - * - * @return current trunk file id - */ - public int getCurrentTrunkFileId() { - return this.currentTrunkFileId; - } - - /** - * set fields - * - * @param bs byte array - * @param offset start offset - */ - public void setFields(byte[] bs, int offset) { - this.groupName = stringValue(bs, offset, fieldsArray[FIELD_INDEX_GROUP_NAME]); - this.totalMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MB]); - this.freeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_FREE_MB]); - this.trunkFreeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TRUNK_FREE_MB]); - this.storageCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_COUNT]); - this.storagePort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_PORT]); - this.storageHttpPort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT]); - this.activeCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_ACTIVE_COUNT]); - this.currentWriteServer = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_WRITE_SERVER]); - this.storePathCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORE_PATH_COUNT]); - this.subdirCountPerPath = intValue(bs, offset, fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH]); - this.currentTrunkFileId = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_TRUNK_FILE_ID]); - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StructStorageStat.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StructStorageStat.java deleted file mode 100644 index 29df4e8..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/StructStorageStat.java +++ /dev/null @@ -1,982 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - -import java.util.Date; - -/** - * C struct body decoder - * - * @author Happy Fish / YuQing - * @version Version 1.25 - */ -public class StructStorageStat extends StructBase { - protected static final int FIELD_INDEX_STATUS = 0; - protected static final int FIELD_INDEX_ID = 1; - protected static final int FIELD_INDEX_IP_ADDR = 2; - protected static final int FIELD_INDEX_DOMAIN_NAME = 3; - protected static final int FIELD_INDEX_SRC_IP_ADDR = 4; - protected static final int FIELD_INDEX_VERSION = 5; - protected static final int FIELD_INDEX_JOIN_TIME = 6; - protected static final int FIELD_INDEX_UP_TIME = 7; - protected static final int FIELD_INDEX_TOTAL_MB = 8; - protected static final int FIELD_INDEX_FREE_MB = 9; - protected static final int FIELD_INDEX_UPLOAD_PRIORITY = 10; - protected static final int FIELD_INDEX_STORE_PATH_COUNT = 11; - protected static final int FIELD_INDEX_SUBDIR_COUNT_PER_PATH = 12; - protected static final int FIELD_INDEX_CURRENT_WRITE_PATH = 13; - protected static final int FIELD_INDEX_STORAGE_PORT = 14; - protected static final int FIELD_INDEX_STORAGE_HTTP_PORT = 15; - - protected static final int FIELD_INDEX_CONNECTION_ALLOC_COUNT = 16; - protected static final int FIELD_INDEX_CONNECTION_CURRENT_COUNT = 17; - protected static final int FIELD_INDEX_CONNECTION_MAX_COUNT = 18; - - protected static final int FIELD_INDEX_TOTAL_UPLOAD_COUNT = 19; - protected static final int FIELD_INDEX_SUCCESS_UPLOAD_COUNT = 20; - protected static final int FIELD_INDEX_TOTAL_APPEND_COUNT = 21; - protected static final int FIELD_INDEX_SUCCESS_APPEND_COUNT = 22; - protected static final int FIELD_INDEX_TOTAL_MODIFY_COUNT = 23; - protected static final int FIELD_INDEX_SUCCESS_MODIFY_COUNT = 24; - protected static final int FIELD_INDEX_TOTAL_TRUNCATE_COUNT = 25; - protected static final int FIELD_INDEX_SUCCESS_TRUNCATE_COUNT = 26; - protected static final int FIELD_INDEX_TOTAL_SET_META_COUNT = 27; - protected static final int FIELD_INDEX_SUCCESS_SET_META_COUNT = 28; - protected static final int FIELD_INDEX_TOTAL_DELETE_COUNT = 29; - protected static final int FIELD_INDEX_SUCCESS_DELETE_COUNT = 30; - protected static final int FIELD_INDEX_TOTAL_DOWNLOAD_COUNT = 31; - protected static final int FIELD_INDEX_SUCCESS_DOWNLOAD_COUNT = 32; - protected static final int FIELD_INDEX_TOTAL_GET_META_COUNT = 33; - protected static final int FIELD_INDEX_SUCCESS_GET_META_COUNT = 34; - protected static final int FIELD_INDEX_TOTAL_CREATE_LINK_COUNT = 35; - protected static final int FIELD_INDEX_SUCCESS_CREATE_LINK_COUNT = 36; - protected static final int FIELD_INDEX_TOTAL_DELETE_LINK_COUNT = 37; - protected static final int FIELD_INDEX_SUCCESS_DELETE_LINK_COUNT = 38; - protected static final int FIELD_INDEX_TOTAL_UPLOAD_BYTES = 39; - protected static final int FIELD_INDEX_SUCCESS_UPLOAD_BYTES = 40; - protected static final int FIELD_INDEX_TOTAL_APPEND_BYTES = 41; - protected static final int FIELD_INDEX_SUCCESS_APPEND_BYTES = 42; - protected static final int FIELD_INDEX_TOTAL_MODIFY_BYTES = 43; - protected static final int FIELD_INDEX_SUCCESS_MODIFY_BYTES = 44; - protected static final int FIELD_INDEX_TOTAL_DOWNLOAD_BYTES = 45; - protected static final int FIELD_INDEX_SUCCESS_DOWNLOAD_BYTES = 46; - protected static final int FIELD_INDEX_TOTAL_SYNC_IN_BYTES = 47; - protected static final int FIELD_INDEX_SUCCESS_SYNC_IN_BYTES = 48; - protected static final int FIELD_INDEX_TOTAL_SYNC_OUT_BYTES = 49; - protected static final int FIELD_INDEX_SUCCESS_SYNC_OUT_BYTES = 50; - protected static final int FIELD_INDEX_TOTAL_FILE_OPEN_COUNT = 51; - protected static final int FIELD_INDEX_SUCCESS_FILE_OPEN_COUNT = 52; - protected static final int FIELD_INDEX_TOTAL_FILE_READ_COUNT = 53; - protected static final int FIELD_INDEX_SUCCESS_FILE_READ_COUNT = 54; - protected static final int FIELD_INDEX_TOTAL_FILE_WRITE_COUNT = 55; - protected static final int FIELD_INDEX_SUCCESS_FILE_WRITE_COUNT = 56; - protected static final int FIELD_INDEX_LAST_SOURCE_UPDATE = 57; - protected static final int FIELD_INDEX_LAST_SYNC_UPDATE = 58; - protected static final int FIELD_INDEX_LAST_SYNCED_TIMESTAMP = 59; - protected static final int FIELD_INDEX_LAST_HEART_BEAT_TIME = 60; - protected static final int FIELD_INDEX_IF_TRUNK_FILE = 61; - - protected static int fieldsTotalSize; - protected static FieldInfo[] fieldsArray = new FieldInfo[62]; - - static { - int offset = 0; - - fieldsArray[FIELD_INDEX_STATUS] = new FieldInfo("status", offset, 1); - offset += 1; - - fieldsArray[FIELD_INDEX_ID] = new FieldInfo("id", offset, ProtoCommon.FDFS_STORAGE_ID_MAX_SIZE); - offset += ProtoCommon.FDFS_STORAGE_ID_MAX_SIZE; - - fieldsArray[FIELD_INDEX_IP_ADDR] = new FieldInfo("ipAddr", offset, ProtoCommon.FDFS_IPADDR_SIZE); - offset += ProtoCommon.FDFS_IPADDR_SIZE; - - fieldsArray[FIELD_INDEX_DOMAIN_NAME] = new FieldInfo("domainName", offset, ProtoCommon.FDFS_DOMAIN_NAME_MAX_SIZE); - offset += ProtoCommon.FDFS_DOMAIN_NAME_MAX_SIZE; - - fieldsArray[FIELD_INDEX_SRC_IP_ADDR] = new FieldInfo("srcIpAddr", offset, ProtoCommon.FDFS_IPADDR_SIZE); - offset += ProtoCommon.FDFS_IPADDR_SIZE; - - fieldsArray[FIELD_INDEX_VERSION] = new FieldInfo("version", offset, ProtoCommon.FDFS_VERSION_SIZE); - offset += ProtoCommon.FDFS_VERSION_SIZE; - - fieldsArray[FIELD_INDEX_JOIN_TIME] = new FieldInfo("joinTime", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_UP_TIME] = new FieldInfo("upTime", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_MB] = new FieldInfo("totalMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_FREE_MB] = new FieldInfo("freeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_UPLOAD_PRIORITY] = new FieldInfo("uploadPriority", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_STORE_PATH_COUNT] = new FieldInfo("storePathCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH] = new FieldInfo("subdirCountPerPath", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_CURRENT_WRITE_PATH] = new FieldInfo("currentWritePath", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_STORAGE_PORT] = new FieldInfo("storagePort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT] = new FieldInfo("storageHttpPort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_CONNECTION_ALLOC_COUNT] = new FieldInfo("connectionAllocCount", offset, 4); - offset += 4; - - fieldsArray[FIELD_INDEX_CONNECTION_CURRENT_COUNT] = new FieldInfo("connectionCurrentCount", offset, 4); - offset += 4; - - fieldsArray[FIELD_INDEX_CONNECTION_MAX_COUNT] = new FieldInfo("connectionMaxCount", offset, 4); - offset += 4; - - fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_COUNT] = new FieldInfo("totalUploadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_COUNT] = new FieldInfo("successUploadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_APPEND_COUNT] = new FieldInfo("totalAppendCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_APPEND_COUNT] = new FieldInfo("successAppendCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_MODIFY_COUNT] = new FieldInfo("totalModifyCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_COUNT] = new FieldInfo("successModifyCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_TRUNCATE_COUNT] = new FieldInfo("totalTruncateCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_TRUNCATE_COUNT] = new FieldInfo("successTruncateCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_SET_META_COUNT] = new FieldInfo("totalSetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_SET_META_COUNT] = new FieldInfo("successSetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_DELETE_COUNT] = new FieldInfo("totalDeleteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_DELETE_COUNT] = new FieldInfo("successDeleteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_COUNT] = new FieldInfo("totalDownloadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_COUNT] = new FieldInfo("successDownloadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_GET_META_COUNT] = new FieldInfo("totalGetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_GET_META_COUNT] = new FieldInfo("successGetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_CREATE_LINK_COUNT] = new FieldInfo("totalCreateLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_CREATE_LINK_COUNT] = new FieldInfo("successCreateLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_DELETE_LINK_COUNT] = new FieldInfo("totalDeleteLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_DELETE_LINK_COUNT] = new FieldInfo("successDeleteLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_BYTES] = new FieldInfo("totalUploadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_BYTES] = new FieldInfo("successUploadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_APPEND_BYTES] = new FieldInfo("totalAppendBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_APPEND_BYTES] = new FieldInfo("successAppendBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_MODIFY_BYTES] = new FieldInfo("totalModifyBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_BYTES] = new FieldInfo("successModifyBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_BYTES] = new FieldInfo("totalDownloadloadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_BYTES] = new FieldInfo("successDownloadloadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_SYNC_IN_BYTES] = new FieldInfo("totalSyncInBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_SYNC_IN_BYTES] = new FieldInfo("successSyncInBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_SYNC_OUT_BYTES] = new FieldInfo("totalSyncOutBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_SYNC_OUT_BYTES] = new FieldInfo("successSyncOutBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_FILE_OPEN_COUNT] = new FieldInfo("totalFileOpenCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_FILE_OPEN_COUNT] = new FieldInfo("successFileOpenCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_FILE_READ_COUNT] = new FieldInfo("totalFileReadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_FILE_READ_COUNT] = new FieldInfo("successFileReadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_TOTAL_FILE_WRITE_COUNT] = new FieldInfo("totalFileWriteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_SUCCESS_FILE_WRITE_COUNT] = new FieldInfo("successFileWriteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_LAST_SOURCE_UPDATE] = new FieldInfo("lastSourceUpdate", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_LAST_SYNC_UPDATE] = new FieldInfo("lastSyncUpdate", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_LAST_SYNCED_TIMESTAMP] = new FieldInfo("lastSyncedTimestamp", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_LAST_HEART_BEAT_TIME] = new FieldInfo("lastHeartBeatTime", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - fieldsArray[FIELD_INDEX_IF_TRUNK_FILE] = new FieldInfo("ifTrunkServer", offset, 1); - offset += 1; - - fieldsTotalSize = offset; - } - - protected byte status; - protected String id; - protected String ipAddr; - protected String srcIpAddr; - protected String domainName; //http domain name - protected String version; - protected long totalMB; //total disk storage in MB - protected long freeMB; //free disk storage in MB - protected int uploadPriority; //upload priority - protected Date joinTime; //storage join timestamp (create timestamp) - protected Date upTime; //storage service started timestamp - protected int storePathCount; //store base path count of each storage server - protected int subdirCountPerPath; - protected int storagePort; - protected int storageHttpPort; //storage http server port - protected int currentWritePath; //current write path index - protected int connectionAllocCount; - protected int connectionCurrentCount; - protected int connectionMaxCount; - protected long totalUploadCount; - protected long successUploadCount; - protected long totalAppendCount; - protected long successAppendCount; - protected long totalModifyCount; - protected long successModifyCount; - protected long totalTruncateCount; - protected long successTruncateCount; - protected long totalSetMetaCount; - protected long successSetMetaCount; - protected long totalDeleteCount; - protected long successDeleteCount; - protected long totalDownloadCount; - protected long successDownloadCount; - protected long totalGetMetaCount; - protected long successGetMetaCount; - protected long totalCreateLinkCount; - protected long successCreateLinkCount; - protected long totalDeleteLinkCount; - protected long successDeleteLinkCount; - protected long totalUploadBytes; - protected long successUploadBytes; - protected long totalAppendBytes; - protected long successAppendBytes; - protected long totalModifyBytes; - protected long successModifyBytes; - protected long totalDownloadloadBytes; - protected long successDownloadloadBytes; - protected long totalSyncInBytes; - protected long successSyncInBytes; - protected long totalSyncOutBytes; - protected long successSyncOutBytes; - protected long totalFileOpenCount; - protected long successFileOpenCount; - protected long totalFileReadCount; - protected long successFileReadCount; - protected long totalFileWriteCount; - protected long successFileWriteCount; - protected Date lastSourceUpdate; - protected Date lastSyncUpdate; - protected Date lastSyncedTimestamp; - protected Date lastHeartBeatTime; - protected boolean ifTrunkServer; - - /** - * get fields total size - * - * @return fields total size - */ - public static int getFieldsTotalSize() { - return fieldsTotalSize; - } - - /** - * get storage status - * - * @return storage status - */ - public byte getStatus() { - return this.status; - } - - /** - * get storage server id - * - * @return storage server id - */ - public String getId() { - return this.id; - } - - /** - * get storage server ip address - * - * @return storage server ip address - */ - public String getIpAddr() { - return this.ipAddr; - } - - /** - * get source storage ip address - * - * @return source storage ip address - */ - public String getSrcIpAddr() { - return this.srcIpAddr; - } - - /** - * get the domain name of the storage server - * - * @return the domain name of the storage server - */ - public String getDomainName() { - return this.domainName; - } - - /** - * get storage version - * - * @return storage version - */ - public String getVersion() { - return this.version; - } - - /** - * get total disk space in MB - * - * @return total disk space in MB - */ - public long getTotalMB() { - return this.totalMB; - } - - /** - * get free disk space in MB - * - * @return free disk space in MB - */ - public long getFreeMB() { - return this.freeMB; - } - - /** - * get storage server upload priority - * - * @return storage server upload priority - */ - public int getUploadPriority() { - return this.uploadPriority; - } - - /** - * get storage server join time - * - * @return storage server join time - */ - public Date getJoinTime() { - return this.joinTime; - } - - /** - * get storage server up time - * - * @return storage server up time - */ - public Date getUpTime() { - return this.upTime; - } - - /** - * get store base path count of each storage server - * - * @return store base path count of each storage server - */ - public int getStorePathCount() { - return this.storePathCount; - } - - /** - * get sub dir count per store path - * - * @return sub dir count per store path - */ - public int getSubdirCountPerPath() { - return this.subdirCountPerPath; - } - - /** - * get storage server port - * - * @return storage server port - */ - public int getStoragePort() { - return this.storagePort; - } - - /** - * get storage server HTTP port - * - * @return storage server HTTP port - */ - public int getStorageHttpPort() { - return this.storageHttpPort; - } - - /** - * get current write path index - * - * @return current write path index - */ - public int getCurrentWritePath() { - return this.currentWritePath; - } - - /** - * get total upload file count - * - * @return total upload file count - */ - public long getTotalUploadCount() { - return this.totalUploadCount; - } - - /** - * get success upload file count - * - * @return success upload file count - */ - public long getSuccessUploadCount() { - return this.successUploadCount; - } - - /** - * get total append count - * - * @return total append count - */ - public long getTotalAppendCount() { - return this.totalAppendCount; - } - - /** - * get success append count - * - * @return success append count - */ - public long getSuccessAppendCount() { - return this.successAppendCount; - } - - /** - * get total modify count - * - * @return total modify count - */ - public long getTotalModifyCount() { - return this.totalModifyCount; - } - - /** - * get success modify count - * - * @return success modify count - */ - public long getSuccessModifyCount() { - return this.successModifyCount; - } - - /** - * get total truncate count - * - * @return total truncate count - */ - public long getTotalTruncateCount() { - return this.totalTruncateCount; - } - - /** - * get success truncate count - * - * @return success truncate count - */ - public long getSuccessTruncateCount() { - return this.successTruncateCount; - } - - /** - * get total set meta data count - * - * @return total set meta data count - */ - public long getTotalSetMetaCount() { - return this.totalSetMetaCount; - } - - /** - * get success set meta data count - * - * @return success set meta data count - */ - public long getSuccessSetMetaCount() { - return this.successSetMetaCount; - } - - /** - * get total delete file count - * - * @return total delete file count - */ - public long getTotalDeleteCount() { - return this.totalDeleteCount; - } - - /** - * get success delete file count - * - * @return success delete file count - */ - public long getSuccessDeleteCount() { - return this.successDeleteCount; - } - - /** - * get total download file count - * - * @return total download file count - */ - public long getTotalDownloadCount() { - return this.totalDownloadCount; - } - - /** - * get success download file count - * - * @return success download file count - */ - public long getSuccessDownloadCount() { - return this.successDownloadCount; - } - - /** - * get total get metadata count - * - * @return total get metadata count - */ - public long getTotalGetMetaCount() { - return this.totalGetMetaCount; - } - - /** - * get success get metadata count - * - * @return success get metadata count - */ - public long getSuccessGetMetaCount() { - return this.successGetMetaCount; - } - - /** - * get total create linke count - * - * @return total create linke count - */ - public long getTotalCreateLinkCount() { - return this.totalCreateLinkCount; - } - - /** - * get success create linke count - * - * @return success create linke count - */ - public long getSuccessCreateLinkCount() { - return this.successCreateLinkCount; - } - - /** - * get total delete link count - * - * @return total delete link count - */ - public long getTotalDeleteLinkCount() { - return this.totalDeleteLinkCount; - } - - /** - * get success delete link count - * - * @return success delete link count - */ - public long getSuccessDeleteLinkCount() { - return this.successDeleteLinkCount; - } - - /** - * get total upload file bytes - * - * @return total upload file bytes - */ - public long getTotalUploadBytes() { - return this.totalUploadBytes; - } - - /** - * get success upload file bytes - * - * @return success upload file bytes - */ - public long getSuccessUploadBytes() { - return this.successUploadBytes; - } - - /** - * get total append bytes - * - * @return total append bytes - */ - public long getTotalAppendBytes() { - return this.totalAppendBytes; - } - - /** - * get success append bytes - * - * @return success append bytes - */ - public long getSuccessAppendBytes() { - return this.successAppendBytes; - } - - /** - * get total modify bytes - * - * @return total modify bytes - */ - public long getTotalModifyBytes() { - return this.totalModifyBytes; - } - - /** - * get success modify bytes - * - * @return success modify bytes - */ - public long getSuccessModifyBytes() { - return this.successModifyBytes; - } - - /** - * get total download file bytes - * - * @return total download file bytes - */ - public long getTotalDownloadloadBytes() { - return this.totalDownloadloadBytes; - } - - /** - * get success download file bytes - * - * @return success download file bytes - */ - public long getSuccessDownloadloadBytes() { - return this.successDownloadloadBytes; - } - - /** - * get total sync in bytes - * - * @return total sync in bytes - */ - public long getTotalSyncInBytes() { - return this.totalSyncInBytes; - } - - /** - * get success sync in bytes - * - * @return success sync in bytes - */ - public long getSuccessSyncInBytes() { - return this.successSyncInBytes; - } - - /** - * get total sync out bytes - * - * @return total sync out bytes - */ - public long getTotalSyncOutBytes() { - return this.totalSyncOutBytes; - } - - /** - * get success sync out bytes - * - * @return success sync out bytes - */ - public long getSuccessSyncOutBytes() { - return this.successSyncOutBytes; - } - - /** - * get total file opened count - * - * @return total file opened bytes - */ - public long getTotalFileOpenCount() { - return this.totalFileOpenCount; - } - - /** - * get success file opened count - * - * @return success file opened count - */ - public long getSuccessFileOpenCount() { - return this.successFileOpenCount; - } - - /** - * get total file read count - * - * @return total file read bytes - */ - public long getTotalFileReadCount() { - return this.totalFileReadCount; - } - - /** - * get success file read count - * - * @return success file read count - */ - public long getSuccessFileReadCount() { - return this.successFileReadCount; - } - - /** - * get total file write count - * - * @return total file write bytes - */ - public long getTotalFileWriteCount() { - return this.totalFileWriteCount; - } - - /** - * get success file write count - * - * @return success file write count - */ - public long getSuccessFileWriteCount() { - return this.successFileWriteCount; - } - - /** - * get last source update timestamp - * - * @return last source update timestamp - */ - public Date getLastSourceUpdate() { - return this.lastSourceUpdate; - } - - /** - * get last synced update timestamp - * - * @return last synced update timestamp - */ - public Date getLastSyncUpdate() { - return this.lastSyncUpdate; - } - - /** - * get last synced timestamp - * - * @return last synced timestamp - */ - public Date getLastSyncedTimestamp() { - return this.lastSyncedTimestamp; - } - - /** - * get last heart beat timestamp - * - * @return last heart beat timestamp - */ - public Date getLastHeartBeatTime() { - return this.lastHeartBeatTime; - } - - /** - * if the trunk server - * - * @return true for the trunk server, otherwise false - */ - public boolean isTrunkServer() { - return this.ifTrunkServer; - } - - /** - * get connection alloc count - * - * @return connection alloc count - */ - public int getConnectionAllocCount() { - return this.connectionAllocCount; - } - - /** - * get connection current count - * - * @return connection current count - */ - public int getConnectionCurrentCount() { - return this.connectionCurrentCount; - } - - /** - * get connection max count - * - * @return connection max count - */ - public int getConnectionMaxCount() { - return this.connectionMaxCount; - } - - /** - * set fields - * - * @param bs byte array - * @param offset start offset - */ - public void setFields(byte[] bs, int offset) { - this.status = byteValue(bs, offset, fieldsArray[FIELD_INDEX_STATUS]); - this.id = stringValue(bs, offset, fieldsArray[FIELD_INDEX_ID]); - this.ipAddr = stringValue(bs, offset, fieldsArray[FIELD_INDEX_IP_ADDR]); - this.srcIpAddr = stringValue(bs, offset, fieldsArray[FIELD_INDEX_SRC_IP_ADDR]); - this.domainName = stringValue(bs, offset, fieldsArray[FIELD_INDEX_DOMAIN_NAME]); - this.version = stringValue(bs, offset, fieldsArray[FIELD_INDEX_VERSION]); - this.totalMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MB]); - this.freeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_FREE_MB]); - this.uploadPriority = intValue(bs, offset, fieldsArray[FIELD_INDEX_UPLOAD_PRIORITY]); - this.joinTime = dateValue(bs, offset, fieldsArray[FIELD_INDEX_JOIN_TIME]); - this.upTime = dateValue(bs, offset, fieldsArray[FIELD_INDEX_UP_TIME]); - this.storePathCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORE_PATH_COUNT]); - this.subdirCountPerPath = intValue(bs, offset, fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH]); - this.storagePort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_PORT]); - this.storageHttpPort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT]); - this.currentWritePath = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_WRITE_PATH]); - - this.connectionAllocCount = int32Value(bs, offset, fieldsArray[FIELD_INDEX_CONNECTION_ALLOC_COUNT]); - this.connectionCurrentCount = int32Value(bs, offset, fieldsArray[FIELD_INDEX_CONNECTION_CURRENT_COUNT]); - this.connectionMaxCount = int32Value(bs, offset, fieldsArray[FIELD_INDEX_CONNECTION_MAX_COUNT]); - - this.totalUploadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_COUNT]); - this.successUploadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_COUNT]); - this.totalAppendCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_APPEND_COUNT]); - this.successAppendCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_APPEND_COUNT]); - this.totalModifyCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MODIFY_COUNT]); - this.successModifyCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_COUNT]); - this.totalTruncateCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_TRUNCATE_COUNT]); - this.successTruncateCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_TRUNCATE_COUNT]); - this.totalSetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_SET_META_COUNT]); - this.successSetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_SET_META_COUNT]); - this.totalDeleteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DELETE_COUNT]); - this.successDeleteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DELETE_COUNT]); - this.totalDownloadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_COUNT]); - this.successDownloadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_COUNT]); - this.totalGetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_GET_META_COUNT]); - this.successGetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_GET_META_COUNT]); - this.totalCreateLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_CREATE_LINK_COUNT]); - this.successCreateLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_CREATE_LINK_COUNT]); - this.totalDeleteLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DELETE_LINK_COUNT]); - this.successDeleteLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DELETE_LINK_COUNT]); - this.totalUploadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_BYTES]); - this.successUploadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_BYTES]); - this.totalAppendBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_APPEND_BYTES]); - this.successAppendBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_APPEND_BYTES]); - this.totalModifyBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MODIFY_BYTES]); - this.successModifyBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_BYTES]); - this.totalDownloadloadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_BYTES]); - this.successDownloadloadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_BYTES]); - this.totalSyncInBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_SYNC_IN_BYTES]); - this.successSyncInBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_SYNC_IN_BYTES]); - this.totalSyncOutBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_SYNC_OUT_BYTES]); - this.successSyncOutBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_SYNC_OUT_BYTES]); - this.totalFileOpenCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_FILE_OPEN_COUNT]); - this.successFileOpenCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_FILE_OPEN_COUNT]); - this.totalFileReadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_FILE_READ_COUNT]); - this.successFileReadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_FILE_READ_COUNT]); - this.totalFileWriteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_FILE_WRITE_COUNT]); - this.successFileWriteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_FILE_WRITE_COUNT]); - this.lastSourceUpdate = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_SOURCE_UPDATE]); - this.lastSyncUpdate = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_SYNC_UPDATE]); - this.lastSyncedTimestamp = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_SYNCED_TIMESTAMP]); - this.lastHeartBeatTime = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_HEART_BEAT_TIME]); - this.ifTrunkServer = booleanValue(bs, offset, fieldsArray[FIELD_INDEX_IF_TRUNK_FILE]); - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/TrackerClient.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/TrackerClient.java deleted file mode 100644 index 865353d..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/TrackerClient.java +++ /dev/null @@ -1,809 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - - -import csource.common.MyException; -import csource.fastdfs.pool.Connection; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.Arrays; - -/** - * Tracker client for request to tracker server - * Note: the instance of this class is NOT thread safe !!! - * - * @author Happy Fish / YuQing - * @version Version 1.19 - */ -public class TrackerClient { - protected TrackerGroup tracker_group; - protected byte errno; - - /** - * constructor with global tracker group - */ - public TrackerClient() { - this.tracker_group = ClientGlobal.g_tracker_group; - } - - /** - * constructor with specified tracker group - * - * @param tracker_group the tracker group object - */ - public TrackerClient(TrackerGroup tracker_group) { - this.tracker_group = tracker_group; - } - - /** - * get the error code of last call - * - * @return the error code of last call - */ - public byte getErrorCode() { - return this.errno; - } - - /** - * get a connection to tracker server - * - * @return tracker server Socket object, return null if fail - */ - public TrackerServer getTrackerServer() throws IOException { - return this.tracker_group.getTrackerServer(); - } - - /** - * query storage server to upload file - * - * @param trackerServer the tracker server - * @return storage server Socket object, return null if fail - */ - public StorageServer getStoreStorage(TrackerServer trackerServer) throws IOException, MyException { - final String groupName = null; - return this.getStoreStorage(trackerServer, groupName); - } - - public Connection getConnection(TrackerServer trackerServer) throws IOException, MyException { - Connection connection = null; - int length = this.tracker_group.tracker_servers.length; - boolean failOver = length > 1 && trackerServer == null; - try { - if (trackerServer == null) { - trackerServer = getTrackerServer(); - if (trackerServer == null) { - throw new MyException("tracker server is empty!"); - } - } - connection = trackerServer.getConnection(); - } catch (IOException e) { - if (failOver) { - System.err.println("trackerServer get connection error, emsg:" + e.getMessage()); - } else { - throw e; - } - } catch (MyException e) { - if (failOver) { - System.err.println("trackerServer get connection error, emsg:" + e.getMessage()); - } else { - throw e; - } - } - if (connection != null || !failOver) { - return connection; - } - //do fail over - int currentIndex = 0; - if (trackerServer != null) { - currentIndex = trackerServer.getIndex(); - } - int failOverCount = 0; - while (failOverCount < length - 1) { - failOverCount++; - currentIndex++; - if (currentIndex >= length) { - currentIndex = 0; - } - try { - trackerServer = this.tracker_group.getTrackerServer(currentIndex); - if (trackerServer == null) { - throw new MyException("tracker server is empty!"); - } - return trackerServer.getConnection(); - } catch (IOException e) { - System.err.println("fail over trackerServer get connection error, failOverCount:" + failOverCount + "," + e.getMessage()); - if (failOverCount == length - 1) { - throw e; - } - - } catch (MyException e) { - System.err.println("fail over trackerServer get connection error, failOverCount:" + failOverCount + ", " + e.getMessage()); - if (failOverCount == length - 1) { - throw e; - } - } - - - } - return null; - } - - - /** - * query storage server to upload file - * - * @param trackerServer the tracker server - * @param groupName the group name to upload file to, can be empty - * @return storage server object, return null if fail - */ - public StorageServer getStoreStorage(TrackerServer trackerServer, String groupName) throws IOException, MyException { - byte[] header; - String ip_addr; - int port; - byte cmd; - int out_len; - byte store_path; - Connection connection = getConnection(trackerServer); - OutputStream out = connection.getOutputStream(); - - try { - if (groupName == null || groupName.length() == 0) { - cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE; - out_len = 0; - } else { - cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE; - out_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; - } - header = ProtoCommon.packHeader(cmd, out_len, (byte) 0); - out.write(header); - - if (groupName != null && groupName.length() > 0) { - byte[] bGroupName; - byte[] bs; - int group_len; - - bs = groupName.getBytes(ClientGlobal.g_charset); - bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; - - if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) { - group_len = bs.length; - } else { - group_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; - } - Arrays.fill(bGroupName, (byte) 0); - System.arraycopy(bs, 0, bGroupName, 0, group_len); - out.write(bGroupName); - } - - ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.TRACKER_PROTO_CMD_RESP, - ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN); - this.errno = pkgInfo.errno; - if (pkgInfo.errno != 0) { - return null; - } - - ip_addr = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim(); - - port = (int) ProtoCommon.buff2long(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN - + ProtoCommon.FDFS_IPADDR_SIZE - 1); - store_path = pkgInfo.body[ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN - 1]; - - return new StorageServer(ip_addr, port, store_path); - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } finally { - if (connection != null) { - try { - connection.release(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } - } - } - } - - /** - * query storage servers to upload file - * - * @param trackerServer the tracker server - * @param groupName the group name to upload file to, can be empty - * @return storage servers, return null if fail - */ - public StorageServer[] getStoreStorages(TrackerServer trackerServer, String groupName) throws IOException, MyException { - byte[] header; - String ip_addr; - int port; - byte cmd; - int out_len; - Connection connection = getConnection(trackerServer); - OutputStream out = connection.getOutputStream(); - - try { - if (groupName == null || groupName.length() == 0) { - cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ALL; - out_len = 0; - } else { - cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ALL; - out_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; - } - header = ProtoCommon.packHeader(cmd, out_len, (byte) 0); - out.write(header); - - if (groupName != null && groupName.length() > 0) { - byte[] bGroupName; - byte[] bs; - int group_len; - - bs = groupName.getBytes(ClientGlobal.g_charset); - bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; - - if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) { - group_len = bs.length; - } else { - group_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; - } - Arrays.fill(bGroupName, (byte) 0); - System.arraycopy(bs, 0, bGroupName, 0, group_len); - out.write(bGroupName); - } - - ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.TRACKER_PROTO_CMD_RESP, -1); - this.errno = pkgInfo.errno; - if (pkgInfo.errno != 0) { - return null; - } - - if (pkgInfo.body.length < ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN) { - this.errno = ProtoCommon.ERR_NO_EINVAL; - return null; - } - - int ipPortLen = pkgInfo.body.length - (ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1); - final int recordLength = ProtoCommon.FDFS_IPADDR_SIZE - 1 + ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - if (ipPortLen % recordLength != 0) { - this.errno = ProtoCommon.ERR_NO_EINVAL; - return null; - } - - int serverCount = ipPortLen / recordLength; - if (serverCount > 16) { - this.errno = ProtoCommon.ERR_NO_ENOSPC; - return null; - } - - StorageServer[] results = new StorageServer[serverCount]; - byte store_path = pkgInfo.body[pkgInfo.body.length - 1]; - int offset = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; - - for (int i = 0; i < serverCount; i++) { - ip_addr = new String(pkgInfo.body, offset, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim(); - offset += ProtoCommon.FDFS_IPADDR_SIZE - 1; - - port = (int) ProtoCommon.buff2long(pkgInfo.body, offset); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - results[i] = new StorageServer(ip_addr, port, store_path); - } - - return results; - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } finally { - if (connection != null) { - try { - connection.release(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } - } - } - } - - /** - * query storage server to download file - * - * @param trackerServer the tracker server - * @param groupName the group name of storage server - * @param filename filename on storage server - * @return storage server Socket object, return null if fail - */ - public StorageServer getFetchStorage(TrackerServer trackerServer, - String groupName, String filename) throws IOException, MyException { - ServerInfo[] servers = this.getStorages(trackerServer, ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE, - groupName, filename); - if (servers == null) { - return null; - } else { - return new StorageServer(servers[0].getIpAddr(), servers[0].getPort(), 0); - } - } - - /** - * query storage server to update file (delete file or set meta data) - * - * @param trackerServer the tracker server - * @param groupName the group name of storage server - * @param filename filename on storage server - * @return storage server Socket object, return null if fail - */ - public StorageServer getUpdateStorage(TrackerServer trackerServer, - String groupName, String filename) throws IOException, MyException { - ServerInfo[] servers = this.getStorages(trackerServer, ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE, - groupName, filename); - if (servers == null) { - return null; - } else { - return new StorageServer(servers[0].getIpAddr(), servers[0].getPort(), 0); - } - } - - /** - * get storage servers to download file - * - * @param trackerServer the tracker server - * @param groupName the group name of storage server - * @param filename filename on storage server - * @return storage servers, return null if fail - */ - public ServerInfo[] getFetchStorages(TrackerServer trackerServer, - String groupName, String filename) throws IOException, MyException { - return this.getStorages(trackerServer, ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL, - groupName, filename); - } - - /** - * query storage server to download file - * - * @param trackerServer the tracker server - * @param cmd command code, ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE or - * ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE - * @param groupName the group name of storage server - * @param filename filename on storage server - * @return storage server Socket object, return null if fail - */ - protected ServerInfo[] getStorages(TrackerServer trackerServer, - byte cmd, String groupName, String filename) throws IOException, MyException { - byte[] header; - byte[] bFileName; - byte[] bGroupName; - byte[] bs; - int len; - String ip_addr; - int port; - Connection connection = getConnection(trackerServer); - OutputStream out = connection.getOutputStream(); - - try { - bs = groupName.getBytes(ClientGlobal.g_charset); - bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; - bFileName = filename.getBytes(ClientGlobal.g_charset); - - if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) { - len = bs.length; - } else { - len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; - } - Arrays.fill(bGroupName, (byte) 0); - System.arraycopy(bs, 0, bGroupName, 0, len); - - header = ProtoCommon.packHeader(cmd, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + bFileName.length, (byte) 0); - byte[] wholePkg = new byte[header.length + bGroupName.length + bFileName.length]; - System.arraycopy(header, 0, wholePkg, 0, header.length); - System.arraycopy(bGroupName, 0, wholePkg, header.length, bGroupName.length); - System.arraycopy(bFileName, 0, wholePkg, header.length + bGroupName.length, bFileName.length); - out.write(wholePkg); - - ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.TRACKER_PROTO_CMD_RESP, -1); - this.errno = pkgInfo.errno; - if (pkgInfo.errno != 0) { - return null; - } - - if (pkgInfo.body.length < ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) { - throw new IOException("Invalid body length: " + pkgInfo.body.length); - } - - if ((pkgInfo.body.length - ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) % (ProtoCommon.FDFS_IPADDR_SIZE - 1) != 0) { - throw new IOException("Invalid body length: " + pkgInfo.body.length); - } - - int server_count = 1 + (pkgInfo.body.length - ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) / (ProtoCommon.FDFS_IPADDR_SIZE - 1); - - ip_addr = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim(); - int offset = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ProtoCommon.FDFS_IPADDR_SIZE - 1; - - port = (int) ProtoCommon.buff2long(pkgInfo.body, offset); - offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; - - ServerInfo[] servers = new ServerInfo[server_count]; - servers[0] = new ServerInfo(ip_addr, port); - for (int i = 1; i < server_count; i++) { - servers[i] = new ServerInfo(new String(pkgInfo.body, offset, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim(), port); - offset += ProtoCommon.FDFS_IPADDR_SIZE - 1; - } - - return servers; - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - - throw ex; - } finally { - if (connection != null) { - try { - connection.release(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } - } - } - } - - /** - * query storage server to download file - * - * @param trackerServer the tracker server - * @param file_id the file id(including group name and filename) - * @return storage server Socket object, return null if fail - */ - public StorageServer getFetchStorage1(TrackerServer trackerServer, String file_id) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = StorageClient1.split_file_id(file_id, parts); - if (this.errno != 0) { - return null; - } - - return this.getFetchStorage(trackerServer, parts[0], parts[1]); - } - - /** - * get storage servers to download file - * - * @param trackerServer the tracker server - * @param file_id the file id(including group name and filename) - * @return storage servers, return null if fail - */ - public ServerInfo[] getFetchStorages1(TrackerServer trackerServer, String file_id) throws IOException, MyException { - String[] parts = new String[2]; - this.errno = StorageClient1.split_file_id(file_id, parts); - if (this.errno != 0) { - return null; - } - - return this.getFetchStorages(trackerServer, parts[0], parts[1]); - } - - /** - * list groups - * - * @param trackerServer the tracker server - * @return group stat array, return null if fail - */ - public StructGroupStat[] listGroups(TrackerServer trackerServer) throws IOException, MyException { - byte[] header; - String ip_addr; - int port; - byte cmd; - int out_len; - byte store_path; - Connection connection = getConnection(trackerServer); - OutputStream out = connection.getOutputStream(); - - try { - header = ProtoCommon.packHeader(ProtoCommon.TRACKER_PROTO_CMD_SERVER_LIST_GROUP, 0, (byte) 0); - out.write(header); - - ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.TRACKER_PROTO_CMD_RESP, -1); - this.errno = pkgInfo.errno; - if (pkgInfo.errno != 0) { - return null; - } - - ProtoStructDecoder decoder = new ProtoStructDecoder(); - return decoder.decode(pkgInfo.body, StructGroupStat.class, StructGroupStat.getFieldsTotalSize()); - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } finally { - connection = null; - } - throw ex; - } catch (Exception ex) { - ex.printStackTrace(); - this.errno = ProtoCommon.ERR_NO_EINVAL; - return null; - } finally { - if (connection != null) { - try { - connection.release(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } - } - } - } - - /** - * query storage server stat info of the group - * - * @param trackerServer the tracker server - * @param groupName the group name of storage server - * @return storage server stat array, return null if fail - */ - public StructStorageStat[] listStorages(TrackerServer trackerServer, String groupName) throws IOException, MyException { - final String storageIpAddr = null; - return this.listStorages(trackerServer, groupName, storageIpAddr); - } - - /** - * query storage server stat info of the group - * - * @param trackerServer the tracker server - * @param groupName the group name of storage server - * @param storageIpAddr the storage server ip address, can be null or empty - * @return storage server stat array, return null if fail - */ - public StructStorageStat[] listStorages(TrackerServer trackerServer, - String groupName, String storageIpAddr) throws IOException, MyException { - byte[] header; - byte[] bGroupName; - byte[] bs; - int len; - Connection connection = getConnection(trackerServer); - OutputStream out = connection.getOutputStream(); - - try { - bs = groupName.getBytes(ClientGlobal.g_charset); - bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; - - if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) { - len = bs.length; - } else { - len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; - } - Arrays.fill(bGroupName, (byte) 0); - System.arraycopy(bs, 0, bGroupName, 0, len); - - int ipAddrLen; - byte[] bIpAddr; - if (storageIpAddr != null && storageIpAddr.length() > 0) { - bIpAddr = storageIpAddr.getBytes(ClientGlobal.g_charset); - if (bIpAddr.length < ProtoCommon.FDFS_IPADDR_SIZE) { - ipAddrLen = bIpAddr.length; - } else { - ipAddrLen = ProtoCommon.FDFS_IPADDR_SIZE - 1; - } - } else { - bIpAddr = null; - ipAddrLen = 0; - } - - header = ProtoCommon.packHeader(ProtoCommon.TRACKER_PROTO_CMD_SERVER_LIST_STORAGE, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ipAddrLen, (byte) 0); - byte[] wholePkg = new byte[header.length + bGroupName.length + ipAddrLen]; - System.arraycopy(header, 0, wholePkg, 0, header.length); - System.arraycopy(bGroupName, 0, wholePkg, header.length, bGroupName.length); - if (ipAddrLen > 0) { - System.arraycopy(bIpAddr, 0, wholePkg, header.length + bGroupName.length, ipAddrLen); - } - out.write(wholePkg); - - ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.TRACKER_PROTO_CMD_RESP, -1); - this.errno = pkgInfo.errno; - if (pkgInfo.errno != 0) { - return null; - } - - ProtoStructDecoder decoder = new ProtoStructDecoder(); - return decoder.decode(pkgInfo.body, StructStorageStat.class, StructStorageStat.getFieldsTotalSize()); - } catch (IOException ex) { - try { - connection.close(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } - - throw ex; - } catch (Exception ex) { - ex.printStackTrace(); - this.errno = ProtoCommon.ERR_NO_EINVAL; - return null; - } finally { - if (connection != null) { - try { - connection.release(); - } catch (IOException ex1) { - ex1.printStackTrace(); - } - } - } - } - - /** - * delete a storage server from the tracker server - * - * @param trackerServer the connected tracker server - * @param groupName the group name of storage server - * @param storageIpAddr the storage server ip address - * @return true for success, false for fail - */ - private boolean deleteStorage(TrackerServer trackerServer, - String groupName, String storageIpAddr) throws IOException, MyException { - byte[] header; - byte[] bGroupName; - byte[] bs; - int len; - Connection connection; - - connection = trackerServer.getConnection(); - OutputStream out = connection.getOutputStream(); - - try { - bs = groupName.getBytes(ClientGlobal.g_charset); - bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; - - if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) { - len = bs.length; - } else { - len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; - } - Arrays.fill(bGroupName, (byte) 0); - System.arraycopy(bs, 0, bGroupName, 0, len); - - int ipAddrLen; - byte[] bIpAddr = storageIpAddr.getBytes(ClientGlobal.g_charset); - if (bIpAddr.length < ProtoCommon.FDFS_IPADDR_SIZE) { - ipAddrLen = bIpAddr.length; - } else { - ipAddrLen = ProtoCommon.FDFS_IPADDR_SIZE - 1; - } - - header = ProtoCommon.packHeader(ProtoCommon.TRACKER_PROTO_CMD_SERVER_DELETE_STORAGE, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ipAddrLen, (byte) 0); - byte[] wholePkg = new byte[header.length + bGroupName.length + ipAddrLen]; - System.arraycopy(header, 0, wholePkg, 0, header.length); - System.arraycopy(bGroupName, 0, wholePkg, header.length, bGroupName.length); - System.arraycopy(bIpAddr, 0, wholePkg, header.length + bGroupName.length, ipAddrLen); - out.write(wholePkg); - - ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), - ProtoCommon.TRACKER_PROTO_CMD_RESP, 0); - this.errno = pkgInfo.errno; - return pkgInfo.errno == 0; - } catch (IOException e) { - try { - connection.close(); - } finally { - connection = null; - } - throw e; - } finally { - if (connection != null) { - connection.release(); - } - } - } - - /** - * delete a storage server from the global FastDFS cluster - * - * @param groupName the group name of storage server - * @param storageIpAddr the storage server ip address - * @return true for success, false for fail - */ - public boolean deleteStorage(String groupName, String storageIpAddr) throws IOException, MyException { - return this.deleteStorage(ClientGlobal.g_tracker_group, groupName, storageIpAddr); - } - - /** - * delete a storage server from the FastDFS cluster - * - * @param trackerGroup the tracker server group - * @param groupName the group name of storage server - * @param storageIpAddr the storage server ip address - * @return true for success, false for fail - */ - public boolean deleteStorage(TrackerGroup trackerGroup, - String groupName, String storageIpAddr) throws IOException, MyException { - int serverIndex; - int notFoundCount; - TrackerServer trackerServer; - - notFoundCount = 0; - for (serverIndex = 0; serverIndex < trackerGroup.tracker_servers.length; serverIndex++) { - try { - trackerServer = trackerGroup.getTrackerServer(serverIndex); - } catch (IOException ex) { - ex.printStackTrace(System.err); - this.errno = ProtoCommon.ECONNREFUSED; - return false; - } - StructStorageStat[] storageStats = listStorages(trackerServer, groupName, storageIpAddr); - if (storageStats == null) { - if (this.errno == ProtoCommon.ERR_NO_ENOENT) { - notFoundCount++; - } else { - return false; - } - } else if (storageStats.length == 0) { - notFoundCount++; - } else if (storageStats[0].getStatus() == ProtoCommon.FDFS_STORAGE_STATUS_ONLINE || - storageStats[0].getStatus() == ProtoCommon.FDFS_STORAGE_STATUS_ACTIVE) { - this.errno = ProtoCommon.ERR_NO_EBUSY; - return false; - } - - } - - if (notFoundCount == trackerGroup.tracker_servers.length) { - this.errno = ProtoCommon.ERR_NO_ENOENT; - return false; - } - - notFoundCount = 0; - for (serverIndex = 0; serverIndex < trackerGroup.tracker_servers.length; serverIndex++) { - try { - trackerServer = trackerGroup.getTrackerServer(serverIndex); - } catch (IOException ex) { - System.err.println("connect to server " + trackerGroup.tracker_servers[serverIndex].getAddress().getHostAddress() + ":" + trackerGroup.tracker_servers[serverIndex].getPort() + " fail"); - ex.printStackTrace(System.err); - this.errno = ProtoCommon.ECONNREFUSED; - return false; - } - if (!this.deleteStorage(trackerServer, groupName, storageIpAddr)) { - if (this.errno != 0) { - if (this.errno == ProtoCommon.ERR_NO_ENOENT) { - notFoundCount++; - } else if (this.errno != ProtoCommon.ERR_NO_EALREADY) { - return false; - } - } - } - - } - - if (notFoundCount == trackerGroup.tracker_servers.length) { - this.errno = ProtoCommon.ERR_NO_ENOENT; - return false; - } - - if (this.errno == ProtoCommon.ERR_NO_ENOENT) { - this.errno = 0; - } - - return this.errno == 0; - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/TrackerGroup.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/TrackerGroup.java deleted file mode 100644 index 824814f..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/TrackerGroup.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - -import java.io.IOException; -import java.net.InetSocketAddress; - -/** - * Tracker server group - * - * @author Happy Fish / YuQing - * @version Version 1.17 - */ -public class TrackerGroup { - public int tracker_server_index; - public InetSocketAddress[] tracker_servers; - protected Integer lock; - - /** - * Constructor - * - * @param tracker_servers tracker servers - */ - public TrackerGroup(InetSocketAddress[] tracker_servers) { - this.tracker_servers = tracker_servers; - this.lock = new Integer(0); - this.tracker_server_index = 0; - } - - /** - * return connected tracker server - * - * @return connected tracker server, null for fail - */ - public TrackerServer getTrackerServer(int serverIndex) throws IOException { - return new TrackerServer(this.tracker_servers[serverIndex], serverIndex); - } - /** - * return connected tracker server - * - * @return connected tracker server, null for fail - */ - public TrackerServer getTrackerServer() throws IOException { - int current_index; - - synchronized (this.lock) { - this.tracker_server_index++; - if (this.tracker_server_index >= this.tracker_servers.length) { - this.tracker_server_index = 0; - } - - current_index = this.tracker_server_index; - } - - try { - return this.getTrackerServer(current_index); - } catch (IOException ex) { - System.err.println("connect to server " + this.tracker_servers[current_index].getAddress().getHostAddress() + ":" + this.tracker_servers[current_index].getPort() + " fail"); - ex.printStackTrace(System.err); - } - - for (int i = 0; i < this.tracker_servers.length; i++) { - if (i == current_index) { - continue; - } - - try { - TrackerServer trackerServer = this.getTrackerServer(i); - - synchronized (this.lock) { - if (this.tracker_server_index == current_index) { - this.tracker_server_index = i; - } - } - - return trackerServer; - } catch (IOException ex) { - System.err.println("connect to server " + this.tracker_servers[i].getAddress().getHostAddress() + ":" + this.tracker_servers[i].getPort() + " fail"); - ex.printStackTrace(System.err); - } - } - - return null; - } - - public Object clone() { - InetSocketAddress[] trackerServers = new InetSocketAddress[this.tracker_servers.length]; - for (int i = 0; i < trackerServers.length; i++) { - trackerServers[i] = new InetSocketAddress(this.tracker_servers[i].getAddress().getHostAddress(), this.tracker_servers[i].getPort()); - } - - return new TrackerGroup(trackerServers); - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/TrackerServer.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/TrackerServer.java deleted file mode 100644 index 364935e..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/TrackerServer.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - -import csource.common.MyException; -import csource.fastdfs.pool.Connection; -import csource.fastdfs.pool.ConnectionFactory; -import csource.fastdfs.pool.ConnectionPool; - -import java.io.IOException; -import java.net.InetSocketAddress; - -/** - * Tracker Server Info - * - * @author Happy Fish / YuQing - * @version Version 1.11 - */ -public class TrackerServer { - protected InetSocketAddress inetSockAddr; - - protected int index; - - - public TrackerServer(InetSocketAddress inetSockAddr) throws IOException { - this.inetSockAddr = inetSockAddr; - } - - public TrackerServer(InetSocketAddress inetSockAddr, int index) { - this.inetSockAddr = inetSockAddr; - this.index = index; - } - - public Connection getConnection() throws MyException, IOException { - Connection connection; - if (ClientGlobal.g_connection_pool_enabled) { - connection = ConnectionPool.getConnection(this.inetSockAddr); - } else { - connection = ConnectionFactory.create(this.inetSockAddr); - } - return connection; - } - - /** - * get the server info - * - * @return the server info - */ - public InetSocketAddress getInetSocketAddress() { - return this.inetSockAddr; - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/UploadCallback.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/UploadCallback.java deleted file mode 100644 index ae07754..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/UploadCallback.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (C) 2008 Happy Fish / YuQing - *

- * FastDFS Java Client may be copied only under the terms of the GNU Lesser - * General Public License (LGPL). - * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. - */ - -package csource.fastdfs; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * upload file callback interface - * - * @author Happy Fish / YuQing - * @version Version 1.0 - */ -public interface UploadCallback { - /** - * send file content callback function, be called only once when the file uploaded - * - * @param out output stream for writing file content - * @return 0 success, return none zero(errno) if fail - */ - public int send(OutputStream out) throws IOException; -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/UploadStream.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/UploadStream.java deleted file mode 100644 index eac5556..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/UploadStream.java +++ /dev/null @@ -1,55 +0,0 @@ -package csource.fastdfs; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Upload file by stream - * - * @author zhouzezhong & Happy Fish / YuQing - * @version Version 1.11 - */ -public class UploadStream implements UploadCallback { - private InputStream inputStream; //input stream for reading - private long fileSize = 0; //size of the uploaded file - - /** - * constructor - * - * @param inputStream input stream for uploading - * @param fileSize size of uploaded file - */ - public UploadStream(InputStream inputStream, long fileSize) { - super(); - this.inputStream = inputStream; - this.fileSize = fileSize; - } - - /** - * send file content callback function, be called only once when the file uploaded - * - * @param out output stream for writing file content - * @return 0 success, return none zero(errno) if fail - */ - public int send(OutputStream out) throws IOException { - long remainBytes = fileSize; - byte[] buff = new byte[256 * 1024]; - int bytes; - while (remainBytes > 0) { - try { - if ((bytes = inputStream.read(buff, 0, remainBytes > buff.length ? buff.length : (int) remainBytes)) < 0) { - return -1; - } - } catch (IOException ex) { - ex.printStackTrace(); - return -1; - } - - out.write(buff, 0, bytes); - remainBytes -= bytes; - } - - return 0; - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/pool/Connection.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/pool/Connection.java deleted file mode 100644 index 46652a0..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/pool/Connection.java +++ /dev/null @@ -1,140 +0,0 @@ -package csource.fastdfs.pool; - - -import csource.fastdfs.ClientGlobal; -import csource.fastdfs.ProtoCommon; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.Socket; - -public class Connection { - - private Socket sock; - private InetSocketAddress inetSockAddr; - private Long lastAccessTime = System.currentTimeMillis(); - - private boolean needActiveTest = false; - - public Connection(Socket sock, InetSocketAddress inetSockAddr) { - this.sock = sock; - this.inetSockAddr = inetSockAddr; - } - - /** - * get the server info - * - * @return the server info - */ - public InetSocketAddress getInetSocketAddress() { - return this.inetSockAddr; - } - - public OutputStream getOutputStream() throws IOException { - return this.sock.getOutputStream(); - } - - public InputStream getInputStream() throws IOException { - return this.sock.getInputStream(); - } - - public Long getLastAccessTime() { - return lastAccessTime; - } - - public void setLastAccessTime(Long lastAccessTime) { - this.lastAccessTime = lastAccessTime; - } - - /** - * @throws IOException - */ - public void close() throws IOException { - //if connection enabled get from connection pool - if (ClientGlobal.g_connection_pool_enabled) { - ConnectionPool.closeConnection(this); - } else { - this.closeDirectly(); - } - } - - public void release() throws IOException { - if (ClientGlobal.g_connection_pool_enabled) { - ConnectionPool.releaseConnection(this); - } else { - this.closeDirectly(); - } - } - - /** - * force close socket, - */ - public void closeDirectly() throws IOException { - if (this.sock != null) { - try { - ProtoCommon.closeSocket(this.sock); - } finally { - this.sock = null; - } - } - } - - public boolean activeTest() throws IOException { - if (this.sock == null) { - return false; - } - return ProtoCommon.activeTest(this.sock); - } - - public boolean isConnected() { - boolean isConnected = false; - if (sock != null) { - if (sock.isConnected()) { - isConnected = true; - } - } - return isConnected; - } - - public boolean isAvaliable() { - if (isConnected()) { - if (sock.getPort() == 0) { - return false; - } - if (sock.getInetAddress() == null) { - return false; - } - if (sock.getRemoteSocketAddress() == null) { - return false; - } - if (sock.isInputShutdown()) { - return false; - } - if (sock.isOutputShutdown()) { - return false; - } - return true; - } - return false; - } - - public boolean isNeedActiveTest() { - return needActiveTest; - } - - public void setNeedActiveTest(boolean needActiveTest) { - this.needActiveTest = needActiveTest; - } - - @Override - public String toString() { - return "Connection{" + - "sock=" + sock + - ", inetSockAddr=" + inetSockAddr + - ", lastAccessTime=" + lastAccessTime + - ", needActiveTest=" + needActiveTest + - '}'; - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/pool/ConnectionFactory.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/pool/ConnectionFactory.java deleted file mode 100644 index 48b9e82..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/pool/ConnectionFactory.java +++ /dev/null @@ -1,31 +0,0 @@ -package csource.fastdfs.pool; - -import csource.common.MyException; -import csource.fastdfs.ClientGlobal; -import org.csource.common.MyException; -import org.csource.fastdfs.ClientGlobal; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; - -public class ConnectionFactory { - /** - * create from InetSocketAddress - * - * @param socketAddress - * @return - * @throws IOException - */ - public static Connection create(InetSocketAddress socketAddress) throws MyException { - try { - Socket sock = new Socket(); - sock.setReuseAddress(true); - sock.setSoTimeout(ClientGlobal.g_network_timeout); - sock.connect(socketAddress, ClientGlobal.g_connect_timeout); - return new Connection(sock, socketAddress); - } catch (Exception e) { - throw new MyException("connect to server " + socketAddress.getAddress().getHostAddress() + ":" + socketAddress.getPort() + " fail, emsg:" + e.getMessage()); - } - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/pool/ConnectionManager.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/pool/ConnectionManager.java deleted file mode 100644 index e66c887..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/pool/ConnectionManager.java +++ /dev/null @@ -1,148 +0,0 @@ -package csource.fastdfs.pool; - -import csource.common.MyException; -import csource.fastdfs.ClientGlobal; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.LinkedList; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.ReentrantLock; - -public class ConnectionManager { - - private InetSocketAddress inetSocketAddress; - - /** - * total create connection pool - */ - private AtomicInteger totalCount = new AtomicInteger(); - - /** - * free connection count - */ - private AtomicInteger freeCount = new AtomicInteger(); - - /** - * lock - */ - private ReentrantLock lock = new ReentrantLock(true); - - private Condition condition = lock.newCondition(); - - /** - * free connections - */ - private LinkedList freeConnections = new LinkedList(); - - private ConnectionManager() { - - } - - public ConnectionManager(InetSocketAddress socketAddress) { - this.inetSocketAddress = socketAddress; - } - - public Connection getConnection() throws MyException { - lock.lock(); - try { - Connection connection = null; - while (true) { - if (freeCount.get() > 0) { - freeCount.decrementAndGet(); - connection = freeConnections.poll(); - if (!connection.isAvaliable() || (System.currentTimeMillis() - connection.getLastAccessTime()) > ClientGlobal.g_connection_pool_max_idle_time) { - closeConnection(connection); - continue; - } - if (connection.isNeedActiveTest()) { - boolean isActive = false; - try { - isActive = connection.activeTest(); - } catch (IOException e) { - System.err.println("send to server[" + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + "] active test error ,emsg:" + e.getMessage()); - isActive = false; - } - if (!isActive) { - closeConnection(connection); - continue; - } else { - connection.setNeedActiveTest(false); - } - } - } else if (ClientGlobal.g_connection_pool_max_count_per_entry == 0 || totalCount.get() < ClientGlobal.g_connection_pool_max_count_per_entry) { - connection = ConnectionFactory.create(this.inetSocketAddress); - totalCount.incrementAndGet(); - } else { - try { - if (condition.await(ClientGlobal.g_connection_pool_max_wait_time_in_ms, TimeUnit.MILLISECONDS)) { - //wait single success - continue; - } - throw new MyException("connect to server " + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + " fail, wait_time > " + ClientGlobal.g_connection_pool_max_wait_time_in_ms + "ms"); - } catch (InterruptedException e) { - e.printStackTrace(); - throw new MyException("connect to server " + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + " fail, emsg:" + e.getMessage()); - } - } - return connection; - } - } finally { - lock.unlock(); - } - } - - public void releaseConnection(Connection connection) { - if (connection == null) { - return; - } - lock.lock(); - try { - connection.setLastAccessTime(System.currentTimeMillis()); - freeConnections.add(connection); - freeCount.incrementAndGet(); - condition.signal(); - } finally { - lock.unlock(); - } - - } - - public void closeConnection(Connection connection) { - try { - if (connection != null) { - totalCount.decrementAndGet(); - connection.closeDirectly(); - } - } catch (IOException e) { - System.err.println("close socket[" + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + "] error ,emsg:" + e.getMessage()); - e.printStackTrace(); - } - } - - public void setActiveTestFlag() { - if (freeCount.get() > 0) { - lock.lock(); - try { - for (Connection freeConnection : freeConnections) { - freeConnection.setNeedActiveTest(true); - } - } finally { - lock.unlock(); - } - } - } - - - @Override - public String toString() { - return "ConnectionManager{" + - "ip:port='" + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + - ", totalCount=" + totalCount + - ", freeCount=" + freeCount + - ", freeConnections =" + freeConnections + - '}'; - } -} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/pool/ConnectionPool.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/pool/ConnectionPool.java deleted file mode 100644 index 0bfabfa..0000000 --- a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/csource/fastdfs/pool/ConnectionPool.java +++ /dev/null @@ -1,81 +0,0 @@ -package csource.fastdfs.pool; - -import csource.common.MyException; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class ConnectionPool { - /** - * key is ip:port, value is ConnectionManager - */ - private final static ConcurrentHashMap CP = new ConcurrentHashMap(); - - public static Connection getConnection(InetSocketAddress socketAddress) throws MyException { - if (socketAddress == null) { - return null; - } - String key = getKey(socketAddress); - ConnectionManager connectionManager; - connectionManager = CP.get(key); - if (connectionManager == null) { - synchronized (ConnectionPool.class) { - connectionManager = CP.get(key); - if (connectionManager == null) { - connectionManager = new ConnectionManager(socketAddress); - CP.put(key, connectionManager); - } - } - } - return connectionManager.getConnection(); - } - - public static void releaseConnection(Connection connection) throws IOException { - if (connection == null) { - return; - } - String key = getKey(connection.getInetSocketAddress()); - ConnectionManager connectionManager = CP.get(key); - if (connectionManager != null) { - connectionManager.releaseConnection(connection); - } else { - connection.closeDirectly(); - } - - } - - public static void closeConnection(Connection connection) throws IOException { - if (connection == null) { - return; - } - String key = getKey(connection.getInetSocketAddress()); - ConnectionManager connectionManager = CP.get(key); - if (connectionManager != null) { - connectionManager.closeConnection(connection); - connectionManager.setActiveTestFlag(); - } else { - connection.closeDirectly(); - } - } - - private static String getKey(InetSocketAddress socketAddress) { - if (socketAddress == null) { - return null; - } - return String.format("%s:%s", socketAddress.getAddress().getHostAddress(), socketAddress.getPort()); - } - - @Override - public String toString() { - if (!CP.isEmpty()) { - StringBuilder builder = new StringBuilder(); - for (Map.Entry managerEntry : CP.entrySet()) { - builder.append("key:[" + managerEntry.getKey() + " ]-------- entry:" + managerEntry.getValue() + "\n"); - } - return builder.toString(); - } - return null; - } -} diff --git a/module/aivfo-doc-spring-boot/aivfo-doc-spring-boot-autoconfigure/pom.xml b/module/aivfo-doc-spring-boot/aivfo-doc-spring-boot-autoconfigure/pom.xml new file mode 100644 index 0000000..31aff68 --- /dev/null +++ b/module/aivfo-doc-spring-boot/aivfo-doc-spring-boot-autoconfigure/pom.xml @@ -0,0 +1,19 @@ + + + + aivfo-doc-spring-boot + com.aivfo + 1.0.0-SNAPSHOT + + 4.0.0 + + aivfo-doc-spring-boot-autoconfigure + + + 11 + 11 + + + \ No newline at end of file diff --git a/module/aivfo-doc-spring-boot/aivfo-doc-spring-boot-start/pom.xml b/module/aivfo-doc-spring-boot/aivfo-doc-spring-boot-start/pom.xml new file mode 100644 index 0000000..9f44ed2 --- /dev/null +++ b/module/aivfo-doc-spring-boot/aivfo-doc-spring-boot-start/pom.xml @@ -0,0 +1,19 @@ + + + + aivfo-doc-spring-boot + com.aivfo + 1.0.0-SNAPSHOT + + 4.0.0 + + aivfo-doc-spring-boot-start + + + 11 + 11 + + + \ No newline at end of file diff --git a/module/aivfo-doc-spring-boot/pom.xml b/module/aivfo-doc-spring-boot/pom.xml new file mode 100644 index 0000000..886ffa9 --- /dev/null +++ b/module/aivfo-doc-spring-boot/pom.xml @@ -0,0 +1,18 @@ + + + + aivfo-model-parent + com.aivfo + 1.0.0-SNAPSHOT + ../../ability/aivfo-framework-build/aivfo-project-build/aivfo-model-parent/pom.xml + + 4.0.0 + aivfo-doc-spring-boot + pom + + aivfo-doc-spring-boot-autoconfigure + aivfo-doc-spring-boot-start + + \ No newline at end of file diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-autoconfigure/pom.xml b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-autoconfigure/pom.xml new file mode 100644 index 0000000..9108b2a --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-autoconfigure/pom.xml @@ -0,0 +1,39 @@ + + + + aivfo-kafka-spring-boot + com.aivfo + 1.0.0-SNAPSHOT + + 4.0.0 + aivfo-kafka-spring-boot-autoconfigure + + + com.aivfo + aivfo-kafka-spring-boot-core + + + org.springframework + spring-context + + + org.springframework.boot + spring-boot + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter + + + \ No newline at end of file diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-autoconfigure/src/main/java/com/aivfo/kafka/autoconfigure/KafkaAutoConfiguration.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-autoconfigure/src/main/java/com/aivfo/kafka/autoconfigure/KafkaAutoConfiguration.java new file mode 100644 index 0000000..43b493f --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-autoconfigure/src/main/java/com/aivfo/kafka/autoconfigure/KafkaAutoConfiguration.java @@ -0,0 +1,29 @@ +package com.aivfo.kafka.autoconfigure; + +import com.aivfo.kafka.KafkaFactory; +import com.aivfo.kafka.producer.ProducerNormal; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@Configuration +@EnableConfigurationProperties(KafkaAutoProperties.class) +@ConditionalOnProperty(prefix = KafkaAutoProperties.PREFIX, name = KafkaAutoProperties.ENABLE, matchIfMissing = true) +public class KafkaAutoConfiguration { + + @Bean + public KafkaFactory kafkaFactory(KafkaAutoProperties kafkaAutoProperties) { + KafkaFactory kafkaFactory = new KafkaFactory(kafkaAutoProperties.getProperties()); + return kafkaFactory; + } + + @Bean + @ConditionalOnProperty(prefix = KafkaAutoProperties.PREFIX, name = KafkaAutoProperties.PRODUCER_ENABLE_PREFIX, havingValue = "true") + public ProducerNormal producerNormal(KafkaAutoProperties kafkaAutoProperties) { + ProducerNormal producerNormal = new ProducerNormal(kafkaAutoProperties.getProperties()); + return producerNormal; + } + +} \ No newline at end of file diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-autoconfigure/src/main/java/com/aivfo/kafka/autoconfigure/KafkaAutoProperties.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-autoconfigure/src/main/java/com/aivfo/kafka/autoconfigure/KafkaAutoProperties.java new file mode 100644 index 0000000..f9558c9 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-autoconfigure/src/main/java/com/aivfo/kafka/autoconfigure/KafkaAutoProperties.java @@ -0,0 +1,39 @@ +package com.aivfo.kafka.autoconfigure; + +import com.aivfo.kafka.properties.KafkaProperties; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + + +/** + * @author: wangyl + * @date: 2023/4/19 + * @description: + */ +@ConfigurationProperties(prefix = KafkaAutoProperties.PREFIX) +@Data +public class KafkaAutoProperties { + /** + * PREFIX + */ + static final String PREFIX = "aivfo.kafka"; + /** + * 生产者启用 + */ + static final String PRODUCER_ENABLE_PREFIX = "properties.producer.enable"; + + /** + * 开启的常量 + */ + static final String ENABLE = "enable"; + /** + * 默认开启 + */ + private boolean enable = true; + + /** + * kafka配置 + */ + private KafkaProperties properties; + +} \ No newline at end of file diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..bc8a1f1 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.aivfo.kafka.autoconfigure.KafkaAutoConfiguration diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/pom.xml b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/pom.xml new file mode 100644 index 0000000..975b101 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/pom.xml @@ -0,0 +1,29 @@ + + + + aivfo-kafka-spring-boot + com.aivfo + 1.0.0-SNAPSHOT + + 4.0.0 + + 1.2.5 + + aivfo-kafka-spring-boot-core + + + org.apache.kafka + kafka-clients + + + com.google.protobuf + protobuf-java + + + com.google.protobuf + protobuf-java-util + + + \ No newline at end of file diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/KafkaFactory.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/KafkaFactory.java new file mode 100644 index 0000000..bfd518e --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/KafkaFactory.java @@ -0,0 +1,45 @@ +package com.aivfo.kafka; + +import com.aivfo.kafka.callback.OffsetCommit; +import com.aivfo.kafka.callback.ReceiveError; +import com.aivfo.kafka.callback.ReceiveMessage; +import com.aivfo.kafka.consumer.ConsumerManualCommit; +import com.aivfo.kafka.properties.KafkaProperties; + +import java.time.Duration; +import java.util.List; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: kafka工厂 + */ +public class KafkaFactory { + public KafkaFactory(KafkaProperties kafkaProperties) { + this.kafkaProperties = kafkaProperties; + } + + private KafkaProperties kafkaProperties; + + ConsumerManualCommit buildConsumerManualCommit(ReceiveMessage receiveMessage, String topic, OffsetCommit offsetCommit, ReceiveError receiveError, List partitions, Duration time) { + ConsumerManualCommit consumerManualCommit = new ConsumerManualCommit(kafkaProperties, receiveMessage, topic, offsetCommit, receiveError, partitions, time); + return consumerManualCommit; + } + + ConsumerManualCommit buildConsumerManualCommit(ReceiveMessage receiveMessage, String topic, ReceiveError receiveError, List partitions, Duration time) { + return this.buildConsumerManualCommit(receiveMessage, topic, null, receiveError, partitions, time); + } + + ConsumerManualCommit buildConsumerManualCommit(ReceiveMessage receiveMessage, String topic, ReceiveError receiveError, List partitions) { + return this.buildConsumerManualCommit(receiveMessage, topic, receiveError, partitions, null); + } + + ConsumerManualCommit buildConsumerManualCommit(ReceiveMessage receiveMessage, String topic, ReceiveError receiveError) { + return this.buildConsumerManualCommit(receiveMessage, topic, receiveError, null, null); + } + + ConsumerManualCommit buildConsumerManualCommit(ReceiveMessage receiveMessage, String topic) { + return this.buildConsumerManualCommit(receiveMessage, topic, null, null, null); + } + +} diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/callback/OffsetCommit.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/callback/OffsetCommit.java new file mode 100644 index 0000000..d54f723 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/callback/OffsetCommit.java @@ -0,0 +1,20 @@ +package com.aivfo.kafka.callback; + +import org.apache.kafka.clients.consumer.OffsetAndMetadata; +import org.apache.kafka.common.TopicPartition; + +import java.util.Map; + +@FunctionalInterface +public interface OffsetCommit { + /** + * offset 异步提交结果 + * + * @param offsets + * @param exception + * @return int 0 正常提交 非0 不提交 + * @Date 2023/5/15 + * @Author wangyl + */ + boolean onComplete(Map offsets, Exception exception); +} diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/callback/ReceiveError.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/callback/ReceiveError.java new file mode 100644 index 0000000..1a57fec --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/callback/ReceiveError.java @@ -0,0 +1,16 @@ +package com.aivfo.kafka.callback; + +import org.apache.kafka.clients.consumer.ConsumerRecord; + +@FunctionalInterface +public interface ReceiveError { + /** + * 消息处理异常 + * + * @param consumerRecords true 继续提交 false不提交 + * @return int + * @Date 2023/5/12 + * @Author wangyl + */ + boolean continueCommit(ConsumerRecord consumerRecords); +} diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/callback/ReceiveMessage.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/callback/ReceiveMessage.java new file mode 100644 index 0000000..87e4ec4 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/callback/ReceiveMessage.java @@ -0,0 +1,21 @@ +package com.aivfo.kafka.callback; + +import org.apache.kafka.clients.consumer.ConsumerRecord; + +/** + * @author: wangyl + * @date: 2023/5/12 + * @description: 收到消息的回调 + */ +@FunctionalInterface +public interface ReceiveMessage { + /** + * 收到消息的处理函数 + * + * @param consumerRecords + * @return java.lang.Boolean true 消息处理成功 false 失败 + * @Date 2023/5/15 + * @Author wangyl + */ + boolean receive(ConsumerRecord consumerRecords); +} diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/callback/SendMessage.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/callback/SendMessage.java new file mode 100644 index 0000000..95dfb12 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/callback/SendMessage.java @@ -0,0 +1,22 @@ +package com.aivfo.kafka.callback; + +import org.apache.kafka.clients.producer.RecordMetadata; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: 消息发送结果回调 + */ +@FunctionalInterface +public interface SendMessage { + /** + * 消息发送结果回调 + * + * @param metadata + * @param exception + * @return void + * @Date 2023/5/15 + * @Author wangyl + */ + void sendResult(RecordMetadata metadata, Exception exception); +} diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/consumer/ConsumerManualCommit.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/consumer/ConsumerManualCommit.java new file mode 100644 index 0000000..d15b6a8 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/consumer/ConsumerManualCommit.java @@ -0,0 +1,229 @@ +package com.aivfo.kafka.consumer; + +import com.aivfo.el.start.core.util.CollectionUtils; +import com.aivfo.el.starter.base.utils.StringUtils; +import com.aivfo.kafka.callback.OffsetCommit; +import com.aivfo.kafka.callback.ReceiveError; +import com.aivfo.kafka.callback.ReceiveMessage; +import com.aivfo.kafka.exception.KafkaFailedException; +import com.aivfo.kafka.properties.KafkaProperties; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.consumer.*; +import org.apache.kafka.common.TopicPartition; + +import java.time.Duration; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: 自动提交消费者 + */ +@Data +@Slf4j +public class ConsumerManualCommit extends Thread { + /** + * 收到消息的处理方式 + */ + private ReceiveMessage receiveMessage; + /** + * kafka配置 + */ + private KafkaProperties kafkaProperties; + /** + * 自动提交结果 + */ + private OffsetCommit offsetCommit; + /** + * 消息处理失败 + */ + private ReceiveError receiveError; + /** + * 主题 + */ + private String topic; + /** + * 分区 + */ + private List partitions; + /** + * 接收消息间隔时间 + */ + private Duration time; + /** + * 消费者 + */ + private Consumer consumer; + + + public ConsumerManualCommit(KafkaProperties kafkaProperties, ReceiveMessage receiveMessage, String topic, OffsetCommit offsetCommit, ReceiveError receiveError, List partitions, Duration time) { + this.receiveMessage = receiveMessage; + this.kafkaProperties = kafkaProperties; + this.offsetCommit = offsetCommit; + this.receiveError = receiveError; + this.topic = topic; + this.partitions = partitions; + this.time = Duration.ofMillis(1000); + if (Objects.nonNull(time)) { + this.time = time; + } + buildConsumer(); + } + + @Override + public void run() { + startConsumer(); + } + + /** + * 单独启动 + * + * @param + * @return void + * @Date 2023/5/15 + * @Author wangyl + */ + public void startConsumer() { + checkDate(); + if (CollectionUtils.isEmpty(partitions)) { + this.monitorTopic(); + } else { + this.monitorPartition(); + } + /** + * 需要提交的offset + */ + Map offsets = new HashMap<>(); + List> successMessage = new ArrayList<>(); + while (true) { + ConsumerRecords poll = consumer.poll(time); + try { + for (ConsumerRecord o : poll) { + Boolean success = receiveMessage.receive(o); + if (success) { + successMessage.add(o); + offsets.putAll(buildOffset(o)); + } else { + if (Objects.nonNull(receiveError)) { + Boolean continueCommit = receiveError.continueCommit(o); + if (continueCommit) { + offsets.putAll(buildOffset(o)); + } else { + throw new KafkaFailedException(); + } + } + } + } + } catch (Exception e) { + log.error("", e); + } finally { + commitOffset(offsets); + offsets.clear(); + successMessage.clear(); + } + } + } + + /** + * 检验数据 + * + * @param + * @return void + * @Date 2023/5/15 + * @Author wangyl + */ + private void checkDate() { + if (Objects.isNull(receiveMessage) || StringUtils.isEmpty(topic)) { + throw new KafkaFailedException(); + } + } + + /** + * 监听指定分区 + * + * @param + * @return void + * @Date 2023/5/15 + * @Author wangyl + */ + private void monitorPartition() { + /** + * 监听指定分区 + */ + List topicPartitionList = partitions.stream().map(partition -> { + TopicPartition topicPartition = new TopicPartition(topic, partition); + return topicPartition; + }).collect(Collectors.toList()); + consumer.assign(topicPartitionList); + } + + /** + * 监听所有分区 + * + * @param + * @return void + * @Date 2023/5/15 + * @Author wangyl + */ + private void monitorTopic() { + consumer.subscribe(Arrays.asList(topic)); + } + + /** + * 构建消费者 + * + * @param + * @return org.apache.kafka.clients.consumer.Consumer + * @Date 2023/5/15 + * @Author wangyl + */ + private Consumer buildConsumer() { + Properties consumerProperties = kafkaProperties.getConsumerProperties(); + /** + * 设置为手动提交 + */ + consumerProperties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); + consumer = new KafkaConsumer<>(consumerProperties); + return consumer; + } + + /** + * 提交指定offset + * + * @param offsets + * @return void + * @Date 2023/5/15 + * @Author wangyl + */ + private void commitOffset(Map offsets) { + if (CollectionUtils.isEmpty(offsets)) { + return; + } + consumer.commitAsync(offsets, (offsetsInfo, exception) -> { + if (exception != null) { + log.error("提交{},消息异常:{}", offsetsInfo, exception.getMessage()); + } else { + log.info("提交消息成功:{}", offsetsInfo); + } + if (Objects.nonNull(offsetCommit)) { + offsetCommit.onComplete(offsetsInfo, exception); + } + }); + } + + /** + * 构建需要提交的offset信息 + * + * @param data + * @return java.util.Map + * @Date 2023/5/15 + * @Author wangyl + */ + private Map buildOffset(ConsumerRecord data) { + Map offsets = new HashMap<>(); + offsets.put(new TopicPartition(data.topic(), data.partition()), new OffsetAndMetadata(data.offset() + 1)); + return offsets; + } +} \ No newline at end of file diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/exception/KafkaFailedException.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/exception/KafkaFailedException.java new file mode 100644 index 0000000..15ab1b0 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/exception/KafkaFailedException.java @@ -0,0 +1,36 @@ +package com.aivfo.kafka.exception; + +import com.aivfo.el.starter.base.exception.BasicException; + +/** + * @author: wangyl + * @date: 2023/4/14 + * @description: Kafka异常信息 + */ +public class KafkaFailedException extends BasicException { + + /** + * DEFAULT_MESSAGE + */ + private static final String DEFAULT_MESSAGE = "服务内部错误: "; + private static final long serialVersionUID = -4288134722183807545L; + + /** + * Basic exception + * + * @since 1.7.0 + */ + public KafkaFailedException() { + super(); + } + + /** + * Base exception + * + * @param describe describe + * @since 1.7.0 + */ + public KafkaFailedException(String describe) { + super(DEFAULT_MESSAGE + describe); + } +} diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/producer/ProducerNormal.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/producer/ProducerNormal.java new file mode 100644 index 0000000..173e974 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/producer/ProducerNormal.java @@ -0,0 +1,84 @@ +package com.aivfo.kafka.producer; + +import com.aivfo.kafka.callback.SendMessage; +import com.aivfo.kafka.properties.KafkaProperties; +import lombok.Data; +import org.apache.kafka.clients.producer.Callback; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; + +import java.util.Objects; +import java.util.Properties; + +/** + * @author: wangyl + * @date: 2023/5/15 + * @description: 通用生产者 + */ +public class ProducerNormal { + private KafkaProperties kafkaProperties; + private KafkaProducer producer; + + public ProducerNormal(KafkaProperties kafkaProperties) { + this.kafkaProperties = kafkaProperties; + Properties producerProperties = kafkaProperties.getProducerProperties(); + producer = new KafkaProducer(producerProperties); + } + + public void sendMessage(String topic, Integer partition, String key, byte[] message, SendMessage sendMessage) { + ProducerRecord record = new ProducerRecord(topic, partition, key, message); + producer.send(record, new SendCallback(sendMessage)); + } + + public void sendMessage(String topic, Integer partition, String key, byte[] message) { + ProducerRecord record = new ProducerRecord(topic, partition, key, message); + producer.send(record, new SendCallback(null)); + } + + public void sendMessage(String topic, Integer partition, byte[] message) { + ProducerRecord record = new ProducerRecord(topic, partition, null, message); + producer.send(record, new SendCallback(null)); + } + + public void sendMessage(String topic, Integer partition, byte[] message, SendMessage sendMessage) { + ProducerRecord record = new ProducerRecord(topic, partition, null, message); + producer.send(record, new SendCallback(sendMessage)); + } + + public void sendMessage(String topic, String key, byte[] message, SendMessage sendMessage) { + ProducerRecord record = new ProducerRecord(topic, null, key, message); + producer.send(record, new SendCallback(sendMessage)); + } + + public void sendMessage(String topic, String key, byte[] message) { + ProducerRecord record = new ProducerRecord(topic, null, key, message); + producer.send(record, new SendCallback(null)); + } + + public void sendMessage(String topic, byte[] message, SendMessage sendMessage) { + ProducerRecord record = new ProducerRecord(topic, message); + producer.send(record, new SendCallback(sendMessage)); + } + + public void sendMessage(String topic, byte[] message) { + ProducerRecord record = new ProducerRecord(topic, message); + producer.send(record, new SendCallback(null)); + } + + @Data + static class SendCallback implements Callback { + SendMessage sendMessage; + + SendCallback(SendMessage sendMessage) { + this.sendMessage = sendMessage; + } + + @Override + public void onCompletion(RecordMetadata metadata, Exception exception) { + if (Objects.nonNull(sendMessage)) { + sendMessage.sendResult(metadata, exception); + } + } + } +} diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/properties/KafkaProperties.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/properties/KafkaProperties.java new file mode 100644 index 0000000..e0e84cc --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-core/src/main/java/com/aivfo/kafka/properties/KafkaProperties.java @@ -0,0 +1,95 @@ +package com.aivfo.kafka.properties; + +import lombok.Data; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.protocol.types.Field; + +import java.util.Properties; + +/** + * @author: wangyl + * @date: 2023/5/11 + * @description: kafka配置 + */ +@Data +public class KafkaProperties { + + String ips; + Producer producer = new Producer(); + Consumer consumer = new Consumer(); + + @Data + static public class Producer { + Boolean enable = false; + /** + * 生产者序列化key的值 + */ + String keySerializerClass = "org.apache.kafka.common.serialization.StringSerializer"; + /** + * 生产者序列化value的值 + */ + String valueSerializerClass = "org.apache.kafka.common.serialization.ByteArraySerializer"; + } + + @Data + static public class Consumer { + /** + * 消费者 返序列化key的值 + */ + String keyDeserializerClass = "org.apache.kafka.common.serialization.StringDeserializer"; + /** + * 消费者 反序列化value的值 + */ + String valueDeserializerClass = "org.apache.kafka.common.serialization.ByteArrayDeserializer"; + /** + * 消费者分组 + */ + String group = "aivfo"; + } + + /** + * 获取生产者配置 + * + * @param + * @return java.util.Properties + * @Date 2023/5/12 + * @Author wangyl + */ + public Properties getProducerProperties() { + Properties properties = this.getCommonProperties(); + properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, this.getProducer().getKeySerializerClass()); + properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, this.getProducer().getValueSerializerClass()); + return properties; + } + + /** + * 获取消费者配置 + * + * @param + * @return java.util.Properties + * @Date 2023/5/12 + * @Author wangyl + */ + public Properties getConsumerProperties() { + Properties properties = this.getCommonProperties(); + properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, this.getConsumer().getKeyDeserializerClass()); + properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, this.getConsumer().getValueDeserializerClass()); + properties.put(ConsumerConfig.GROUP_ID_CONFIG, this.getConsumer().getGroup()); + return properties; + } + + /** + * 获取通用配置 + * + * @param + * @return java.util.Properties + * @Date 2023/5/12 + * @Author wangyl + */ + private Properties getCommonProperties() { + Properties commonProperties = new Properties(); + commonProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, this.ips); + return commonProperties; + } +} diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/pom.xml b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/pom.xml new file mode 100644 index 0000000..a80c195 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/pom.xml @@ -0,0 +1,18 @@ + + + + aivfo-kafka-spring-boot + com.aivfo + 1.0.0-SNAPSHOT + + 4.0.0 + aivfo-kafka-spring-boot-starter + + + com.aivfo + aivfo-kafka-spring-boot-autoconfigure + + + \ No newline at end of file diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/java/com/aivfo/kafka/KafkaTestApplication.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/java/com/aivfo/kafka/KafkaTestApplication.java new file mode 100644 index 0000000..e0f7468 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/java/com/aivfo/kafka/KafkaTestApplication.java @@ -0,0 +1,36 @@ +package com.aivfo.kafka; + +import com.aivfo.kafka.callback.ReceiveMessageInfo; +import com.aivfo.kafka.callback.Testwyl; +import com.aivfo.kafka.consumer.ConsumerManualCommit; +import com.aivfo.kafka.producer.ProducerNormal; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.PropertySource; + +@SpringBootTest(classes = KafkaTestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) +@PropertySource(value = "classpath:application.properties") +@ComponentScan(basePackages = "com.aivfo.kafka") +public class KafkaTestApplication { + @Autowired + KafkaFactory kafkaFactory; + @Autowired + ProducerNormal producerNormal; + @Autowired + Testwyl test; + + @Test + public void buildConsumerManualCommit() throws InterruptedException { + ConsumerManualCommit wyl = kafkaFactory.buildConsumerManualCommit(new ReceiveMessageInfo(), "wyl_topic"); + wyl.start(); + Thread.sleep(100000); + } + + @Test + public void sendMessage() { + producerNormal.sendMessage("wyl_topic", "wyl".getBytes()); + } + +} diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/java/com/aivfo/kafka/callback/OffsetCommitInfo.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/java/com/aivfo/kafka/callback/OffsetCommitInfo.java new file mode 100644 index 0000000..7de6a03 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/java/com/aivfo/kafka/callback/OffsetCommitInfo.java @@ -0,0 +1,22 @@ +package com.aivfo.kafka.callback; + +import org.apache.kafka.clients.consumer.OffsetAndMetadata; +import org.apache.kafka.common.TopicPartition; + +import java.util.Map; + +public class OffsetCommitInfo implements OffsetCommit { + /** + * offset 异步提交结果 + * + * @param offsets + * @param exception + * @return int 0 正常提交 非0 不提交 + * @Date 2023/5/15 + * @Author wangyl + */ + @Override + public boolean onComplete(Map offsets, Exception exception) { + return true; + } +} diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/java/com/aivfo/kafka/callback/ReceiveMessageInfo.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/java/com/aivfo/kafka/callback/ReceiveMessageInfo.java new file mode 100644 index 0000000..ebe6477 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/java/com/aivfo/kafka/callback/ReceiveMessageInfo.java @@ -0,0 +1,21 @@ +package com.aivfo.kafka.callback; + + +import org.apache.kafka.clients.consumer.ConsumerRecord; + +public class ReceiveMessageInfo implements ReceiveMessage { + /** + * 收到消息的处理函数 + * + * @param consumerRecords + * @return java.lang.Boolean true 消息处理成功 false 失败 + * @Date 2023/5/15 + * @Author wangyl + */ + @Override + public boolean receive(ConsumerRecord consumerRecords) { + byte[] value = consumerRecords.value(); + System.out.println(value); + return true; + } +} diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/java/com/aivfo/kafka/callback/Testwyl.java b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/java/com/aivfo/kafka/callback/Testwyl.java new file mode 100644 index 0000000..7422579 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/java/com/aivfo/kafka/callback/Testwyl.java @@ -0,0 +1,10 @@ +package com.aivfo.kafka.callback; + +import org.springframework.stereotype.Component; + +@Component +public class Testwyl { + public void test1() { + System.out.println(1); + } +} diff --git a/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/resources/application.properties b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/resources/application.properties new file mode 100644 index 0000000..dc6e800 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/aivfo-kafka-spring-boot-starter/src/test/resources/application.properties @@ -0,0 +1,2 @@ +aivfo.kafka.properties.ips=192.168.31.89:9092 +aivfo.kafka.properties.producer.enable=true \ No newline at end of file diff --git a/module/aivfo-kafka-spring-boot/pom.xml b/module/aivfo-kafka-spring-boot/pom.xml new file mode 100644 index 0000000..4d5b117 --- /dev/null +++ b/module/aivfo-kafka-spring-boot/pom.xml @@ -0,0 +1,39 @@ + + + + aivfo-model-parent + com.aivfo + 1.0.0-SNAPSHOT + ../../ability/aivfo-framework-build/aivfo-project-build/aivfo-model-parent/pom.xml + + 4.0.0 + aivfo-kafka-spring-boot + pom + 1.0.0-SNAPSHOT + + aivfo-kafka-spring-boot-core + aivfo-kafka-spring-boot-autoconfigure + aivfo-kafka-spring-boot-starter + + + + + com.aivfo + aivfo-kafka-spring-boot-autoconfigure + ${project.version} + + + com.aivfo + aivfo-kafka-spring-boot-core + ${project.version} + + + com.aivfo + aivfo-kafka-spring-boot-starter + ${project.version} + + + + \ No newline at end of file diff --git a/module/aivfo-log-spring-boot/aivfo-log-spring-boot-core/src/main/resources/logback.xml b/module/aivfo-log-spring-boot/aivfo-log-spring-boot-core/src/main/resources/logback.xml index d07dbb7..6e0a08e 100644 --- a/module/aivfo-log-spring-boot/aivfo-log-spring-boot-core/src/main/resources/logback.xml +++ b/module/aivfo-log-spring-boot/aivfo-log-spring-boot-core/src/main/resources/logback.xml @@ -6,7 +6,8 @@ - %date{yyyy-MM-dd HH:mm:ss.SSS} java %-5level [%replace(%thread){'\s', '-'}] %logger{36} %X{traceId:-TraceId} %msg%n + %date{yyyy-MM-dd HH:mm:ss.SSS} java %-5level [%replace(%thread){'\s', '-'}] %logger{36} + %X{traceId:-TraceId} %msg%n utf8 @@ -27,7 +28,8 @@ 10GB - %date{HH:mm:ss.SSS} [%replace(%thread){'\s', '-'}] %-5level %logger{36} %X{trace.id:-TraceId} %msg%n + %date{HH:mm:ss.SSS} [%replace(%thread){'\s', '-'}] %-5level %logger{36} %X{trace.id:-TraceId} + %msg%n diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-autoconfigure/src/main/java/com/aivfo/mqtt/autoconfigure/MqttAutoProperties.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-autoconfigure/src/main/java/com/aivfo/mqtt/autoconfigure/MqttAutoProperties.java index e2544df..96041db 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-autoconfigure/src/main/java/com/aivfo/mqtt/autoconfigure/MqttAutoProperties.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-autoconfigure/src/main/java/com/aivfo/mqtt/autoconfigure/MqttAutoProperties.java @@ -11,17 +11,23 @@ import lombok.Data; /** * @author: wangyl * @date: 2023/4/19 - * @description: + * @description: */ @ConfigurationProperties(prefix = MqttAutoProperties.PREFIX) @Data public class MqttAutoProperties { - /** PREFIX */ + /** + * PREFIX + */ static final String PREFIX = "aivfo.mqtt"; - /** 默认开启 */ + /** + * 默认开启 + */ private boolean enable = true; - /** Clients */ + /** + * Clients + */ private List clients; } \ No newline at end of file diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-autoconfigure/src/main/java/com/aivfo/mqtt/autoconfigure/PayloadJacksonAutoConfiguration.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-autoconfigure/src/main/java/com/aivfo/mqtt/autoconfigure/PayloadJacksonAutoConfiguration.java index 4792886..4f74940 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-autoconfigure/src/main/java/com/aivfo/mqtt/autoconfigure/PayloadJacksonAutoConfiguration.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-autoconfigure/src/main/java/com/aivfo/mqtt/autoconfigure/PayloadJacksonAutoConfiguration.java @@ -1,6 +1,6 @@ package com.aivfo.mqtt.autoconfigure; -import com.aivfo.el.base.utils.AivfoJsonUtils; +import com.aivfo.el.starter.base.utils.JsonUtils; import com.aivfo.mqtt.core.converter.PayloadDeserialize; import com.aivfo.mqtt.core.converter.PayloadSerialize; import com.fasterxml.jackson.core.JsonGenerator; @@ -58,7 +58,7 @@ public class PayloadJacksonAutoConfiguration { @Order(1001) @ConditionalOnMissingBean(ObjectMapper.class) public ObjectMapper objectMapper() { - return AivfoJsonUtils.getCopyMapper(); + return JsonUtils.getCopyMapper(); } /** diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/pom.xml b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/pom.xml index cfe2a32..6c5b513 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/pom.xml +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/pom.xml @@ -18,11 +18,6 @@ org.eclipse.paho.client.mqttv3 ${mqttv3.version} - - com.aivfo - aivfo-element-base - 1.0.0-SNAPSHOT - com.fasterxml.jackson.core jackson-databind diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/annotation/MqttClient.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/annotation/MqttClient.java index b4d1a6d..71ef50c 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/annotation/MqttClient.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/annotation/MqttClient.java @@ -8,7 +8,7 @@ import java.lang.annotation.Target; /** * @author: wangyl * @date: 2023/4/17 - * @description: + * @description: */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/annotation/MqttInterceptor.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/annotation/MqttInterceptor.java index 89c1cbc..7ecb994 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/annotation/MqttInterceptor.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/annotation/MqttInterceptor.java @@ -5,7 +5,7 @@ import java.lang.annotation.*; /** * @author: wangyl * @date: 2023/4/17 - * @description: + * @description: */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/annotation/Payload.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/annotation/Payload.java index 805975d..f70b32d 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/annotation/Payload.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/annotation/Payload.java @@ -6,6 +6,7 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; + /** * @author wyl */ @@ -20,7 +21,6 @@ public @interface Payload { /** * 转换器 - * */ Class>[] converter() default {}; diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/callback/ConnectClientCallBack.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/callback/ConnectClientCallBack.java index eda87c8..c8e7c5d 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/callback/ConnectClientCallBack.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/callback/ConnectClientCallBack.java @@ -17,7 +17,9 @@ import org.eclipse.paho.client.mqttv3.MqttMessage; @Slf4j public class ConnectClientCallBack implements MqttCallbackExtended { - /** Client */ + /** + * Client + */ private Client client; /** @@ -62,7 +64,7 @@ public class ConnectClientCallBack implements MqttCallbackExtended { String clientId = this.client.getClientId(); log.warn("mqtt client:{},断开连接,尝试重新连接!", clientId); ConnectorTaskExecutor.submitReconnect(this.client, - this.client::connect); + this.client::connect); } /** @@ -75,14 +77,13 @@ public class ConnectClientCallBack implements MqttCallbackExtended { @Override public void messageArrived(String topic, MqttMessage message) { String clientId = this.client.getClientId(); - if(log.isDebugEnabled()) - { + if (log.isDebugEnabled()) { log.debug("clientId:{},接收到topic:{},消息执行处理逻辑。。", clientId, topic); } DefaultMqttMessageEntity mqttMessageEntity = DefaultMqttMessageEntity.builder() - .mqttMessage(message) - .client(this.client) - .topic(topic).build(); + .mqttMessage(message) + .client(this.client) + .topic(topic).build(); this.client.getConnector().processMessage(mqttMessageEntity); MqttClientContext.remove(); } diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/callback/MqttClientCallBackHolder.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/callback/MqttClientCallBackHolder.java index 13902f6..2c6e5e4 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/callback/MqttClientCallBackHolder.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/callback/MqttClientCallBackHolder.java @@ -1,6 +1,6 @@ package com.aivfo.mqtt.core.callback; -import com.aivfo.el.base.utils.AivfoStringPool; +import com.aivfo.el.starter.base.utils.StringPool; import com.aivfo.mqtt.core.annotation.MqttClient; import com.aivfo.mqtt.core.annotation.MqttSubscribe; import com.aivfo.mqtt.core.client.Client; @@ -26,10 +26,11 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; + /** * @author: wangyl * @date: 2023/4/17 - * @description: + * @description: */ @Slf4j public class MqttClientCallBackHolder implements EnvironmentAware { @@ -71,18 +72,18 @@ public class MqttClientCallBackHolder implements EnvironmentAware { */ public boolean messageArrived(String clientId, String topic, MqttMessage mqttMessage) { long count = methodHolders.stream() - .filter(paramsHolder -> paramsHolder.match(clientId, topic)) - .peek(paramsHolder -> paramsHolder.invoke(clientId, topic, mqttMessage)).count(); + .filter(paramsHolder -> paramsHolder.match(clientId, topic)) + .peek(paramsHolder -> paramsHolder.invoke(clientId, topic, mqttMessage)).count(); if (count <= 0) { try { MqttCallBackHandler mqttCallBackInterface = - Objects.requireNonNull(callBackHandlerMap.get(clientId), - String.format("mqtt" + " clientId:%s,处理类没有找到", clientId)); + Objects.requireNonNull(callBackHandlerMap.get(clientId), + String.format("mqtt" + " clientId:%s,处理类没有找到", clientId)); MqttClientMessage mqttClientMessage = MqttClientMessage.builder() - .clientId(clientId) - .payload(new String(mqttMessage.getPayload())) - .qos(mqttMessage.getQos()) - .topic(topic).build(); + .clientId(clientId) + .payload(new String(mqttMessage.getPayload())) + .qos(mqttMessage.getQos()) + .topic(topic).build(); mqttCallBackInterface.messageArrived(mqttClientMessage); } catch (Exception e) { log.error("消息处理失败:{}", e.getMessage()); @@ -109,22 +110,22 @@ public class MqttClientCallBackHolder implements EnvironmentAware { if (match) { //过滤掉不合理的topic,否则在进行订阅时topic不合理会导致客户端反复重连。订阅时,获取到真实的topic进行订阅(替换占位符) String[] topics = paramsHolder - .getTopics() - .stream() - .map(topicPair -> org.springframework.util.StringUtils.isEmpty(topicPair.getRealityTopic()) ? topicPair.getTopic() - : - topicPair.getRealityTopic()) - .filter(topic -> { - try { - //mqtt topic的合法性验证 - MqttTopic.validate(topic, true); - } catch (IllegalArgumentException e) { - log.error("topic不合理:{}", e.getMessage()); - return false; - } - return true; - }) - .toArray(String[]::new); + .getTopics() + .stream() + .map(topicPair -> org.springframework.util.StringUtils.isEmpty(topicPair.getRealityTopic()) ? topicPair.getTopic() + : + topicPair.getRealityTopic()) + .filter(topic -> { + try { + //mqtt topic的合法性验证 + MqttTopic.validate(topic, true); + } catch (IllegalArgumentException e) { + log.error("topic不合理:{}", e.getMessage()); + return false; + } + return true; + }) + .toArray(String[]::new); if (topics.length <= 0) { return; } @@ -136,11 +137,11 @@ public class MqttClientCallBackHolder implements EnvironmentAware { for (String topic : topics) { List paramsHolderTopics = paramsHolder.getTopics(); ParamsHolder.TopicPair topicPair = - paramsHolderTopics.stream().filter(t -> { - String realityTopic = t.getRealityTopic(); - String tTopic = t.getTopic(); - return (org.springframework.util.StringUtils.isEmpty(realityTopic) ? tTopic : realityTopic).equals(topic); - }).findFirst().orElse(null); + paramsHolderTopics.stream().filter(t -> { + String realityTopic = t.getRealityTopic(); + String tTopic = t.getTopic(); + return (org.springframework.util.StringUtils.isEmpty(realityTopic) ? tTopic : realityTopic).equals(topic); + }).findFirst().orElse(null); if (topicPair == null) { continue; } @@ -181,20 +182,20 @@ public class MqttClientCallBackHolder implements EnvironmentAware { String[] clients = annotation.clients(); if (clients.length <= 0) { MqttClient mqttClient = bean.getClass().getAnnotation(MqttClient.class); - clients = new String[] {parseConfig(mqttClient.value())}; + clients = new String[]{parseConfig(mqttClient.value())}; } int[] qos = annotation.qos(); String[] topics = annotation.value(); Assert.notEmpty(clients, "订阅客户端id不能为空"); Assert.notEmpty(topics, "topic不能为空"); ParamsHolder paramsHolder = ParamsHolder.builder() - .targetBean(bean) - .clients(Arrays.asList(clients)) - .topics(ParamsHolder.TopicPair.of(this, Arrays.asList(topics))) - .method(method) - .qos(qos) - .paramModels(ParamsHolder.ParamModel.of(method)) - .build(); + .targetBean(bean) + .clients(Arrays.asList(clients)) + .topics(ParamsHolder.TopicPair.of(this, Arrays.asList(topics))) + .method(method) + .qos(qos) + .paramModels(ParamsHolder.ParamModel.of(method)) + .build(); TopicType topicType = annotation.topicType(); List paramsHolderTopics = paramsHolder.getTopics(); paramsHolderTopics.forEach(topicPair -> { @@ -221,7 +222,7 @@ public class MqttClientCallBackHolder implements EnvironmentAware { */ public String parseConfig(String config) { Assert.notNull(config, "配置不能为空"); - if (config.startsWith(AivfoStringPool.DOLLAR_LEFT_BRACE)) { + if (config.startsWith(StringPool.DOLLAR_LEFT_BRACE)) { config = configurableEnvironment.resolvePlaceholders(config); } return config; diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/AbstractClient.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/AbstractClient.java index c68b53a..bfe4822 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/AbstractClient.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/AbstractClient.java @@ -14,7 +14,7 @@ import java.util.List; /** * @author: wangyl * @date: 2023/4/18 - * @description: + * @description: */ @Slf4j public abstract class AbstractClient implements ClientApi, Client { diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/ClientApi.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/ClientApi.java index 3aa8468..3a01cab 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/ClientApi.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/ClientApi.java @@ -7,7 +7,7 @@ import java.util.List; /** * @author: wangyl * @date: 2023/4/18 - * @description: + * @description: */ public interface ClientApi { diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/DefaultMqttClient.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/DefaultMqttClient.java index 8dffa3a..aa2a2ac 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/DefaultMqttClient.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/DefaultMqttClient.java @@ -55,7 +55,7 @@ public class DefaultMqttClient extends AbstractClient { * @since 1.0 */ public DefaultMqttClient(IMqttAsyncClient mqttAsyncClient, - Connector connector,MqttConnectOptions mqttConnectOptions) { + Connector connector, MqttConnectOptions mqttConnectOptions) { this.clientId = mqttAsyncClient.getClientId(); this.mqttAsyncClient = mqttAsyncClient; this.connector = connector; diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/MqttClientCreateFactory.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/MqttClientCreateFactory.java index 30bcb60..d7a0c8e 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/MqttClientCreateFactory.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/client/MqttClientCreateFactory.java @@ -72,7 +72,7 @@ public class MqttClientCreateFactory { */ public static Client createClient(String id, MqttConnectOptions options, Connector connector) { IMqttAsyncClient mqttClient = createMqttClient(id, options); - return new DefaultMqttClient(mqttClient, connector,options); + return new DefaultMqttClient(mqttClient, connector, options); } /** diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/common/MqttStartConstName.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/common/MqttStartConstName.java index f83b03f..8a15bb5 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/common/MqttStartConstName.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/common/MqttStartConstName.java @@ -3,19 +3,27 @@ package com.aivfo.mqtt.core.common; /** * @author: wangyl * @date: 2023/4/18 - * @description: + * @description: */ public class MqttStartConstName { - /** ENABLE */ + /** + * ENABLE + */ public static final String ENABLE = "enable"; - /** TOPIC */ + /** + * TOPIC + */ public static final String TOPIC = "topic"; - /** CLIENT_ID */ + /** + * CLIENT_ID + */ public static final String CLIENT_ID = "clientId"; - /** SYS_TOPIC */ + /** + * SYS_TOPIC + */ public static final String SYS_TOPIC = "$SYS"; } diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/common/TopicProcessor.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/common/TopicProcessor.java index 2befdc4..040ba15 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/common/TopicProcessor.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/common/TopicProcessor.java @@ -1,6 +1,6 @@ package com.aivfo.mqtt.core.common; -import com.aivfo.el.base.utils.AivfoStringPool; +import com.aivfo.el.starter.base.utils.StringPool; import lombok.extern.slf4j.Slf4j; /** @@ -36,9 +36,9 @@ public class TopicProcessor { * @since 2.1.0 */ private static String sys(String topic) { - boolean slashStartsWith = topic.startsWith(AivfoStringPool.SLASH); + boolean slashStartsWith = topic.startsWith(StringPool.SLASH); if (!slashStartsWith) { - topic = AivfoStringPool.SLASH + topic; + topic = StringPool.SLASH + topic; } boolean sysStartsWith = topic.startsWith(MqttStartConstName.SYS_TOPIC); if (sysStartsWith) { diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/common/TopicType.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/common/TopicType.java index 4cc8d3b..beb3085 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/common/TopicType.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/common/TopicType.java @@ -8,15 +8,23 @@ import lombok.Getter; @Getter public enum TopicType { - /** Sys topic type */ + /** + * Sys topic type + */ NORMAL(1, "普通主题"), - /** Sys topic type */ + /** + * Sys topic type + */ SYS(2, "系统主题"); - /** Value */ + /** + * Value + */ private final Integer value; - /** Desc */ + /** + * Desc + */ private final String desc; } diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/connector/ConnectorTaskExecutor.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/connector/ConnectorTaskExecutor.java index 83bf169..41f73e8 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/connector/ConnectorTaskExecutor.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/connector/ConnectorTaskExecutor.java @@ -17,13 +17,19 @@ import java.util.concurrent.atomic.AtomicInteger; @Slf4j public class ConnectorTaskExecutor { - /** SCHEDULED */ + /** + * SCHEDULED + */ public static final ScheduledExecutorService SCHEDULED = new ScheduledThreadPoolExecutor(2, (ThreadFactory) Thread::new); - /** 重新连结任务池 */ + /** + * 重新连结任务池 + */ public static final Map> RECONNECT_TASK = new ConcurrentHashMap<>(2); - /** 重新连接任务的次数 */ + /** + * 重新连接任务的次数 + */ public static final Map RECONNECT_COUNT = new ConcurrentHashMap<>(2); diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/connector/DefaultMqttConnector.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/connector/DefaultMqttConnector.java index ddae812..a737914 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/connector/DefaultMqttConnector.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/connector/DefaultMqttConnector.java @@ -25,19 +25,29 @@ import java.util.function.Supplier; @Slf4j public class DefaultMqttConnector implements DisposableBean, Connector, SmartLifecycle { - /** 保存mqtt客户端 */ + /** + * 保存mqtt客户端 + */ private Map mqttClientMap = new ConcurrentHashMap<>(); - /** 默认客户端 */ + /** + * 默认客户端 + */ private Supplier defaultClient; - /** mqtt的配置 */ + /** + * mqtt的配置 + */ private MqttProperties mqttProperties; - /** mqtt客户端处理器 主要用于拦截器的执行 */ + /** + * mqtt客户端处理器 主要用于拦截器的执行 + */ private MqttClientPostProcessor mqttStartPostProcessor; - /** 消息调用持有器 */ + /** + * 消息调用持有器 + */ private MqttClientCallBackHolder mqttClientCallBackHolder; /** @@ -95,8 +105,8 @@ public class DefaultMqttConnector implements DisposableBean, Connector, SmartLif @Override public boolean processMessage(IMqttMessage mqttMessage) { return this.getMqttClientCallBackHolder().messageArrived(mqttMessage.getClient().getClientId(), - mqttMessage.getTopic(), - mqttMessage.getMqttMessage()); + mqttMessage.getTopic(), + mqttMessage.getMqttMessage()); } /** diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/connector/MqttClientPostProcessor.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/connector/MqttClientPostProcessor.java index 117c25c..e90cbbe 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/connector/MqttClientPostProcessor.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/connector/MqttClientPostProcessor.java @@ -13,21 +13,28 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; + /** * @author: wangyl * @date: 2023/4/18 - * @description: + * @description: */ @Data public abstract class MqttClientPostProcessor { - /** Mqtt properties */ + /** + * Mqtt properties + */ private MqttProperties mqttProperties; - /** Mqtt client interceptors */ + /** + * Mqtt client interceptors + */ private Map> mqttClientInterceptors = new HashMap<>(); - /** Mqtt client call back holder */ + /** + * Mqtt client call back holder + */ private MqttClientCallBackHolder mqttClientCallBackHolder; /** diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/converter/MqttConverterService.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/converter/MqttConverterService.java index f51a1a9..f7c6428 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/converter/MqttConverterService.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/converter/MqttConverterService.java @@ -19,13 +19,15 @@ import java.util.Set; /** * @author: wangyl * @date: 2023/4/18 - * @description: + * @description: */ @Slf4j public class MqttConverterService extends GenericConversionService { - /** sharedInstance */ + /** + * sharedInstance + */ private static volatile MqttConverterService sharedInstance; /** diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/entity/DefaultMqttMessageEntity.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/entity/DefaultMqttMessageEntity.java index 1b7b37d..6694dc3 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/entity/DefaultMqttMessageEntity.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/entity/DefaultMqttMessageEntity.java @@ -13,10 +13,16 @@ import org.eclipse.paho.client.mqttv3.MqttMessage; @Data @Builder public class DefaultMqttMessageEntity implements IMqttMessage { - /** Topic */ + /** + * Topic + */ private String topic; - /** Client */ + /** + * Client + */ private Client client; - /** Mqtt message */ + /** + * Mqtt message + */ private MqttMessage mqttMessage; } diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/entity/MqttClientMessage.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/entity/MqttClientMessage.java index 6569103..d9dfa48 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/entity/MqttClientMessage.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/entity/MqttClientMessage.java @@ -17,18 +17,28 @@ import java.io.Serializable; @Builder public class MqttClientMessage implements Serializable { - /** serialVersionUID */ + /** + * serialVersionUID + */ private static final long serialVersionUID = 1128847949412401730L; - /** Topic */ + /** + * Topic + */ private String topic; - /** Client id */ + /** + * Client id + */ private String clientId; - /** Payload */ + /** + * Payload + */ private String payload; - /** Qos */ + /** + * Qos + */ private Integer qos; } diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/properties/MqttProperties.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/properties/MqttProperties.java index 974b4d5..cf547c8 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/properties/MqttProperties.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/properties/MqttProperties.java @@ -1,9 +1,8 @@ package com.aivfo.mqtt.core.properties; -import com.aivfo.el.base.utils.AivfoStringUtils; +import com.aivfo.el.starter.base.utils.StringUtils; import lombok.Data; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.springframework.context.annotation.Configuration; import java.util.List; import java.util.Map; @@ -18,9 +17,13 @@ import java.util.stream.Collectors; @Data public class MqttProperties { - /** 默认开启 */ + /** + * 默认开启 + */ private boolean enable = true; - /** Clients */ + /** + * Clients + */ private Map clients; /** @@ -32,7 +35,7 @@ public class MqttProperties { */ public MqttProperties(boolean enable, List clients) { this.clients = clients.stream().collect(Collectors.toMap(MqttConnectorProperties::getClientId, - m -> m)); + m -> m)); } /** @@ -44,7 +47,7 @@ public class MqttProperties { */ public MqttConnectorProperties getProperties(String clientId) { return this.getClients().compute(clientId, (key, value) -> - this.getClients().values().stream().findFirst().orElse(new MqttConnectorProperties())); + this.getClients().values().stream().findFirst().orElse(new MqttConnectorProperties())); } @@ -110,12 +113,11 @@ public class MqttProperties { /** * 填写了用户和密码 尝试使用用户密码链接 */ - if(AivfoStringUtils.hasText(mqttConnectorProperties.username)&&AivfoStringUtils.hasText(mqttConnectorProperties.password)) - { + if (StringUtils.hasText(mqttConnectorProperties.username) && StringUtils.hasText(mqttConnectorProperties.password)) { mqttConnectOptions.setUserName(mqttConnectorProperties.username); mqttConnectOptions.setPassword(mqttConnectorProperties.password.toCharArray()); } - mqttConnectOptions.setServerURIs(new String[] {mqttConnectorProperties.getUrl()}); + mqttConnectOptions.setServerURIs(new String[]{mqttConnectorProperties.getUrl()}); mqttConnectOptions.setConnectionTimeout(mqttConnectorProperties.connectionTimeout); mqttConnectOptions.setKeepAliveInterval(mqttConnectorProperties.keepAliveInterval); mqttConnectOptions.setCleanSession(mqttConnectorProperties.cleanSession); @@ -134,35 +136,65 @@ public class MqttProperties { */ @Data public static class MqttConnectorProperties { - /** Url */ + /** + * Url + */ private String url; - /** Client id */ + /** + * Client id + */ private String clientId; - /** 用户名 */ + /** + * 用户名 + */ private String username; - /** 密码 */ + /** + * 密码 + */ private String password; - /** 连接超时时间 */ + /** + * 连接超时时间 + */ private int connectionTimeout = MqttConnectOptions.CONNECTION_TIMEOUT_DEFAULT; - /** 默认推送qos */ + /** + * 默认推送qos + */ private int defaultPublishQos = 0; - /** 默认订阅qos */ + /** + * 默认订阅qos + */ private int defaultSubscribeQos = 0; - /** 最大重连等待时间 (秒) */ + /** + * 最大重连等待时间 (秒) + */ private Integer maxReconnectDelay = 5; - /** 最大重连次数 */ + /** + * 最大重连次数 + */ private Integer maxReconnectCount = 5; - /** 周期 */ + /** + * 周期 + */ private int keepAliveInterval = MqttConnectOptions.KEEP_ALIVE_INTERVAL_DEFAULT; - /** 是否清除会话 */ + /** + * 是否清除会话 + */ private boolean cleanSession = false; - /** 是否自动重连 */ + /** + * 是否自动重连 + */ private boolean automaticReconnect = true; - /** 发送超时时间 */ + /** + * 发送超时时间 + */ private int executorServiceTimeout = 1; - /** 默认消息超时时间,超过当前时间的消息不在接收 (秒) */ + /** + * 默认消息超时时间,超过当前时间的消息不在接收 (秒) + */ private Integer defaultMsgTimeout = 10; - /** 数据是否加密 */ + /** + * 数据是否加密 + */ private boolean encryption = false; } diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/provider/MqttClientContext.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/provider/MqttClientContext.java index 95a47d8..01c2136 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/provider/MqttClientContext.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/provider/MqttClientContext.java @@ -5,11 +5,13 @@ import org.eclipse.paho.client.mqttv3.IMqttAsyncClient; /** * @author: wangyl * @date: 2023/4/18 - * @description: + * @description: */ public class MqttClientContext { - /** I_MQTT_ASYNC_CLIENT_THREAD_LOCAL */ + /** + * I_MQTT_ASYNC_CLIENT_THREAD_LOCAL + */ public static final ThreadLocal I_MQTT_ASYNC_CLIENT_THREAD_LOCAL = new ThreadLocal<>(); /** diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/provider/MqttTopicMatcher.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/provider/MqttTopicMatcher.java index ad1e3ee..fa46552 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/provider/MqttTopicMatcher.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/provider/MqttTopicMatcher.java @@ -1,6 +1,6 @@ package com.aivfo.mqtt.core.provider; -import com.aivfo.el.base.utils.AivfoStringPool; +import com.aivfo.el.starter.base.utils.StringPool; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -29,22 +29,22 @@ public class MqttTopicMatcher { char clientChar = clientTopic.charAt(clientTopicPos); char mqttChar = mqttTopic.charAt(mqttTopicPos); //如果客户端订阅的 #号 直接返回 - if (AivfoStringPool.HASH.charAt(0) == clientChar) { + if (StringPool.HASH.charAt(0) == clientChar) { mqttTopicPos = mqttTopicLength; clientTopicPos = clientTopicLength; break; } //如果不是 / 开头直接返回 - if (AivfoStringPool.SLASH.charAt(0) == clientChar && AivfoStringPool.SLASH.charAt(0) != mqttChar) { + if (StringPool.SLASH.charAt(0) == clientChar && StringPool.SLASH.charAt(0) != mqttChar) { break; } //如果不是通配符的,并且字符还不相等的 - if (AivfoStringPool.PLUS.charAt(0) != clientChar && clientChar != mqttChar) { + if (StringPool.PLUS.charAt(0) != clientChar && clientChar != mqttChar) { break; } //匹配+号 - if (AivfoStringPool.PLUS.charAt(0) == clientChar) { - while (AivfoStringPool.SLASH.charAt(0) != mqttChar) { + if (StringPool.PLUS.charAt(0) == clientChar) { + while (StringPool.SLASH.charAt(0) != mqttChar) { ++mqttTopicPos; mqttChar = mqttTopic.charAt(mqttTopicPos); } @@ -65,8 +65,8 @@ public class MqttTopicMatcher { */ public static String validate(String topic) { Assert.notNull(topic, "mqtt topic not null"); - if (!topic.endsWith(AivfoStringPool.SLASH)) { - topic += AivfoStringPool.SLASH; + if (!topic.endsWith(StringPool.SLASH)) { + topic += StringPool.SLASH; } return topic; } diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/provider/ParamsHolder.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/provider/ParamsHolder.java index 98df634..bfe717b 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/provider/ParamsHolder.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/provider/ParamsHolder.java @@ -1,6 +1,6 @@ package com.aivfo.mqtt.core.provider; -import com.aivfo.el.base.utils.AivfoStringPool; +import com.aivfo.el.starter.base.utils.StringPool; import com.aivfo.mqtt.core.annotation.Payload; import com.aivfo.mqtt.core.callback.MqttClientCallBackHolder; import com.aivfo.mqtt.core.common.MqttStartConstName; @@ -27,22 +27,34 @@ import java.util.stream.Collectors; @SuppressWarnings("unchecked") public class ParamsHolder { - /** Target bean */ + /** + * Target bean + */ private Object targetBean; - /** Topics */ + /** + * Topics + */ private List topics; - /** Clients */ + /** + * Clients + */ private List clients; - /** Method */ + /** + * Method + */ private Method method; - /** Param models */ + /** + * Param models + */ private List paramModels; - /** Qos */ + /** + * Qos + */ private int[] qos; @@ -92,8 +104,8 @@ public class ParamsHolder { * @since 2.1.0 */ private Object[] transformParams(String clientId, - String topic, - MqttMessage mqttMessage) { + String topic, + MqttMessage mqttMessage) { return this.paramModels.stream().map(paramModel -> { Class type = paramModel.getType(); //处理paylod中数据 @@ -131,17 +143,29 @@ public class ParamsHolder { @Slf4j public static class ParamModel { - /** Sign */ + /** + * Sign + */ private boolean sign; //标记当前参数是否为消息内容 - /** Is require */ + /** + * Is require + */ private boolean isRequire; - /** Default value */ + /** + * Default value + */ private Object defaultValue; - /** Name */ + /** + * Name + */ private String name; - /** Type */ + /** + * Type + */ private Class type; - /** Converters */ + /** + * Converters + */ private LinkedList> converters; /** @@ -240,10 +264,14 @@ public class ParamsHolder { @Slf4j public static class TopicPair { - /** topic */ + /** + * topic + */ private String topic; - /** 真实topic值(如果不是占位符,那么这里就为空) */ + /** + * 真实topic值(如果不是占位符,那么这里就为空) + */ private String realityTopic; @@ -262,12 +290,12 @@ public class ParamsHolder { } return topics.stream().map(topic -> { TopicPairBuilder topicPairBuilder = TopicPair.builder().topic(topic); - boolean startsWith = topic.startsWith(AivfoStringPool.DOLLAR_LEFT_BRACE); + boolean startsWith = topic.startsWith(StringPool.DOLLAR_LEFT_BRACE); //如果是占位符那么 realityTopic 就不会为空 if (startsWith) { String value = mqttClientCallBackHolder.parseConfig(topic); topicPairBuilder - .realityTopic(value); + .realityTopic(value); } return topicPairBuilder.build(); }).collect(Collectors.toList()); diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/registrar/MqttCallBackRegistrar.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/registrar/MqttCallBackRegistrar.java index cfa5ae5..9e2e0ab 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/registrar/MqttCallBackRegistrar.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/registrar/MqttCallBackRegistrar.java @@ -19,7 +19,9 @@ import java.util.Set; @Data public class MqttCallBackRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware { - /** Resource loader */ + /** + * Resource loader + */ private ResourceLoader resourceLoader; /** @@ -51,8 +53,8 @@ public class MqttCallBackRegistrar implements ImportBeanDefinitionRegistrar, Res definitionScanner.setResourceLoader(resourceLoader); } definitionScanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> - metadataReader.getAnnotationMetadata().isAnnotated(MqttClient.class.getName()) || - metadataReader.getAnnotationMetadata().isAnnotated(MqttInterceptor.class.getName())); + metadataReader.getAnnotationMetadata().isAnnotated(MqttClient.class.getName()) || + metadataReader.getAnnotationMetadata().isAnnotated(MqttInterceptor.class.getName())); //获取到扫描的包路径 String packageName = ClassUtils.getPackageName(importingClassMetadata.getClassName()); definitionScanner.scan(packageName); @@ -60,7 +62,6 @@ public class MqttCallBackRegistrar implements ImportBeanDefinitionRegistrar, Res /** * 自定义实现注解扫描后续可增加逻辑 - * */ public static class MqttClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner { diff --git a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/registrar/MqttClientBeanPostProcessor.java b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/registrar/MqttClientBeanPostProcessor.java index dbaf7fa..0e19f7e 100644 --- a/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/registrar/MqttClientBeanPostProcessor.java +++ b/module/aivfo-mqtt-spring-boot/aivfo-mqtt-spring-boot-core/src/main/java/com/aivfo/mqtt/core/registrar/MqttClientBeanPostProcessor.java @@ -9,15 +9,18 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import java.lang.reflect.Method; + /** * @author: wangyl * @date: 2023/4/18 - * @description: + * @description: */ @AllArgsConstructor public class MqttClientBeanPostProcessor implements InstantiationAwareBeanPostProcessor { - /** Mqtt client call back holder */ + /** + * Mqtt client call back holder + */ private final MqttClientCallBackHolder mqttClientCallBackHolder; /** diff --git a/module/aivfo-web-spring-boot/pom.xml b/module/aivfo-web-spring-boot/pom.xml new file mode 100644 index 0000000..efb5ebf --- /dev/null +++ b/module/aivfo-web-spring-boot/pom.xml @@ -0,0 +1,14 @@ + + + + aivfo-dependencies-parent + com.aivfo + 1.0.0-SNAPSHOT + ../../ability/aivfo-framework-build/aivfo-dependencies-parent/pom.xml + + 4.0.0 + aivfo-web-spring-boot + pom + diff --git a/module/pom.xml b/module/pom.xml index 834b42a..d66a197 100644 --- a/module/pom.xml +++ b/module/pom.xml @@ -7,10 +7,33 @@ pom module stable - - aivfo-mqtt-spring-boot - aivfo-log-spring-boot - aivfo-dependencies-spring-boot-module - aivfo-dfs-client-spring-boot - + + aivfo-mqtt-spring-boot + aivfo-log-spring-boot + aivfo-dependencies-spring-boot-module + aivfo-dfs-client-spring-boot + aivfo-kafka-spring-boot + aivfo-web-spring-boot + aivfo-doc-spring-boot + + + + com.aivfo + aivfo-element-base + 1.0.0-SNAPSHOT + + + + + + nexus + releases + http://192.168.31.90:8081/repository/maven-releases/ + + + nexus + snapshots + http://192.168.31.90:8081/repository/maven-snapshots/ + + diff --git a/pom.xml b/pom.xml index 8be4746..4eca888 100644 --- a/pom.xml +++ b/pom.xml @@ -14,4 +14,16 @@ module + + + nexus + releases + http://192.168.31.90:8081/repository/maven-releases/ + + + nexus + snapshots + http://192.168.31.90:8081/repository/maven-snapshots/ + +