├── weixin
└── build.gradle
├── tools
├── src
│ ├── test
│ │ ├── resources
│ │ │ └── mustache
│ │ │ │ └── t1.mustache
│ │ └── java
│ │ │ └── cn
│ │ │ └── msjc
│ │ │ └── utils
│ │ │ ├── misc
│ │ │ └── BitToolsTest.java
│ │ │ └── ds
│ │ │ ├── string
│ │ │ └── mustache
│ │ │ │ └── MustacheToolsTest.java
│ │ │ └── json
│ │ │ └── JsonUtilsTest.java
│ └── main
│ │ └── java
│ │ └── cn
│ │ └── msjc
│ │ └── utils
│ │ ├── lang
│ │ ├── clone
│ │ │ ├── package-info.java
│ │ │ ├── EnhancedCloneable.java
│ │ │ ├── AutoCloneable.java
│ │ │ └── CloneRuntimeException.java
│ │ ├── annotation
│ │ │ ├── package-info.java
│ │ │ └── Alias.java
│ │ ├── ExceptionUtils.java
│ │ └── concurrent
│ │ │ └── FutureUtils.java
│ │ ├── bean
│ │ ├── HasId.java
│ │ ├── HasName.java
│ │ ├── HasTimeFields.java
│ │ ├── pageable
│ │ │ ├── PageNumBasedPageLink.java
│ │ │ └── PageNumBasedPageData.java
│ │ └── Response.java
│ │ ├── misc
│ │ ├── BitTools.java
│ │ └── i18n
│ │ │ └── phonenumber
│ │ │ └── PhoneNumberTools.java
│ │ ├── ds
│ │ ├── ObjectUtils.java
│ │ ├── string
│ │ │ ├── mustache
│ │ │ │ └── MustacheTools.java
│ │ │ └── RandomGenerator.java
│ │ ├── hash
│ │ │ └── HashUtils.java
│ │ ├── json
│ │ │ └── JsonUtils.java
│ │ └── date
│ │ │ └── LocalDateTimeUtil.java
│ │ ├── storage
│ │ └── file
│ │ │ └── SimpleFileUtils.java
│ │ └── system
│ │ └── CmdExecutor.java
└── build.gradle
├── aliyun
└── build.gradle
├── lombok.config
├── config
├── settings.zip
├── env.tpl
├── flyway-local.conf
├── spotbugs-exclude-filter.xml
├── checkstyle.xml
└── intellij-java-google-style.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── settings.gradle
├── .gitattributes
├── .github
└── ISSUE_TEMPLATE
│ ├── custom.md
│ ├── feature_request.md
│ └── bug_report.md
├── rm-misc.sh
├── LICENSE
├── gradle.properties
├── gradlew.bat
├── README.md
├── .gitignore
├── gradlew
└── header.svg
/weixin/build.gradle:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tools/src/test/resources/mustache/t1.mustache:
--------------------------------------------------------------------------------
1 | {{name}}, {{description}}
--------------------------------------------------------------------------------
/aliyun/build.gradle:
--------------------------------------------------------------------------------
1 | compile group: 'com.alipay.sdk', name: 'alipay-easysdk', version: '2.1.0'
--------------------------------------------------------------------------------
/lombok.config:
--------------------------------------------------------------------------------
1 | lombok.accessors.chain=true
2 | lombok.extern.findbugs.addSuppressFBWarnings = true
--------------------------------------------------------------------------------
/config/settings.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wx-chevalier/ms-java-commons/master/config/settings.zip
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/lang/clone/package-info.java:
--------------------------------------------------------------------------------
1 | /** 克隆封装 */
2 | package cn.msjc.utils.lang.clone;
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wx-chevalier/ms-java-commons/master/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/bean/HasId.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.bean;
2 |
3 | public interface HasId {
4 | T getId();
5 | }
6 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/lang/annotation/package-info.java:
--------------------------------------------------------------------------------
1 | /** 注解包,提供增强型注解和注解工具类 */
2 | package cn.msjc.utils.lang.annotation;
3 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/bean/HasName.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.bean;
2 |
3 | public interface HasName {
4 | String getName();
5 | }
6 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'ms-java-common-tools'
2 |
3 | // 公共 Tools
4 | include ':msjc-utils'
5 | project(':msjc-utils').projectDir = new File("$rootDir/msjc-utils")
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | #
2 | # https://help.github.com/articles/dealing-with-line-endings/
3 | #
4 | # These are explicitly windows files and should use crlf
5 | *.bat text eol=crlf
6 |
7 |
--------------------------------------------------------------------------------
/config/env.tpl:
--------------------------------------------------------------------------------
1 | # build-locally.sh
2 | export DISABLE_TEST=
3 | export DISABLE_SPOTBUGS=
4 | export TAG=
5 |
6 | # deploy-locally.sh
7 | export RELEASE=
8 | export VALUES_FILE=
9 | export NAMESPACE=
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Custom issue template
3 | about: Describe this issue template's purpose here.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/config/flyway-local.conf:
--------------------------------------------------------------------------------
1 | flyway.url=jdbc:mysql://127.0.0.1:3306/test-db
2 | flyway.user=root
3 | flyway.defaultSchema =test-db
4 | flyway.schemas=test-db
5 | flyway.password=roottoor
6 | flyway.cleanDisabled=true
7 |
--------------------------------------------------------------------------------
/rm-misc.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | find . -name '*.project' -exec rm -rf {} \;
4 | find . -name '*.idea' -exec rm -rf {} \;
5 | find . -name '*.iml' -exec rm -rf {} \;
6 | find . -name 'bin' -exec rm -rf {} \;
7 | find . -name 'target' -exec rm -rf {} \;
8 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/bean/HasTimeFields.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.bean;
2 |
3 | import java.time.LocalDateTime;
4 |
5 | public interface HasTimeFields {
6 |
7 | LocalDateTime getCreatedAt();
8 |
9 | LocalDateTime getUpdatedAt();
10 | }
11 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/lang/clone/EnhancedCloneable.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.lang.clone;
2 |
3 | /**
4 | * 克隆支持接口
5 | *
6 | * @param 实现克隆接口的类型
7 | */
8 | public interface EnhancedCloneable extends java.lang.Cloneable {
9 |
10 | /**
11 | * 克隆当前对象,浅复制
12 | *
13 | * @return 克隆后的对象
14 | */
15 | T clone();
16 | }
17 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/lang/ExceptionUtils.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.lang;
2 |
3 | import java.io.PrintWriter;
4 | import java.io.StringWriter;
5 |
6 | public class ExceptionUtils {
7 |
8 | public static String getStackTrace(Throwable throwable) {
9 | StringWriter sw = new StringWriter();
10 | PrintWriter pw = new PrintWriter(sw, true);
11 | throwable.printStackTrace(pw);
12 | return sw.getBuffer().toString();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/lang/clone/AutoCloneable.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.lang.clone;
2 |
3 | import cn.hutool.core.clone.CloneRuntimeException;
4 |
5 | /**
6 | * 克隆支持类,提供默认的克隆方法
7 | *
8 | * @param
9 | */
10 | public class AutoCloneable implements Cloneable {
11 |
12 | @SuppressWarnings("unchecked")
13 | @Override
14 | public T clone() {
15 | try {
16 | return (T) super.clone();
17 | } catch (CloneNotSupportedException e) {
18 | throw new CloneRuntimeException(e);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/bean/pageable/PageNumBasedPageLink.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.bean.pageable;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import lombok.Data;
5 | import lombok.RequiredArgsConstructor;
6 |
7 | @Data
8 | @RequiredArgsConstructor
9 | public class PageNumBasedPageLink {
10 |
11 | // 0 页开始
12 | final int pageNum;
13 |
14 | // 每页大小
15 | final int pageSize;
16 |
17 | @JsonCreator
18 | public PageNumBasedPageLink nextPage() {
19 | return new PageNumBasedPageLink(getPageNum() + 1, getPageSize());
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/config/spotbugs-exclude-filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/lang/annotation/Alias.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.lang.annotation;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | /** 别名注解,使用此注解的字段、方法、参数等会有一个别名,用于Bean拷贝、Bean转Map等 */
10 | @Documented
11 | @Retention(RetentionPolicy.RUNTIME)
12 | @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
13 | public @interface Alias {
14 |
15 | /**
16 | * 别名值,即使用此注解要替换成的别名名称
17 | *
18 | * @return 别名值
19 | */
20 | String value();
21 | }
22 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/tools/src/test/java/cn/msjc/utils/misc/BitToolsTest.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.misc;
2 |
3 | import static org.junit.jupiter.api.Assertions.assertEquals;
4 |
5 | import com.google.common.collect.Sets;
6 | import java.util.HashSet;
7 | import java.util.Set;
8 | import org.junit.jupiter.api.Test;
9 |
10 | class BitToolsTest {
11 |
12 | @Test
13 | void testBitString() {
14 | String[] strs = new String[] {"ABCD", "CDEF", "MMMM", "XXXX", "EE"};
15 | HashSet s1 = Sets.newHashSet("XXXX", "EE");
16 | String bitS = BitTools.convertToBitString(s1, strs);
17 | System.out.println(bitS);
18 | Set s2 = BitTools.convertFromBitString(bitS, strs);
19 | assertEquals(s1, s2);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/misc/BitTools.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.misc;
2 |
3 | import java.util.HashSet;
4 | import java.util.Set;
5 |
6 | public class BitTools {
7 |
8 | public static String convertToBitString(Set strings, String[] strArray) {
9 | StringBuilder sb = new StringBuilder();
10 | for (String s : strArray) {
11 | if (strings.contains(s)) {
12 | sb.append('1');
13 | } else {
14 | sb.append('0');
15 | }
16 | }
17 | return sb.toString();
18 | }
19 |
20 | public static Set convertFromBitString(String bitString, String[] strArray) {
21 | Set res = new HashSet<>();
22 | int i = 0;
23 | for (char ch : bitString.toCharArray()) {
24 | if (ch == '1') {
25 | res.add(strArray[i]);
26 | }
27 | i++;
28 | }
29 | return res;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/lang/clone/CloneRuntimeException.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.lang.clone;
2 |
3 | import cn.hutool.core.exceptions.ExceptionUtil;
4 | import cn.hutool.core.util.StrUtil;
5 |
6 | /** 克隆异常 */
7 | public class CloneRuntimeException extends RuntimeException {
8 | private static final long serialVersionUID = 6774837422188798989L;
9 |
10 | public CloneRuntimeException(Throwable e) {
11 | super(ExceptionUtil.getMessage(e), e);
12 | }
13 |
14 | public CloneRuntimeException(String message) {
15 | super(message);
16 | }
17 |
18 | public CloneRuntimeException(String messageTemplate, Object... params) {
19 | super(StrUtil.format(messageTemplate, params));
20 | }
21 |
22 | public CloneRuntimeException(String message, Throwable throwable) {
23 | super(message, throwable);
24 | }
25 |
26 | public CloneRuntimeException(Throwable throwable, String messageTemplate, Object... params) {
27 | super(StrUtil.format(messageTemplate, params), throwable);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/misc/i18n/phonenumber/PhoneNumberTools.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.misc.i18n.phonenumber;
2 |
3 | import com.google.i18n.phonenumbers.PhoneNumberUtil;
4 | import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
5 | import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
6 | import io.vavr.API;
7 | import io.vavr.control.Try;
8 |
9 | public class PhoneNumberTools {
10 |
11 | private static final PhoneNumberUtil UTIL = PhoneNumberUtil.getInstance();
12 |
13 | public static Try parse(String phoneNumber) {
14 | return API.Try(() -> UTIL.parse(phoneNumber, "CN"));
15 | }
16 |
17 | public static Try parse(String phoneNumber, String defaultRegion) {
18 | return API.Try(() -> UTIL.parse(phoneNumber, defaultRegion));
19 | }
20 |
21 | public static String format(PhoneNumber phoneNumber, PhoneNumberFormat phoneNumberFormat) {
22 | return UTIL.format(phoneNumber, phoneNumberFormat);
23 | }
24 |
25 | public static String formatE164(PhoneNumber phoneNumber) {
26 | return format(phoneNumber, PhoneNumberFormat.E164);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 王下邀月熊
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | springBootVersion=2.1.7.RELEASE
2 | mybatisPlusBootStarterVersion=3.2.0
3 | pahoMqttVersion=1.2.4
4 | guavaVersion=29.0-jre
5 | libphonenumberVersion=8.12.8
6 | slf4jVersion=1.7.30
7 | jetbrainsAnnotationVersion=19.0.0
8 | jupiterVersion=5.6.2
9 | hamcrestLibraryVersion=2.2
10 | findbugsAnnotationVersion=3.0.1
11 | lombokVersion=1.18.12
12 | javaJwtVersion=3.10.3
13 | validationApiVersion=2.0.1.Final
14 | swaggerVersion=1.5.22
15 | hsqldbVersion=2.5.0
16 | jacksonVersion=2.11.0
17 | orgJsonVersion=20200518
18 | mockitoVersion=3.3.3
19 | slf4jSimpleVersion=2.0.0-alpha1
20 | javaxMailVersion=1.6.2
21 | aliyunSdkOssVersion=3.5.0
22 | aliyunJavaSdkCoreVersion=4.1.0
23 | mysqlConnectorJavaVersion=8.0.20
24 | easyExcelVersion=2.1.4
25 | gexinVersion=4.1.0.5
26 | aliyunHitsdbClientVersion=0.2.6
27 | drillJdbcVersion=1.17.0
28 | akkaVersion=2.6.6
29 | wechatPaySdkVersion=3.7.8.B
30 | knifeDocVersion=2.0.2
31 | redissonVersion=3.13.0
32 | druidSpringBootStarterVersion=1.1.18
33 | micrometerRegistryPrometheusVersion=1.3.0
34 | vavrVersion=1.0.0-alpha-3
35 | modelmapperVersion=2.3.0
36 | mustacheJavaCompilerVersion=0.9.6
37 | hutoolVersion=5.5.1
--------------------------------------------------------------------------------
/tools/src/test/java/cn/msjc/utils/ds/string/mustache/MustacheToolsTest.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.ds.string.mustache;
2 |
3 | import static org.junit.jupiter.api.Assertions.assertEquals;
4 |
5 | import io.vavr.API;
6 | import org.junit.jupiter.api.Test;
7 |
8 | class MustacheToolsTest {
9 |
10 | @Test
11 | void test_mustache_template_file() {
12 | String res =
13 | MustacheTools.mustacheTemplateFile(
14 | "mustache/t1.mustache",
15 | API.Map("name", "msjc", "description", "Industrial Internet of things platform"));
16 |
17 | assertEquals("msjc, Industrial Internet of things platform", res);
18 | }
19 |
20 | @Test
21 | void test_mustache_template() {
22 | String res =
23 | MustacheTools.mustache(
24 | "{{name}}, {{description}}",
25 | API.Map("name", "msjc", "description", "Industrial Internet of things platform"));
26 |
27 | assertEquals("msjc, Industrial Internet of things platform", res);
28 | }
29 |
30 | @Test
31 | void test_mustache_lack_of_value() {
32 | String res = MustacheTools.mustache("{{name}}, {{description}}", API.Map("name", "msjc"));
33 | assertEquals("msjc, ", res);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/bean/pageable/PageNumBasedPageData.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.bean.pageable;
2 |
3 | import io.vavr.collection.List;
4 | import java.util.function.Function;
5 | import lombok.Data;
6 | import lombok.RequiredArgsConstructor;
7 | import org.jetbrains.annotations.Nullable;
8 |
9 | @Data
10 | @RequiredArgsConstructor
11 | public class PageNumBasedPageData {
12 |
13 | final List data;
14 | final PageNumBasedPageLink pageLink;
15 | final Integer totalPage;
16 | final Long totalElements;
17 |
18 | @Nullable
19 | public PageNumBasedPageLink getNextPageLink() {
20 | if (pageLink != null && pageLink.getPageNum() < totalPage) {
21 | return pageLink.nextPage();
22 | } else {
23 | return null;
24 | }
25 | }
26 |
27 | /**
28 | * 未分页数据
29 | *
30 | * @param data 所有数据
31 | */
32 | public PageNumBasedPageData(List data) {
33 | this.data = data;
34 | this.pageLink = new PageNumBasedPageLink(0, data.size());
35 | this.totalPage = 1;
36 | this.totalElements = (long) data.size();
37 | }
38 |
39 | public PageNumBasedPageData map(Function mapper) {
40 | return new PageNumBasedPageData<>(data.map(mapper), pageLink, totalPage, totalElements);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/ds/ObjectUtils.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.ds;
2 |
3 | import cn.hutool.core.util.NumberUtil;
4 | import java.math.BigDecimal;
5 | import java.util.Objects;
6 |
7 | /** 对象工具类,包括判空、克隆、序列化等操作 */
8 | public class ObjectUtils {
9 |
10 | /**
11 | * 比较两个对象是否相等,此方法是 {@link #equal(Object, Object)}的别名方法。
12 | * 相同的条件有两个,满足其一即可:
13 | *
14 | *
15 | * - obj1 == null && obj2 == null
16 | *
- obj1.equals(obj2)
17 | *
- 如果是 BigDecimal 比较,0 == obj1.compareTo(obj2)
18 | *
19 | *
20 | * @param obj1 对象1
21 | * @param obj2 对象2
22 | * @return 是否相等
23 | * @see #equal(Object, Object)
24 | */
25 | public static boolean equals(Object obj1, Object obj2) {
26 | return equal(obj1, obj2);
27 | }
28 |
29 | /**
30 | * 比较两个对象是否相等。
31 | * 相同的条件有两个,满足其一即可:
32 | *
33 | *
34 | * - obj1 == null && obj2 == null
35 | *
- obj1.equals(obj2)
36 | *
- 如果是BigDecimal比较,0 == obj1.compareTo(obj2)
37 | *
38 | *
39 | * @param obj1 对象1
40 | * @param obj2 对象2
41 | * @return 是否相等
42 | * @see Objects#equals(Object, Object)
43 | */
44 | public static boolean equal(Object obj1, Object obj2) {
45 | // 首先判断是否为 BigDecimal,如果是则进行模糊判断
46 | if (obj1 instanceof BigDecimal && obj2 instanceof BigDecimal) {
47 | return NumberUtil.equals((BigDecimal) obj1, (BigDecimal) obj2);
48 | }
49 |
50 | return Objects.equals(obj1, obj2);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/ds/string/mustache/MustacheTools.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.ds.string.mustache;
2 |
3 | import com.github.mustachejava.DefaultMustacheFactory;
4 | import com.github.mustachejava.Mustache;
5 | import com.github.mustachejava.MustacheFactory;
6 | import io.vavr.API;
7 | import io.vavr.collection.Map;
8 | import java.io.IOException;
9 | import java.io.StringReader;
10 | import java.io.StringWriter;
11 | import java.util.UUID;
12 |
13 | // https://mustache.github.io/
14 | // https://github.com/spullara/mustache.java
15 | public class MustacheTools {
16 | private static final MustacheFactory mf = new DefaultMustacheFactory();
17 |
18 | public static String mustacheTemplateFile(String templateFile, Object scope) {
19 | Mustache mustache = mf.compile(templateFile);
20 | StringWriter stringWriter = new StringWriter();
21 | mustache.execute(stringWriter, scope);
22 | return stringWriter.toString();
23 | }
24 |
25 | public static String mustacheTemplateFile(String templateFile, Map scopes) {
26 | return mustacheTemplateFile(templateFile, scopes.toJavaMap());
27 | }
28 |
29 | public static String mustache(String template, Object scope) {
30 | Mustache mustache = mf.compile(new StringReader(template), UUID.randomUUID().toString());
31 | StringWriter stringWriter = new StringWriter();
32 | mustache.execute(stringWriter, scope);
33 | return stringWriter.toString();
34 | }
35 |
36 | public static String mustache(String template, Map scopes) {
37 | return mustache(template, scopes.toJavaMap());
38 | }
39 |
40 | public static void main(String[] args) throws IOException {
41 | System.out.println(
42 | mustache(
43 | "{{name}}, {{description}}",
44 | API.Map(
45 | "name", "lotuc",
46 | "description", "programmer")));
47 | System.out.println(
48 | mustacheTemplateFile(
49 | "abc.mustache",
50 | API.Map(
51 | "hello", "damn",
52 | "world", "room",
53 | "he", "shitter",
54 | "it", "nobody")));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/ds/hash/HashUtils.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.ds.hash;
2 |
3 | import com.google.common.io.BaseEncoding;
4 | import com.google.common.util.concurrent.UncheckedExecutionException;
5 | import java.io.BufferedInputStream;
6 | import java.io.File;
7 | import java.io.FileInputStream;
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.nio.charset.StandardCharsets;
11 | import java.security.MessageDigest;
12 | import java.security.NoSuchAlgorithmException;
13 |
14 | public class HashUtils {
15 |
16 | /** @return hex string with upper case */
17 | public static String sha256(File file) {
18 | try (InputStream ins = new BufferedInputStream(new FileInputStream(file))) {
19 | MessageDigest md = MessageDigest.getInstance("SHA-256");
20 | byte[] buf = new byte[1024];
21 | int nRead = 0;
22 | while ((nRead = ins.read(buf)) != -1) {
23 | md.update(buf, 0, nRead);
24 | }
25 | return BaseEncoding.base16().encode(md.digest());
26 | } catch (IOException | NoSuchAlgorithmException e) {
27 | throw new UncheckedExecutionException(e);
28 | }
29 | }
30 |
31 | /** @return hex string with upper case */
32 | public static String md5(File file) {
33 | try (InputStream ins = new BufferedInputStream(new FileInputStream(file))) {
34 | MessageDigest md = MessageDigest.getInstance("MD5");
35 | byte[] buf = new byte[1024];
36 | int nRead = 0;
37 | while ((nRead = ins.read(buf)) != -1) {
38 | md.update(buf, 0, nRead);
39 | }
40 | return BaseEncoding.base16().encode(md.digest());
41 | } catch (IOException | NoSuchAlgorithmException e) {
42 | throw new UncheckedExecutionException(e);
43 | }
44 | }
45 |
46 | /** @return hex string with upper case */
47 | public static String sha256(String s) {
48 | try {
49 | MessageDigest md = MessageDigest.getInstance("SHA-256");
50 | return BaseEncoding.base16().encode(md.digest(s.getBytes(StandardCharsets.UTF_8)));
51 | } catch (NoSuchAlgorithmException e) {
52 | throw new UncheckedExecutionException(e);
53 | }
54 | }
55 |
56 | /** @return hex string with upper case */
57 | public static String md5(String s) {
58 | try {
59 | MessageDigest md = MessageDigest.getInstance("MD5");
60 | return BaseEncoding.base16().encode(md.digest(s.getBytes(StandardCharsets.UTF_8)));
61 | } catch (NoSuchAlgorithmException e) {
62 | throw new UncheckedExecutionException(e);
63 | }
64 | }
65 |
66 | /** @return hex string with upper case */
67 | public static String md5(byte[] bytes) {
68 | try {
69 | MessageDigest md = MessageDigest.getInstance("MD5");
70 | return BaseEncoding.base16().encode(md.digest(bytes));
71 | } catch (NoSuchAlgorithmException e) {
72 | throw new UncheckedExecutionException(e);
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/ds/string/RandomGenerator.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.ds.string;
2 |
3 | import java.math.BigDecimal;
4 | import java.math.RoundingMode;
5 | import java.util.Objects;
6 | import java.util.Random;
7 | import org.jetbrains.annotations.Nullable;
8 |
9 | public class RandomGenerator {
10 |
11 | private static final Random rand = new Random(System.currentTimeMillis());
12 |
13 | /** 注意此处移除1,i,I o o O等易混淆的符号 */
14 | private static final String VERIFY_CODES = "1234567890";
15 |
16 | public static String randomCode(int size) {
17 | int codesLen = VERIFY_CODES.length();
18 | StringBuilder verifyCode = new StringBuilder(size);
19 | for (int i = 0; i < size; i++) {
20 | verifyCode.append(VERIFY_CODES.charAt(rand.nextInt(codesLen - 1)));
21 | }
22 | return verifyCode.toString();
23 | }
24 |
25 | public static double randomRate() {
26 | // 生成随机比例
27 | double v = rand.nextDouble();
28 | // 随机正负号
29 | int prefixFlag = rand.nextInt() % 3 == 0 ? -1 : 1;
30 | BigDecimal b = new BigDecimal(prefixFlag * v);
31 | return b.setScale(2, RoundingMode.HALF_UP).doubleValue();
32 | }
33 |
34 | public static Long randomLong() {
35 | return rand.nextLong();
36 | }
37 |
38 | public static int randomInt(int bound) {
39 | return rand.nextInt(bound);
40 | }
41 |
42 | public static int randomInt() {
43 | return rand.nextInt();
44 | }
45 |
46 | public static Long randomLong(Integer start, Integer endExclusive) {
47 | return start + randomLong() % (endExclusive - start);
48 | }
49 |
50 | /**
51 | * 为指定Code添加校验位
52 | *
53 | * @apiNote 校验位和数据通过中划线分割
54 | * @apiNote 校验位计算公式(参考身份证号校验位算法): ∑((int)char * [index+1]) % 11
55 | */
56 | public static String appendCheckData(String randomStr) {
57 | if (randomStr == null || randomStr.length() == 0) {
58 | return "";
59 | }
60 | char[] chars = randomStr.toCharArray();
61 | int s = 0;
62 | for (int i = 0; i < chars.length; i++) {
63 | char c = chars[i];
64 | s += c * (i + 1);
65 | }
66 |
67 | String validateBitData = s % 11 == 10 ? "X" : String.valueOf(s % 11);
68 | return randomStr + "-" + validateBitData;
69 | }
70 |
71 | /**
72 | * @apiNote 支持校验格式为 20200409-476889-8 格式的单号
73 | * @apiNote 单号的校验位与数据位通过 - 分割
74 | */
75 | public static boolean validate(String code) {
76 | if (!hasText(code)) {
77 | return false;
78 | }
79 | String rawCode = code.substring(0, code.length() - 2);
80 | String checkCode = appendCheckData(rawCode);
81 | return Objects.equals(checkCode, code);
82 | }
83 |
84 | private static boolean hasText(@Nullable String str) {
85 | return (str != null && !str.isEmpty() && containsText(str));
86 | }
87 |
88 | private static boolean containsText(CharSequence str) {
89 | int strLen = str.length();
90 | for (int i = 0; i < strLen; i++) {
91 | if (!Character.isWhitespace(str.charAt(i))) {
92 | return true;
93 | }
94 | }
95 | return false;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/storage/file/SimpleFileUtils.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.storage.file;
2 |
3 | import static com.google.common.base.Preconditions.checkArgument;
4 | import static com.google.common.base.Preconditions.checkState;
5 |
6 | import com.google.common.io.BaseEncoding;
7 | import java.io.BufferedInputStream;
8 | import java.io.BufferedReader;
9 | import java.io.File;
10 | import java.io.FileInputStream;
11 | import java.io.IOException;
12 | import java.io.InputStreamReader;
13 | import java.io.UncheckedIOException;
14 | import java.nio.charset.StandardCharsets;
15 | import java.nio.file.Files;
16 | import java.util.Arrays;
17 | import lombok.extern.slf4j.Slf4j;
18 | import org.jetbrains.annotations.Nullable;
19 |
20 | @Slf4j
21 | public class SimpleFileUtils {
22 |
23 | private static final int BUFFER_SIZE = 3 * 1024;
24 |
25 | public static File createTempFile(String suffix) {
26 | try {
27 | return Files.createTempFile("", suffix).toFile();
28 | } catch (IOException e) {
29 | throw new UncheckedIOException(e);
30 | }
31 | }
32 |
33 | public static File createTempFile() {
34 | try {
35 | return Files.createTempFile("", "").toFile();
36 | } catch (IOException e) {
37 | throw new UncheckedIOException(e);
38 | }
39 | }
40 |
41 | public static File createTempDir(String prefix) {
42 | try {
43 | return Files.createTempDirectory(prefix).toFile();
44 | } catch (IOException e) {
45 | throw new UncheckedIOException(e);
46 | }
47 | }
48 |
49 | public static String base64EncodeFile(File inputFile) {
50 | try (BufferedInputStream in =
51 | new BufferedInputStream(new FileInputStream(inputFile), BUFFER_SIZE)) {
52 | BaseEncoding encoding = BaseEncoding.base64();
53 | StringBuilder result = new StringBuilder();
54 | byte[] chunk = new byte[BUFFER_SIZE];
55 | int len;
56 | while ((len = in.read(chunk)) == BUFFER_SIZE) {
57 | result.append(encoding.encode(chunk));
58 | }
59 | if (len > 0) {
60 | chunk = Arrays.copyOf(chunk, len);
61 | result.append(encoding.encode(chunk));
62 | }
63 | return result.toString();
64 | } catch (IOException e) {
65 | throw new UncheckedIOException(e);
66 | }
67 | }
68 |
69 | public static String readToString(File file) {
70 | try (BufferedReader br =
71 | new BufferedReader(
72 | new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
73 | StringBuilder content = new StringBuilder();
74 | String line;
75 | while (true) {
76 | line = br.readLine();
77 | if (line == null) {
78 | break;
79 | } else {
80 | content.append(line).append('\n');
81 | }
82 | }
83 | return content.toString();
84 | } catch (IOException e) {
85 | throw new UncheckedIOException(e);
86 | }
87 | }
88 |
89 | public static void ensureDir(File file) {
90 | if (file.exists()) {
91 | checkState(file.isDirectory(), "%s is not directory", file);
92 | } else {
93 | if (file.mkdirs()) {
94 | log.debug("mkdir {}", file);
95 | }
96 | }
97 | }
98 |
99 | @SuppressWarnings("UnstableApiUsage")
100 | public static void copyFile(File src, File dst) {
101 | try {
102 | com.google.common.io.Files.copy(src, dst);
103 | } catch (IOException e) {
104 | throw new UncheckedIOException(e);
105 | }
106 | }
107 |
108 | public static void deleteFileIfExists(@Nullable File file) {
109 | if (file == null || !file.exists()) {
110 | return;
111 | }
112 |
113 | checkArgument(file.isFile(), "not a file: %s", file);
114 | if (file.exists() && file.delete()) {
115 | log.debug("Removed file {}", file);
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/bean/Response.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.bean;
2 |
3 | import cn.msjc.utils.lang.ExceptionUtils;
4 | import com.fasterxml.jackson.annotation.JsonInclude;
5 | import com.fasterxml.jackson.annotation.JsonInclude.Include;
6 | import lombok.AllArgsConstructor;
7 | import lombok.Data;
8 | import lombok.NoArgsConstructor;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | @Data
12 | @JsonInclude(Include.NON_NULL)
13 | public class Response {
14 |
15 | private ResponseStatus status;
16 | private ResponseMeta meta;
17 | private ErrorMessage err;
18 | private T data;
19 |
20 | public static Response ok() {
21 | return new Response().setStatus(ResponseStatus.ok);
22 | }
23 |
24 | public static Response ok(T data) {
25 | return new Response().setStatus(ResponseStatus.ok).setData(data);
26 | }
27 |
28 | public static Response ok(T data, Pagination pagination) {
29 | return ok(data).setMeta(new ResponseMeta().setPagination(pagination));
30 | }
31 |
32 | public static Response ok(
33 | T data, Integer pageNum, Long totalElements, Integer totalPages) {
34 | return ok(data)
35 | .setMeta(
36 | new ResponseMeta().setPagination(new Pagination(pageNum, totalElements, totalPages)));
37 | }
38 |
39 | public static Response err(ErrorMessage err) {
40 | return new Response().setStatus(ResponseStatus.error).setErr(err);
41 | }
42 |
43 | public static Response err(String code, String reason) {
44 | return new Response().setStatus(ResponseStatus.error).setErr(new ErrorMessage(code, reason));
45 | }
46 |
47 | public enum ResponseStatus {
48 | ok,
49 | error
50 | }
51 |
52 | @Data
53 | @JsonInclude(Include.NON_NULL)
54 | public static class ResponseMeta {
55 |
56 | private Pagination pagination;
57 | }
58 |
59 | @Data
60 | @NoArgsConstructor
61 | @JsonInclude(Include.NON_NULL)
62 | public static class Pagination {
63 |
64 | private static final Pagination EMPTY = new Pagination(0, 0L, 0);
65 | public static String PAGE_NUMBER = "pageNum";
66 | public static String PAGE_SIZE = "pageSize";
67 | public static String ID_OFFSET = "idOffset";
68 | public static String ASC = "asc";
69 | public static String ORDER_BY = "orderBy";
70 | private Integer pageNum;
71 | private Long totalElements;
72 | private Integer totalPages;
73 | private String pageStart;
74 | private String nextPageStart;
75 |
76 | public Pagination(Integer pageNum, Long totalElements, Integer totalPages) {
77 | this.pageNum = pageNum;
78 | this.totalElements = totalElements;
79 | this.totalPages = totalPages;
80 | }
81 |
82 | public Pagination(String pageStart, String nextPageStart) {
83 | this.pageStart = pageStart;
84 | this.nextPageStart = nextPageStart;
85 | }
86 |
87 | public static Pagination empty() {
88 | return EMPTY;
89 | }
90 |
91 | public static Pagination singlePage(long totalElements) {
92 | return new Pagination(0, totalElements, 0);
93 | }
94 |
95 | public static Pagination singlePage(int totalElements) {
96 | return new Pagination(0, (long) totalElements, 0);
97 | }
98 | }
99 |
100 | @Data
101 | @AllArgsConstructor
102 | public static class ErrorMessage {
103 |
104 | private String code;
105 |
106 | private String reason;
107 |
108 | @Nullable private String exception;
109 |
110 | public ErrorMessage(String code, String reason) {
111 | this.code = code;
112 | this.reason = reason;
113 | this.exception = null;
114 | }
115 |
116 | public ErrorMessage(String code, String reason, @Nullable Throwable t) {
117 | this.code = code;
118 | this.reason = reason;
119 | if (t != null) {
120 | this.exception = ExceptionUtils.getStackTrace(t);
121 | }
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [![Contributors][contributors-shield]][contributors-url]
2 | [![Forks][forks-shield]][forks-url]
3 | [![Stargazers][stars-shield]][stars-url]
4 | [![Issues][issues-shield]][issues-url]
5 | [![MIT License][license-shield]][license-url]
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Demo
16 | ·
17 | 更多项目
18 | ·
19 | 参考资料
20 |
21 |
22 |
23 |
24 |
25 | # ms-java-common-tools
26 |
27 | # Usage
28 |
29 | ## IDE
30 |
31 | 安装 google-java-format 和 lombok 插件;在配置中 Editor | Code Style | scheme 中导入 `conf/intellij-java-google-style.xml` 风格配置。
32 |
33 | ## Publish
34 |
35 | 此发布指将 tools/ 下公共库发布到 Maven Central 等仓库,参考 [Bintray](https://reflectoring.io/guide-publishing-to-bintray-with-gradle/)。
36 |
37 | ```sh
38 | $ ./gradlew bintrayUpload -Dbintray.user= -Dbintray.key=
39 | ```
40 |
41 | # About
42 |
43 |
44 |
45 | ## Roadmap
46 |
47 | See the [open issues](https://github.com/wx-chevalier/ms-java-commons/issues) for a list of proposed features (and known issues).
48 |
49 |
50 |
51 | ## Contributing
52 |
53 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
54 |
55 | 1. Fork the Project
56 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
57 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
58 | 4. Push to the Branch (`git push origin feature/AmazingFeature`)
59 | 5. Open a Pull Request
60 |
61 |
62 |
63 | ## License
64 |
65 | Distributed under the MIT License. See `LICENSE` for more information.
66 |
67 |
68 |
69 | ## Acknowledgements
70 |
71 | - [Awesome-Lists](https://github.com/wx-chevalier/Awesome-Lists): 📚 Guide to Galaxy, curated, worthy and up-to-date links/reading list for ITCS-Coding/Algorithm/SoftwareArchitecture/AI. 💫 ITCS-编程/算法/软件架构/人工智能等领域的文章/书籍/资料/项目链接精选。
72 |
73 | - [Awesome-CS-Books](https://github.com/wx-chevalier/Awesome-CS-Books): :books: Awesome CS Books/Series(.pdf by git lfs) Warehouse for Geeks, ProgrammingLanguage, SoftwareEngineering, Web, AI, ServerSideApplication, Infrastructure, FE etc. :dizzy: 优秀计算机科学与技术领域相关的书籍归档。
74 |
75 | - [Hutool #Project#](https://www.hutool.cn/): A set of tools that keep Java sweet.
76 |
77 | - [freedom](https://github.com/8treenet/freedom): freedom 是一个基于六边形架构的框架,可以支撑充血的领域模型范式。
78 |
79 | ## Copyright & More | 延伸阅读
80 |
81 | 笔者所有文章遵循[知识共享 署名 - 非商业性使用 - 禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh),欢迎转载,尊重版权。您还可以前往 [NGTE Books](https://ng-tech.icu/books/) 主页浏览包含知识体系、编程语言、软件工程、模式与架构、Web 与大前端、服务端开发实践与工程架构、分布式基础架构、人工智能与深度学习、产品运营与创业等多类目的书籍列表:
82 |
83 | [](https://ng-tech.icu/books/)
84 |
85 |
86 |
87 |
88 | [contributors-shield]: https://img.shields.io/github/contributors/wx-chevalier/ms-java-commons.svg?style=flat-square
89 | [contributors-url]: https://github.com/wx-chevalier/ms-java-commons/graphs/contributors
90 | [forks-shield]: https://img.shields.io/github/forks/wx-chevalier/ms-java-commons.svg?style=flat-square
91 | [forks-url]: https://github.com/wx-chevalier/ms-java-commons/network/members
92 | [stars-shield]: https://img.shields.io/github/stars/wx-chevalier/ms-java-commons.svg?style=flat-square
93 | [stars-url]: https://github.com/wx-chevalier/ms-java-commons/stargazers
94 | [issues-shield]: https://img.shields.io/github/issues/wx-chevalier/ms-java-commons.svg?style=flat-square
95 | [issues-url]: https://github.com/wx-chevalier/ms-java-commons/issues
96 | [license-shield]: https://img.shields.io/github/license/wx-chevalier/ms-java-commons.svg?style=flat-square
97 | [license-url]: https://github.com/wx-chevalier/ms-java-commons/blob/master/LICENSE.txt
98 |
--------------------------------------------------------------------------------
/tools/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.jfrog.artifactory" version "4.17.1"
3 | id "com.jfrog.bintray" version "1.8.5"
4 | id "maven-publish"
5 | }
6 |
7 | dependencies {
8 | api "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}"
9 | api "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"
10 | api "com.fasterxml.jackson.core:jackson-annotations:${jacksonVersion}"
11 | api "com.fasterxml.jackson.module:jackson-module-parameter-names:${jacksonVersion}"
12 | api "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:${jacksonVersion}"
13 | api "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jacksonVersion}"
14 | api "com.googlecode.libphonenumber:libphonenumber:${libphonenumberVersion}"
15 |
16 | api "org.modelmapper:modelmapper:${modelmapperVersion}"
17 | api "com.github.spullara.mustache.java:compiler:${mustacheJavaCompilerVersion}"
18 |
19 | api "com.google.guava:guava:${guavaVersion}"
20 | api "org.slf4j:slf4j-api:${slf4jVersion}"
21 | }
22 |
23 | test {
24 | useJUnitPlatform()
25 | }
26 |
27 | // run gradle with "-Dsnapshot=true" to automatically append "-SNAPSHOT" to the version
28 | version = '0.0.1' + (Boolean.valueOf(System.getProperty("snapshot")) ? "-SNAPSHOT" : "")
29 | sourceCompatibility = 1.8
30 |
31 | ext{
32 | bintrayUser = System.getProperty("bintray.user")
33 | bintrayKey = System.getProperty("bintray.key")
34 | buildNumber = System.getProperty("build.number")
35 | }
36 |
37 |
38 | task sourcesJar(type: Jar, dependsOn: classes) {
39 | classifier = 'sources'
40 | from sourceSets.main.allSource
41 | }
42 |
43 | javadoc.failOnError = false
44 | task javadocJar(type: Jar, dependsOn: javadoc) {
45 | classifier = 'javadoc'
46 | from javadoc.destinationDir
47 | }
48 |
49 | artifacts {
50 | archives sourcesJar
51 | archives javadocJar
52 | }
53 |
54 | def pomConfig = {
55 | licenses {
56 | license {
57 | name "The Apache Software License, Version 2.0"
58 | url "http://www.apache.org/licenses/LICENSE-2.0.txt"
59 | distribution "repo"
60 | }
61 | }
62 | developers {
63 | developer {
64 | id "wx-chevalier"
65 | name "Chevalier"
66 | email "384924552@qq.com"
67 | }
68 | }
69 |
70 | scm {
71 | url "https://github.com/wx-chevalier/msjc-utils"
72 | }
73 | }
74 |
75 | publishing {
76 | publications {
77 | mavenPublication(MavenPublication) {
78 | from components.java
79 | artifact sourcesJar {
80 | classifier "sources"
81 | }
82 | artifact javadocJar {
83 | classifier "javadoc"
84 | }
85 | groupId 'cn.msjc.utils'
86 | artifactId 'msjc-utils'
87 | version project.version
88 | pom.withXml {
89 | def root = asNode()
90 | root.appendNode('description', 'Java Commons & Boilerplates')
91 | root.appendNode('name', 'ms-java-common')
92 | root.appendNode('url', 'https://github.com/wx-chevalier/msjc-utils')
93 | root.children().last() + pomConfig
94 | }
95 | }
96 | }
97 | }
98 |
99 | artifactory {
100 | contextUrl = 'http://oss.jfrog.org'
101 | publish {
102 | repository {
103 | repoKey = 'oss-snapshot-local'
104 | username = bintrayUser
105 | password = bintrayKey
106 | }
107 | defaults {
108 | publications('mavenPublication')
109 | publishArtifacts = true
110 | publishPom = true
111 | properties = [
112 | 'build.number': buildNumber,
113 | 'build.name': 'msjc-utils'
114 | ]
115 | }
116 | }
117 | resolve {
118 | repoKey = 'jcenter'
119 | }
120 | clientConfig.info.setBuildNumber(buildNumber)
121 | clientConfig.info.setBuildName('msjc-utils')
122 | }
123 |
124 | bintray {
125 | user = bintrayUser
126 | key = bintrayKey
127 | publications = ['mavenPublication']
128 |
129 | pkg {
130 | userOrg = 'ms-java-commons'
131 | repo = 'msjc-utils'
132 | name = 'msjc-utils'
133 | licenses = ['Apache-2.0']
134 | vcsUrl = 'https://github.com/wx-chevalier/msjc-utils'
135 | version {
136 | name = project.version
137 | desc = "build ${buildNumber}"
138 | released = new Date()
139 | gpg {
140 | sign = true
141 | }
142 | }
143 | }
144 |
145 | publish = true
146 | }
147 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/vim,macos,emacs,gradle,intellij+all
2 | # Edit at https://www.gitignore.io/?templates=vim,macos,emacs,gradle,intellij+all
3 |
4 | ### Emacs ###
5 | # -*- mode: gitignore; -*-
6 | *~
7 | \#*\#
8 | /.emacs.desktop
9 | /.emacs.desktop.lock
10 | *.elc
11 | auto-save-list
12 | tramp
13 | .\#*
14 |
15 | # Org-mode
16 | .org-id-locations
17 | *_archive
18 |
19 | # flymake-mode
20 | *_flymake.*
21 |
22 | # eshell files
23 | /eshell/history
24 | /eshell/lastdir
25 |
26 | # elpa packages
27 | /elpa/
28 |
29 | # reftex files
30 | *.rel
31 |
32 | # AUCTeX auto folder
33 | /auto/
34 |
35 | # cask packages
36 | .cask/
37 | dist/
38 |
39 | # Flycheck
40 | flycheck_*.el
41 |
42 | # server auth directory
43 | /server/
44 |
45 | # projectiles files
46 | .projectile
47 |
48 | # directory configuration
49 | .dir-locals.el
50 |
51 | # network security
52 | /network-security.data
53 |
54 |
55 | ### Intellij+all ###
56 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
57 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
58 |
59 | # User-specific stuff
60 | .idea/**/workspace.xml
61 | .idea/**/tasks.xml
62 | .idea/**/usage.statistics.xml
63 | .idea/**/dictionaries
64 | .idea/**/shelf
65 |
66 | # Generated files
67 | .idea/**/contentModel.xml
68 |
69 | # Sensitive or high-churn files
70 | .idea/**/dataSources/
71 | .idea/**/dataSources.ids
72 | .idea/**/dataSources.local.xml
73 | .idea/**/sqlDataSources.xml
74 | .idea/**/dynamic.xml
75 | .idea/**/uiDesigner.xml
76 | .idea/**/dbnavigator.xml
77 |
78 | # Gradle
79 | .idea/**/gradle.xml
80 | .idea/**/libraries
81 |
82 | # Gradle and Maven with auto-import
83 | # When using Gradle or Maven with auto-import, you should exclude module files,
84 | # since they will be recreated, and may cause churn. Uncomment if using
85 | # auto-import.
86 | # .idea/modules.xml
87 | # .idea/*.iml
88 | # .idea/modules
89 | # *.iml
90 | # *.ipr
91 |
92 | # CMake
93 | cmake-build-*/
94 |
95 | # Mongo Explorer plugin
96 | .idea/**/mongoSettings.xml
97 |
98 | # File-based project format
99 | *.iws
100 |
101 | # IntelliJ
102 | out/
103 |
104 | # mpeltonen/sbt-idea plugin
105 | .idea_modules/
106 |
107 | # JIRA plugin
108 | atlassian-ide-plugin.xml
109 |
110 | # Cursive Clojure plugin
111 | .idea/replstate.xml
112 |
113 | # Crashlytics plugin (for Android Studio and IntelliJ)
114 | com_crashlytics_export_strings.xml
115 | crashlytics.properties
116 | crashlytics-build.properties
117 | fabric.properties
118 |
119 | # Editor-based Rest Client
120 | .idea/httpRequests
121 |
122 | # Android studio 3.1+ serialized cache file
123 | .idea/caches/build_file_checksums.ser
124 |
125 | ### Intellij+all Patch ###
126 | # Ignores the whole .idea folder and all .iml files
127 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
128 |
129 | .idea/
130 |
131 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
132 |
133 | *.iml
134 | modules.xml
135 | .idea/misc.xml
136 | *.ipr
137 |
138 | # Sonarlint plugin
139 | .idea/sonarlint
140 |
141 | ### macOS ###
142 | # General
143 | .DS_Store
144 | .AppleDouble
145 | .LSOverride
146 |
147 | # Icon must end with two \r
148 | Icon
149 |
150 | # Thumbnails
151 | ._*
152 |
153 | # Files that might appear in the root of a volume
154 | .DocumentRevisions-V100
155 | .fseventsd
156 | .Spotlight-V100
157 | .TemporaryItems
158 | .Trashes
159 | .VolumeIcon.icns
160 | .com.apple.timemachine.donotpresent
161 |
162 | # Directories potentially created on remote AFP share
163 | .AppleDB
164 | .AppleDesktop
165 | Network Trash Folder
166 | Temporary Items
167 | .apdisk
168 |
169 | ### Vim ###
170 | # Swap
171 | [._]*.s[a-v][a-z]
172 | [._]*.sw[a-p]
173 | [._]s[a-rt-v][a-z]
174 | [._]ss[a-gi-z]
175 | [._]sw[a-p]
176 |
177 | # Session
178 | Session.vim
179 | Sessionx.vim
180 |
181 | # Temporary
182 | .netrwhist
183 | # Auto-generated tag files
184 | tags
185 | # Persistent undo
186 | [._]*.un~
187 |
188 | ### Gradle ###
189 | .gradle
190 | build/
191 |
192 | # Ignore Gradle GUI config
193 | gradle-app.setting
194 |
195 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
196 | !gradle-wrapper.jar
197 |
198 | # Cache of project
199 | .gradletasknamecache
200 |
201 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
202 | # gradle/wrapper/gradle-wrapper.properties
203 |
204 | ### Gradle Patch ###
205 | **/build/
206 |
207 | # End of https://www.gitignore.io/api/vim,macos,emacs,gradle,intellij+all
208 |
209 | LOG_FILE_IS_UNDEFINED*
210 |
211 | .settings
212 | bin
213 | .project
214 | .classpath
215 | *.env
216 |
217 | application-*.yml
218 | !application.yml
219 | !application-dev.yml
220 | *.tar.gz
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/tools/src/test/java/cn/msjc/utils/ds/json/JsonUtilsTest.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.ds.json;
2 |
3 | import static org.junit.jupiter.api.Assertions.assertEquals;
4 | import static org.junit.jupiter.api.Assertions.assertFalse;
5 | import static org.junit.jupiter.api.Assertions.assertNotNull;
6 | import static org.junit.jupiter.api.Assertions.assertNull;
7 | import static org.junit.jupiter.api.Assertions.assertThrows;
8 | import static org.junit.jupiter.api.Assertions.assertTrue;
9 |
10 | import com.fasterxml.jackson.core.type.TypeReference;
11 | import com.fasterxml.jackson.databind.JsonNode;
12 | import com.fasterxml.jackson.databind.node.ArrayNode;
13 | import com.fasterxml.jackson.databind.node.ObjectNode;
14 | import java.io.UncheckedIOException;
15 | import java.nio.charset.StandardCharsets;
16 | import java.util.Collections;
17 | import java.util.List;
18 | import lombok.Data;
19 | import org.junit.jupiter.api.Test;
20 |
21 | class JsonUtilsTest {
22 |
23 | private final JsonUtils JSON = JsonUtils.JSON;
24 |
25 | @Test
26 | void testNewNodeWorks() {
27 | ObjectNode o1 = JSON.newObjectNode();
28 | assertEquals("{}", JSON.toString(o1));
29 |
30 | ArrayNode a1 = JSON.newArrayNode();
31 | assertEquals("[]", JSON.toString(a1));
32 |
33 | a1.add(o1);
34 | assertEquals("[{}]", JSON.toString(a1));
35 |
36 | a1.add(JSON.newObjectNode(n -> n.put("hello", "world")));
37 | assertEquals("[{},{\"hello\":\"world\"}]", JSON.toString(a1));
38 |
39 | a1.add(JSON.newArrayNode(n -> n.add(42)));
40 | assertEquals("[{},{\"hello\":\"world\"},[42]]", JSON.toString(a1));
41 | }
42 |
43 | @Test
44 | void testNewJsonString() {
45 | assertEquals("{}", JSON.newJsonObjectString(n -> {}));
46 | assertEquals("{\"hello\":\"world\"}", JSON.newJsonObjectString(n -> n.put("hello", "world")));
47 |
48 | assertEquals("[]", JSON.newJsonArrayString(n -> {}));
49 | assertEquals("[42,\"hello\"]", JSON.newJsonArrayString(n -> n.add(42).add("hello")));
50 | }
51 |
52 | @Test
53 | void testNullCheck() {
54 | assertTrue(JSON.isNull(null));
55 | assertTrue(JSON.isNull(JSON.fromString("null")));
56 | }
57 |
58 | @Test
59 | void testToJsonNode() {
60 | M m = new M().setM("hello");
61 | JsonNode mNode = JSON.toJsonNode(m);
62 | M m1 = JSON.fromJsonNode(mNode, M.class);
63 | assertEquals(m, m1);
64 | }
65 |
66 | @SuppressWarnings("ConstantConditions")
67 | @Test
68 | void testFromString() {
69 | M m1 = JSON.fromString("{\"m\":\"hello\"}", M.class);
70 | assertEquals(new M().setM("hello"), m1);
71 |
72 | M m2 = JSON.fromString("null", M.class);
73 | assertNull(m2);
74 |
75 | M m3 = JSON.fromString(null, M.class);
76 | assertNull(m3);
77 |
78 | List ms1 = JSON.fromString("[{\"m\":\"hello\"}]", new TypeReference>() {});
79 | assertEquals(Collections.singletonList(new M().setM("hello")), ms1);
80 |
81 | List ms2 = JSON.fromString("null", new TypeReference>() {});
82 | assertNull(ms2);
83 |
84 | List ms3 = JSON.fromString(null, new TypeReference>() {});
85 | assertNull(ms3);
86 | }
87 |
88 | @Test
89 | void testFromNullReturnsNull() {
90 | assertNull(JSON.fromBytes(null));
91 | assertNull(JSON.fromBytesOrGet(null, JSON::newObjectNode));
92 | assertNull(JSON.fromBytes(null, JSON.newObjectNode()));
93 |
94 | assertNull(JSON.fromString(null));
95 | assertNull(JSON.fromStringOrGet(null, JSON::newObjectNode));
96 | assertNull(JSON.fromString(null, JSON.newObjectNode()));
97 |
98 | assertNull(JSON.fromJsonNode(null, String.class));
99 | assertNull(JSON.fromJsonNodeOrGet(null, String.class, () -> "42"));
100 | assertNull(JSON.fromJsonNode(null, String.class, "42"));
101 |
102 | assertNull(JSON.fromJsonNode(null, new TypeReference>() {}));
103 | assertNull(
104 | JSON.fromJsonNodeOrGet(null, new TypeReference>() {}, Collections::emptyList));
105 | assertNull(
106 | JSON.fromJsonNode(null, new TypeReference>() {}, Collections.emptyList()));
107 | }
108 |
109 | @Test
110 | void testFromXXXThrows() {
111 | String errJsonString = "this is not a json string";
112 | byte[] errJsonBytes = errJsonString.getBytes(StandardCharsets.UTF_8);
113 |
114 | ArrayNode notMNode = JSON.newArrayNode();
115 | notMNode.add("this is not M class content");
116 |
117 | assertThrows(UncheckedIOException.class, () -> JSON.fromString(errJsonString, JsonNode.class));
118 | assertThrows(IllegalArgumentException.class, () -> JSON.fromJsonNode(notMNode, M.class));
119 | assertThrows(UncheckedIOException.class, () -> JSON.fromBytes(errJsonBytes));
120 | }
121 |
122 | @Test
123 | void testWithDefaultIgnoresError() {
124 | String errJsonString = "this is not a json string";
125 | byte[] errJsonBytes = errJsonString.getBytes(StandardCharsets.UTF_8);
126 |
127 | ObjectNode defaultNode = JSON.newObjectNode();
128 |
129 | ArrayNode notMNode = JSON.newArrayNode();
130 | notMNode.add("this is not M class content");
131 | M defaultM = new M();
132 |
133 | assertEquals(defaultNode, JSON.fromBytesOrGet(errJsonBytes, () -> defaultNode));
134 | assertEquals(defaultNode, JSON.fromBytes(errJsonBytes, defaultNode));
135 |
136 | assertEquals(defaultNode, JSON.fromStringOrGet(errJsonString, () -> defaultNode));
137 | assertEquals(defaultNode, JSON.fromString(errJsonString, defaultNode));
138 |
139 | assertEquals(defaultM, JSON.fromJsonNodeOrGet(notMNode, M.class, () -> defaultM));
140 | assertEquals(defaultM, JSON.fromJsonNode(notMNode, M.class, defaultM));
141 |
142 | assertEquals(
143 | Collections.emptyList(),
144 | JSON.fromJsonNodeOrGet(notMNode, new TypeReference>() {}, Collections::emptyList));
145 | assertEquals(
146 | Collections.emptyList(),
147 | JSON.fromJsonNode(notMNode, new TypeReference>() {}, Collections.emptyList()));
148 | }
149 |
150 | @Test
151 | void test_accessors() {
152 | ObjectNode obj = JSON.newObjectNode(n -> n.putPOJO("hello", new M().setM("world")));
153 | assertTrue(JSON.isNull(obj, "a"));
154 | assertTrue(JSON.isNull(obj, "hello", "a"));
155 | assertFalse(JSON.isNull(obj, "hello", "m"));
156 | assertEquals("world", JSON.getString(obj, "hello", "m").getOrNull());
157 | }
158 |
159 | @Test
160 | void testVavrModule() {
161 | io.vavr.collection.List s =
162 | JSON.fromString("[\"a\"]", new TypeReference>() {});
163 | assertNotNull(s);
164 | assertEquals(1, s.size());
165 | assertEquals("a", s.head());
166 | }
167 |
168 | @Data
169 | static class M {
170 | private String m;
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/tools/src/main/java/cn/msjc/utils/system/CmdExecutor.java:
--------------------------------------------------------------------------------
1 | package cn.msjc.utils.system;
2 |
3 | import static com.google.common.base.Preconditions.checkArgument;
4 |
5 | import com.google.common.util.concurrent.FutureCallback;
6 | import com.google.common.util.concurrent.Futures;
7 | import com.google.common.util.concurrent.ListenableFuture;
8 | import com.google.common.util.concurrent.ListeningExecutorService;
9 | import com.google.common.util.concurrent.MoreExecutors;
10 | import com.google.common.util.concurrent.SettableFuture;
11 | import java.io.BufferedReader;
12 | import java.io.IOException;
13 | import java.io.InputStream;
14 | import java.io.InputStreamReader;
15 | import java.io.UncheckedIOException;
16 | import java.nio.charset.StandardCharsets;
17 | import java.util.ArrayList;
18 | import java.util.Collection;
19 | import java.util.List;
20 | import java.util.Optional;
21 | import java.util.concurrent.ConcurrentHashMap;
22 | import java.util.concurrent.Executors;
23 | import java.util.concurrent.TimeUnit;
24 | import java.util.function.Consumer;
25 | import java.util.function.Function;
26 | import lombok.Getter;
27 | import lombok.extern.slf4j.Slf4j;
28 | import org.jetbrains.annotations.NotNull;
29 | import org.jetbrains.annotations.Nullable;
30 |
31 | @Slf4j
32 | public class CmdExecutor {
33 |
34 | private static final ConcurrentHashMap> processListened =
35 | new ConcurrentHashMap<>();
36 |
37 | @Getter
38 | private static final ListeningExecutorService executorService =
39 | MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
40 |
41 | public static Process executeCmd(String... cmd) {
42 |
43 | log.debug("Executing:\n {}", toCmdString(cmd));
44 |
45 | try {
46 | return Runtime.getRuntime().exec(cmd);
47 | } catch (IOException e) {
48 | throw new UncheckedIOException("Error executing: " + toCmdString(cmd), e);
49 | }
50 | }
51 |
52 | public static boolean checkCommand(Collection cmd, int timeoutSeconds) {
53 | return checkCommand(cmd, timeoutSeconds, false);
54 | }
55 |
56 | public static boolean checkCommand(String[] cmd, int timeoutSeconds) {
57 | return checkCommand(cmd, timeoutSeconds, false);
58 | }
59 |
60 | public static boolean checkCommand(
61 | Collection cmd, int timeoutSeconds, boolean printLogOnError) {
62 | return checkCommand(cmd.toArray(new String[] {}), timeoutSeconds, printLogOnError);
63 | }
64 |
65 | public static boolean checkCommand(String[] cmd, int timeoutSeconds, boolean printLogOnError) {
66 | StringBuilder sb = new StringBuilder();
67 |
68 | try {
69 | Process process = executeCmd(cmd);
70 | if (printLogOnError) {
71 | listenOnLines(
72 | process, l -> sb.append(" ").append(l), l -> sb.append(" ").append(l), null);
73 | }
74 |
75 | if (!process.waitFor(timeoutSeconds, TimeUnit.SECONDS)) {
76 | process.destroyForcibly();
77 | }
78 | if (process.exitValue() != 0) {
79 | if (printLogOnError) {
80 | log.warn(
81 | "Check command failed[exitValue={}]: {}\n{}",
82 | process.exitValue(),
83 | toCmdString(cmd),
84 | sb);
85 | }
86 | return false;
87 | } else {
88 | return true;
89 | }
90 | } catch (Throwable t) {
91 | log.warn(
92 | "Check command failed: {} - [{}] {}\n{}",
93 | toCmdString(cmd),
94 | t.getClass(),
95 | t.getMessage(),
96 | sb);
97 | return false;
98 | }
99 | }
100 |
101 | public static ListenableFuture> listenOnLines(
102 | Process process,
103 | @Nullable Consumer stdOutLineConsumer,
104 | @Nullable Consumer stdErrLineConsumer,
105 | @Nullable Runnable onExit) {
106 |
107 | Consumer stdOutStreamConsumer = null;
108 | Consumer stdErrStreamConsumer = null;
109 |
110 | Function, Consumer> lineConsumerToStreamConsumer =
111 | (lineConsumer) ->
112 | inputStream -> {
113 | try (BufferedReader reader =
114 | new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
115 | String line;
116 | while ((line = reader.readLine()) != null) {
117 | lineConsumer.accept(line);
118 | }
119 | } catch (IOException e) {
120 | throw new UncheckedIOException(e);
121 | }
122 | };
123 |
124 | if (stdOutLineConsumer != null) {
125 | stdOutStreamConsumer = lineConsumerToStreamConsumer.apply(stdOutLineConsumer);
126 | }
127 | if (stdErrLineConsumer != null) {
128 | stdErrStreamConsumer = lineConsumerToStreamConsumer.apply(stdErrLineConsumer);
129 | }
130 |
131 | return listenOnStream(process, stdOutStreamConsumer, stdErrStreamConsumer, onExit);
132 | }
133 |
134 | // 暂时一个进程只能一次 listen
135 | // onExit 会确保在所有 stream consumer 执行完之后、且进程退出时执行
136 | // @returns 会等待进程结束以及所有的 stream consumer 结束
137 | public static ListenableFuture> listenOnStream(
138 | Process process,
139 | @Nullable Consumer stdOutStreamConsumer,
140 | @Nullable Consumer stdErrStreamConsumer,
141 | @Nullable Runnable onExit) {
142 |
143 | return processListened.compute(
144 | process,
145 | (k, prevFuture) -> {
146 | checkArgument(prevFuture == null, "Process stream already listened: %s", process);
147 |
148 | List> streamFutures = new ArrayList<>();
149 |
150 | if (stdOutStreamConsumer != null) {
151 | streamFutures.add(
152 | executorService.submit(
153 | () -> stdOutStreamConsumer.accept(process.getInputStream())));
154 | }
155 |
156 | if (stdErrStreamConsumer != null) {
157 | streamFutures.add(
158 | executorService.submit(
159 | () -> stdErrStreamConsumer.accept(process.getErrorStream())));
160 | }
161 |
162 | SettableFuture future = SettableFuture.create();
163 |
164 | executorService.submit(
165 | () -> {
166 | try {
167 | process.waitFor();
168 | } catch (InterruptedException e) {
169 | log.info("interrupted", e);
170 | } finally {
171 | processListened.remove(process);
172 | future.set(process);
173 | Futures.allAsList(streamFutures)
174 | .addListener(
175 | () -> Optional.ofNullable(onExit).ifPresent(Runnable::run),
176 | executorService);
177 | }
178 | });
179 |
180 | List> futures = new ArrayList<>(streamFutures);
181 | futures.add(future);
182 | return Futures.allAsList(futures);
183 | });
184 | }
185 |
186 | public static void logExecutionError(
187 | ListenableFuture> future, String[] cmd, StringBuilder stdErrOutBuilder) {
188 | Futures.addCallback(
189 | future,
190 | new FutureCallback