diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 8a4e039b..747a7c5e 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -11,4 +11,5 @@ issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
- - https://img.dferic.com/img/pay.png
+ - https://hylexus.github.io/jt-framework/img/pay.png
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 22791b32..41954a69 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,25 @@
+## 2.1.4(2024-06-08)
+
+### ⭐ New Features
+
+- 完善 `Jt808MsgBuilder`
+- 新增 `RebuildableByteBufJt808MsgBuilder`
+
+### 🔨 Dependency Upgrades
+
+- `Gradle` : **8.6** 升级到 **8.8**
+- `spring-boot-dependencies`
+ - **2.7.14** 升级到 **2.7.18**
+ - **3.1.2** 升级到 **3.3.0**
+- `spring-cloud-dependencies`
+ - **2021.0.8** 升级到 **2021.0.9**
+ - **2022.0.4** 升级到 **2023.0.2**
+
+### 📔 Documentation
+
+- 新增消息加解密相关文档
+- 新增 `RebuildableByteBufJt808MsgBuilder` 相关文档
+
## 2.1.4-rc.4(2024-06-02)
### ⭐ New Features
diff --git a/README.md b/README.md
index 8cdafd3f..2013cbc6 100644
--- a/README.md
+++ b/README.md
@@ -17,8 +17,10 @@ Jt-808协议服务端。
## Compatibility
-支持 **spring-boot-2.x** [![spring-boot-2.x](https://img.shields.io/maven-central/v/io.github.hylexus.jt/jt-808-server-spring-boot-starter-boot2.svg?label=spring-boot-2.x)](https://search.maven.org/search?q=g:%22io.github.hylexus.jt%22%20AND%20a:%22jt-808-server-spring-boot-starter-boot2%22)
-和 **spring-boot-3.x** [![spring-boot-3.x](https://img.shields.io/maven-central/v/io.github.hylexus.jt/jt-808-server-spring-boot-starter.svg?label=spring-boot-3.x)](https://search.maven.org/search?q=g:%22io.github.hylexus.jt%22%20AND%20a:%22jt-808-server-spring-boot-starter%22) 。
+支持 **spring-boot-2.x
+** [![spring-boot-2.x](https://img.shields.io/maven-central/v/io.github.hylexus.jt/jt-808-server-spring-boot-starter-boot2.svg?label=spring-boot-2.x)](https://search.maven.org/search?q=g:%22io.github.hylexus.jt%22%20AND%20a:%22jt-808-server-spring-boot-starter-boot2%22)
+和 **spring-boot-3.x
+** [![spring-boot-3.x](https://img.shields.io/maven-central/v/io.github.hylexus.jt/jt-808-server-spring-boot-starter.svg?label=spring-boot-3.x)](https://search.maven.org/search?q=g:%22io.github.hylexus.jt%22%20AND%20a:%22jt-808-server-spring-boot-starter%22) 。
更多有关版本兼容性的信息,请移步: [入门--兼容性](https://hylexus.github.io/jt-framework/v2/jt-808/guide/quick-start/compatibility.html) 。
@@ -28,14 +30,14 @@ Jt-808协议服务端。
`starter` 的 **JDK** 版本、**spring-boot** 版本限制如下:
-| Module | JDK | spring-boot | Desc |
-|--------------------------------------------|-------|------------------|--------------------------------------------|
-| `jt-808-server-spring-boot-starter-boot2` | `11+` | `[2.2.x, 2.7.x]` | 为 **spring-boot-2.x** 提供的 starter |
-| `jt-808-server-spring-boot-starter` | `17+` | `[3.0.0, ...]` | 为 **spring-boot-3.x** 提供的 starter |
-| `jt-1078-server-spring-boot-starter-boot2` | `11+` | `[2.2.x, 2.7.x]` | 为 **spring-boot-2.x** 提供的 starter; `beta版` |
-| `jt-1078-server-spring-boot-starter` | `17+` | `[3.0.0, ...]` | 为 **spring-boot-3.x** 提供的 starter; `beta版` |
-| `jt-dashboard-client-spring-boot-starter` | `17+` | `[3.0.0, ...]` | 暂时只支持 `jdk17+/spring-boot-3.x`; `beta版` |
-| `jt-dashboard-server-spring-boot-starter` | `17+` | `[3.0.0, ...]` | 暂时只支持 `jdk17+/spring-boot-3.x`; `beta版` |
+| Module | JDK | spring-boot | Desc |
+|--------------------------------------------|-------|-------------------|--------------------------------------------|
+| `jt-808-server-spring-boot-starter-boot2` | `11+` | `[2.2.x, 2.7.18]` | 为 **spring-boot-2.x** 提供的 starter |
+| `jt-808-server-spring-boot-starter` | `17+` | `[3.0.0, ...]` | 为 **spring-boot-3.x** 提供的 starter |
+| `jt-1078-server-spring-boot-starter-boot2` | `11+` | `[2.2.x, 2.7.18]` | 为 **spring-boot-2.x** 提供的 starter; `beta版` |
+| `jt-1078-server-spring-boot-starter` | `17+` | `[3.0.0, ...]` | 为 **spring-boot-3.x** 提供的 starter; `beta版` |
+| `jt-dashboard-client-spring-boot-starter` | `17+` | `[3.0.0, ...]` | 暂时只支持 `jdk17+/spring-boot-3.x`; `beta版` |
+| `jt-dashboard-server-spring-boot-starter` | `17+` | `[3.0.0, ...]` | 暂时只支持 `jdk17+/spring-boot-3.x`; `beta版` |
## Modules
@@ -44,15 +46,15 @@ Jt-808协议服务端。
| Module | JDK | CompileLevel | .class | spring-boot | Desc |
|------------------------------------------|-----|--------------|-------------|-------------|-------------------------------------------|
| jt-core | 17 | JDK-11 | 55 (JDK-11) | -- | |
-| jt-808-server-spring-boot-starter | 17 | _**JDK-17**_ | 61 (JDK-17) | _**3.1.2**_ | |
-| jt-808-server-spring-boot-starter-boot2 | 17 | JDK-11 | 55 (JDK-11) | 2.7.14 | |
-| jt-808-server-spring-boot-autoconfigure | 17 | JDK-11 | 55 (JDK-11) | 2.7.14 | |
+| jt-808-server-spring-boot-starter | 17 | _**JDK-17**_ | 61 (JDK-17) | _**3.3.0**_ | |
+| jt-808-server-spring-boot-starter-boot2 | 17 | JDK-11 | 55 (JDK-11) | 2.7.18 | |
+| jt-808-server-spring-boot-autoconfigure | 17 | JDK-11 | 55 (JDK-11) | 2.7.18 | |
| jt-808-server-support | 17 | JDK-11 | 55 (JDK-11) | -- | |
-| jt-1078-server-spring-boot-starter | 17 | _**JDK-17**_ | 61 (JDK-17) | _**3.1.2**_ | |
-| jt-1078-server-spring-boot-starter-boot2 | 17 | JDK-11 | 55 (JDK-11) | 2.7.14 | |
-| jt-1078-server-spring-boot-autoconfigure | 17 | JDK-11 | 55 (JDK-11) | 2.7.14 | |
+| jt-1078-server-spring-boot-starter | 17 | _**JDK-17**_ | 61 (JDK-17) | _**3.3.0**_ | |
+| jt-1078-server-spring-boot-starter-boot2 | 17 | JDK-11 | 55 (JDK-11) | 2.7.18 | |
+| jt-1078-server-spring-boot-autoconfigure | 17 | JDK-11 | 55 (JDK-11) | 2.7.18 | |
| jt-1078-server-support | 17 | JDK-11 | 55 (JDK-11) | -- | |
-| `dashboard/**` | 17 | _**JDK-17**_ | 61 (JDK-11) | _**3.1.2**_ | dashboard 模块暂时只支持 `spring-boot-3.x/jdk17` |
+| `dashboard/**` | 17 | _**JDK-17**_ | 61 (JDK-11) | _**3.3.0**_ | dashboard 模块暂时只支持 `spring-boot-3.x/jdk17` |
- 模块介绍
@@ -139,7 +141,7 @@ Jt-808协议服务端。
- gradle
```groovy
-implementation group: 'io.github.hylexus.jt', name: 'jt-808-server-spring-boot-starter-boot2', version: "2.1.4-rc.3"
+implementation group: 'io.github.hylexus.jt', name: 'jt-808-server-spring-boot-starter-boot2', version: "2.1.4"
```
- maven
@@ -149,7 +151,7 @@ implementation group: 'io.github.hylexus.jt', name: 'jt-808-server-spring-boot-s
io.github.hylexus.jt
jt-808-server-spring-boot-starter-boot2
- 2.1.4-rc.3
+ 2.1.4
```
@@ -219,4 +221,5 @@ Maven版示例项目
项目的发展离不开你的支持,请作者喝一杯🍺吧!
-![有钱的捧个钱场 没钱的捧个人场](https://img.dferic.com/img/pay.png)
+![有钱的捧个钱场 没钱的捧个人场](https://hylexus.github.io/jt-framework/img/pay.png)
+
diff --git a/docs/src/.vuepress/public/img/pay.png b/docs/src/.vuepress/public/img/pay.png
new file mode 100644
index 00000000..a0363e13
Binary files /dev/null and b/docs/src/.vuepress/public/img/pay.png differ
diff --git a/docs/src/.vuepress/sidebar/zh.ts b/docs/src/.vuepress/sidebar/zh.ts
index 1a60dd44..c331c661 100644
--- a/docs/src/.vuepress/sidebar/zh.ts
+++ b/docs/src/.vuepress/sidebar/zh.ts
@@ -72,6 +72,7 @@ export const zhSidebar = sidebar({
'/v2/jt-808/guide/customization/sub-package-config.md',
'/v2/jt-808/guide/customization/request-lifecycle-listener.md',
'/v2/jt-808/guide/customization/jt-808-request-filter.md',
+ '/v2/jt-808/guide/customization/msg-encryption.md',
]
},
{
diff --git a/docs/src/about.md b/docs/src/about.md
index e7dcf650..3d78b45a 100644
--- a/docs/src/about.md
+++ b/docs/src/about.md
@@ -13,4 +13,4 @@
项目的发展离不开你的支持,请作者喝一杯🍺吧!
-![有钱的捧个钱场 没钱的捧个人场](https://img.dferic.com/img/pay.png)
+![有钱的捧个钱场 没钱的捧个人场](https://hylexus.github.io/jt-framework/img/pay.png)
diff --git a/docs/src/frequently-asked-questions/package-parsing.md b/docs/src/frequently-asked-questions/package-parsing.md
index 2fb63f6b..d2919130 100644
--- a/docs/src/frequently-asked-questions/package-parsing.md
+++ b/docs/src/frequently-asked-questions/package-parsing.md
@@ -12,4 +12,9 @@
## 报文加密解密
-暂时不支持
+`2.1.4` 开始支持。
+
+报文加解密参考资料:
+
+- [消息加密](../v2/jt-808/guide/customization/msg-encryption.md)
+- [issues-82](https://github.com/hylexus/jt-framework/issues/82)
diff --git a/docs/src/v2/jt-808/guide/customization/msg-encryption.md b/docs/src/v2/jt-808/guide/customization/msg-encryption.md
new file mode 100644
index 00000000..ea41237b
--- /dev/null
+++ b/docs/src/v2/jt-808/guide/customization/msg-encryption.md
@@ -0,0 +1,104 @@
+---
+icon: lock
+---
+
+# 消息加密(v2.1.4)
+
+## 相关资料
+
+- [#issues-82](https://github.com/hylexus/jt-framework/issues/82)
+
+## 加密方式的判断
+
+收到消息,解析之后,消息体**可能**是密文。具体通过消息头中的消息体属性字段判断。
+
+消息体属性格式如下:
+
+- v2011/v2013
+
+```
+消息体属性 word(16)
+ bit[0-10) 消息体长度
+ bit[10-13) 数据加密方式
+ 此三位都为 0,表示消息体不加密
+ 第 10 位为 1,表示消息体经过 RSA 算法加密
+ 其它保留
+ bit[13] 分包
+ 1: 消息体卫长消息,进行分包发送处理,具体分包信息由消息包封装项决定
+ 0: 则消息头中无消息包封装项字段
+ bit[14-15] 保留
+```
+
+- v2019
+
+```
+消息体属性 word(16)
+ bit[0-10) 消息体长度
+ bit[10-13) 数据加密方式
+ 此三位都为 0,表示消息体不加密
+ 第 10 位为 1,表示消息体经过 RSA 算法加密
+ 其它保留
+ bit[13] 分包
+ 1: 消息体卫长消息,进行分包发送处理,具体分包信息由消息包封装项决定
+ 0: 则消息头中无消息包封装项字段
+ bit[14] 版本标识
+ bit[15] 保留
+```
+
+尽管消息体属性有所不同,但是其中的 `数据加密方式位` 都是相同的:消息体属性中的 `bit10`, `bit11` 和 `bit12` 三个位。
+
+这三个二进制位具体含义,应该和具体的硬件实现相关。
+
+## 加密/解密报文
+
+可以通过自定义 `Jt808MsgEncryptionHandler` 实现报文的加密/解密。也就是给 **spring** 容器中加入一个 `Jt808MsgEncryptionHandler` 实现类即可。
+
+```java
+
+@Component
+public class Jt808MsgEncryptionHandlerDemo01 implements Jt808MsgEncryptionHandler {
+
+ @Override
+ public ByteBuf decryptRequestBody(Jt808RequestHeader header, ByteBuf body) {
+ final int encryptionType = header.msgBodyProps().encryptionType();
+ if (encryptionType == 0) {
+ return body;
+ }
+ // @see https://github.com/hylexus/jt-framework/issues/82
+ // 消息属性中的 第10位,11位,12位 为 010 时,表示消息体经过SM4算法加密
+ if (encryptionType == 0b010) {
+ try {
+ return JtCryptoUtil.SM4.ecbDecrypt(getSecretKey(), body);
+ } finally {
+ JtProtocolUtils.release(body);
+ }
+ }
+ throw new NotImplementedException("不支持的加密类型: 0b" + FormatUtils.toBinaryString(encryptionType, 3));
+ }
+
+ @Override
+ public ByteBuf encryptResponseBody(Jt808Response response, ByteBuf plaintextBody) {
+ // response.encryptionType(010);
+ final int encryptionType = response.encryptionType();
+ if (encryptionType == 0) {
+ return plaintextBody;
+ }
+
+ // @see https://github.com/hylexus/jt-framework/issues/82
+ // 消息属性中的 第10位,11位,12位 为 010 时,表示消息体经过SM4算法加密
+ if (encryptionType == 0b010) {
+ try {
+ return JtCryptoUtil.SM4.ecbEncrypt(getSecretKey(), plaintextBody);
+ } finally {
+ JtProtocolUtils.release(plaintextBody);
+ }
+ }
+ throw new NotImplementedException("不支持的加密类型: 0b" + FormatUtils.toBinaryString(encryptionType, 3));
+ }
+
+ private byte[] getSecretKey() {
+ // 从其他配置中获取密钥
+ return HexStringUtils.hexString2Bytes("8e47374be6b8d114cb47be6a9a128a37");
+ }
+}
+```
diff --git a/docs/src/v2/jt-808/guide/quick-start/quick-start.md b/docs/src/v2/jt-808/guide/quick-start/quick-start.md
index b4f4aa0a..b13feadd 100644
--- a/docs/src/v2/jt-808/guide/quick-start/quick-start.md
+++ b/docs/src/v2/jt-808/guide/quick-start/quick-start.md
@@ -37,14 +37,14 @@ icon: launch
io.github.hylexus.jt
jt-808-server-spring-boot-starter-boot2
- 2.1.4-rc.3
+ 2.1.4
```
@tab:active gradle
```groovy
-implementation 'io.github.hylexus.jt:jt-808-server-spring-boot-starter-boot2:2.1.4-rc.3'
+implementation 'io.github.hylexus.jt:jt-808-server-spring-boot-starter-boot2:2.1.4'
```
:::
@@ -62,14 +62,14 @@ implementation 'io.github.hylexus.jt:jt-808-server-spring-boot-starter-boot2:2.1
io.github.hylexus.jt
jt-808-server-spring-boot-starter
- 2.1.4-rc.3
+ 2.1.4
```
@tab:active gradle
```groovy
-implementation 'io.github.hylexus.jt:jt-808-server-spring-boot-starter:2.1.4-rc.3'
+implementation 'io.github.hylexus.jt:jt-808-server-spring-boot-starter:2.1.4'
```
:::
diff --git a/docs/src/v2/jt-808/guide/utilities/msg-builder.md b/docs/src/v2/jt-808/guide/utilities/msg-builder.md
index f19aff07..a0942690 100644
--- a/docs/src/v2/jt-808/guide/utilities/msg-builder.md
+++ b/docs/src/v2/jt-808/guide/utilities/msg-builder.md
@@ -13,6 +13,14 @@ icon: build
该实现类通过被 `@Jt808ResponseBody` 标记的实体类来构建报文。
+::: danger 注意
+
+- 该实现 **支持** 多次调用 `builder.build()` 方法
+- 该实现不需要显式调用 `builder.release()`;当然即便是显式调用 `builder.release()`,也不会有任何影响
+- 方法返回的构建结果需要调用方自己在恰当的时机释放
+
+:::
+
```java
public class Jt808MsgBuilderTest {
@@ -70,6 +78,15 @@ public class Jt808MsgBuilderTest {
该实现类通过 `Jt808ByteWriter` (`ByteBuf`) 来构建报文。
+::: danger 注意
+
+- 该实现 **不支持** 多次调用 `builder.build()` 方法
+- 该实现需要调用 `builder.release()` 方法来释放 `builder` 内部用到的临时 `ByteBuf`
+- 推荐在 `try-with-resources` 块中使用
+- 方法返回的构建结果需要调用方自己在恰当的时机释放
+
+:::
+
```java
public class Jt808MsgBuilderTest {
@@ -79,35 +96,95 @@ public class Jt808MsgBuilderTest {
@Test
public void testByteBufMsgBuilder() {
- final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer(128);
- final ByteBufJt808MsgBuilder builder = Jt808MsgBuilder.newByteBufBuilder(ALWAYS_RETURN_0, originalBuf)
- .version(Jt808ProtocolVersion.VERSION_2013)
- .msgId(BuiltinJt808MsgType.CLIENT_COMMON_REPLY)
- .terminalId("013912344323")
- // 消息体借助 Jt808ByteWriter 来写入内容
- // 也可以直接提供一个已经写好内容的 ByteBuf 用来充当消息体
- .body(writer -> writer
- // 1. 应答流水号 WORD 对应的平台消息的流水号
- .writeWord(0)
- // 2. 应答id WORD 对应的平台消息的 ID
- .writeWord(0x8103)
- // 3. 结果 byte 0:成功/确认;1:失败;2:消息有误;3:不支持
- .writeByte(0)
- );
+ // final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer(128);
+ final ByteBuf originalBuf = Unpooled.buffer(128);
+ try (ByteBufJt808MsgBuilder builder = Jt808MsgBuilder.newByteBufBuilder(ALWAYS_RETURN_0, originalBuf)) {
+ builder.version(Jt808ProtocolVersion.VERSION_2013)
+ .msgId(BuiltinJt808MsgType.CLIENT_COMMON_REPLY)
+ .terminalId("013912344323")
+ // 消息体借助 Jt808ByteWriter 来写入内容
+ // 也可以直接提供一个已经写好内容的 ByteBuf 用来充当消息体
+ .body(writer -> writer
+ // 1. 应答流水号 WORD 对应的平台消息的流水号
+ .writeWord(0)
+ // 2. 应答id WORD 对应的平台消息的 ID
+ .writeWord(0x8103)
+ // 3. 结果 byte 0:成功/确认;1:失败;2:消息有误;3:不支持
+ .writeByte(0)
+ );
+
+ final ByteBuf result = builder.build();
+ assertEquals("7E0001000501391234432300000000810300F87E", HexStringUtils.byteBufToString(result));
+
+ assertEquals(1, result.refCnt());
+ assertEquals(1, originalBuf.refCnt());
+
+ // 在恰当的时机释放构建结果
+ result.release();
+ assertEquals(0, result.refCnt());
+ }
+
+ // try-with-resource 释放了 originalBuf
+ assertEquals(0, originalBuf.refCnt());
+ }
+}
+```
- final ByteBuf result = builder.build();
- assertEquals("7E0001000501391234432300000000810300F87E", HexStringUtils.byteBufToString(result));
- assertEquals("7E0001000501391234432300000000810300F87E", builder.toHexString());
+## RebuildableByteBufJt808MsgBuilder
- assertEquals(1, originalBuf.refCnt());
- assertEquals(result.refCnt(), originalBuf.refCnt());
+该实现类通过 `Jt808ByteWriter` (`ByteBuf`) 来构建报文。
- result.release();
+::: danger 注意
- assertEquals(0, originalBuf.refCnt());
- assertEquals(result.refCnt(), originalBuf.refCnt());
+- 该实现 **支持** 多次调用 `builder.build()` 方法
+- 该实现需要调用 `builder.release()` 方法来释放 `builder` 内部用到的临时 `ByteBuf`
+- 推荐在 `try-with-resources` 块中使用
+- 方法返回的构建结果需要调用方自己在恰当的时机释放
+
+:::
+
+```java
+class RebuildableByteBufJt808MsgBuilderTest {
+
+ Jt808FlowIdGenerator flowIdGenerator = step -> 0;
+
+ @Test
+ void test2() {
+ final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer();
+ try (var builder = Jt808MsgBuilder.newRebuildableByteBufBuilder(flowIdGenerator, originalBuf)) {
+ builder.version(Jt808ProtocolVersion.VERSION_2013)
+ .msgId(BuiltinJt808MsgType.CLIENT_COMMON_REPLY)
+ .terminalId("013912344323")
+ // 消息体借助 Jt808ByteWriter 来写入内容
+ // 也可以直接提供一个已经写好内容的 ByteBuf 用来充当消息体
+ .body(writer -> writer
+ // 1. 应答流水号 WORD 对应的平台消息的流水号
+ .writeWord(0)
+ // 2. 应答id WORD 对应的平台消息的 ID
+ .writeWord(0x8103)
+ // 3. 结果 byte 0:成功/确认;1:失败;2:消息有误;3:不支持
+ .writeByte(0)
+ );
+ final ByteBuf result = builder.build();
+ Assertions.assertEquals(1, result.refCnt());
+ Assertions.assertEquals("7E0001000501391234432300000000810300F87E", FormatUtils.toHexString(result));
+
+ doSomeProcess(result);
+
+ result.release();
+ Assertions.assertEquals(0, result.refCnt());
+
+ final ByteBuf result2 = builder.build();
+ Assertions.assertEquals(1, result2.refCnt());
+
+ doSomeProcess(result2);
+
+ result2.release();
+ Assertions.assertEquals(0, result2.refCnt());
+ }
+ // try-with-resources 会自动释放 originalBuf
+ Assertions.assertEquals(0, originalBuf.refCnt());
}
+
}
```
-
-
diff --git a/gradle.properties b/gradle.properties
index 5855c39e..e4c5aa9e 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,6 @@
+org.gradle.jvmargs=-Dio.netty.allocator.type=unpooled
projectGroup=io.github.hylexus.jt
-projectVersion=2.1.4-rc.4
+projectVersion=2.1.4
# scm
projectScmUrl=https://github.com/hylexus/jt-framework
projectScmConnection=scm:git:git@github.com:hylexus/jt-framework.git
@@ -22,8 +23,8 @@ defaultJavaVersion=11
maximumJavaVersion=17
# bom
# spring-boot-2.x
-defaultSpringBootBomVersion=2.7.14
-defaultSpringCloudBomVersion=2021.0.8
+defaultSpringBootBomVersion=2.7.18
+defaultSpringCloudBomVersion=2021.0.9
# spring-boot-3.x
-maximumSpringBootBomVersion=3.1.2
-maximumSpringCloudBomVersion=2022.0.4
+maximumSpringBootBomVersion=3.3.0
+maximumSpringCloudBomVersion=2023.0.2
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index c1962a79..d64cd491 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 416a97c2..896b076a 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.6-bin.zip
+#distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
+distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.8-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index aeb74cbb..1aa94a42 100755
--- a/gradlew
+++ b/gradlew
@@ -83,7 +83,8 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -130,10 +131,13 @@ location of your Java installation."
fi
else
JAVACMD=java
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
@@ -141,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC3045
+ # shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -149,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC3045
+ # shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -198,11 +202,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-# Collect all arguments for the java command;
-# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-# shell script including quotes and variable substitutions, so put them in
-# double quotes to make sure that they get re-expanded; and
-# * put everything else in single quotes, so that it's not re-expanded.
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
diff --git a/gradlew.bat b/gradlew.bat
index 93e3f59f..25da30db 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
diff --git a/jt-1078-server-support/jt-1078-server-support.gradle b/jt-1078-server-support/jt-1078-server-support.gradle
index 7bf82675..9af0a313 100644
--- a/jt-1078-server-support/jt-1078-server-support.gradle
+++ b/jt-1078-server-support/jt-1078-server-support.gradle
@@ -37,4 +37,6 @@ dependencies {
test {
useJUnitPlatform()
+ // https://github.com/gradle/gradle/issues/7773
+ systemProperties(System.properties)
}
diff --git a/jt-808-server-spring-boot-autoconfigure/src/main/java/io/github/hylexus/jt/jt808/boot/config/configuration/Jt808ServerComponentStatistics.java b/jt-808-server-spring-boot-autoconfigure/src/main/java/io/github/hylexus/jt/jt808/boot/config/configuration/Jt808ServerComponentStatistics.java
index c3be6565..edc69bb2 100644
--- a/jt-808-server-spring-boot-autoconfigure/src/main/java/io/github/hylexus/jt/jt808/boot/config/configuration/Jt808ServerComponentStatistics.java
+++ b/jt-808-server-spring-boot-autoconfigure/src/main/java/io/github/hylexus/jt/jt808/boot/config/configuration/Jt808ServerComponentStatistics.java
@@ -67,7 +67,8 @@ public class Jt808ServerComponentStatistics implements CommandLineRunner, Applic
Jt808TerminalHeatBeatHandler.class,
Jt808ServerNettyConfigure.class,
//Jt808RequestMsgQueue.class,
- Jt808RequestMsgQueueListener.class
+ Jt808RequestMsgQueueListener.class,
+ Jt808MsgEncryptionHandler.class
)
);
@Setter
diff --git a/jt-808-server-support/jt-808-server-support.gradle b/jt-808-server-support/jt-808-server-support.gradle
index 98f377d8..94128d85 100644
--- a/jt-808-server-support/jt-808-server-support.gradle
+++ b/jt-808-server-support/jt-808-server-support.gradle
@@ -25,4 +25,6 @@ dependencies {
test {
useJUnitPlatform()
+ // https://github.com/gradle/gradle/issues/7773
+ systemProperties(System.properties)
}
diff --git a/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/Jt808MsgBuilder.java b/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/Jt808MsgBuilder.java
index 2ff68a1f..d54fcd7d 100644
--- a/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/Jt808MsgBuilder.java
+++ b/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/Jt808MsgBuilder.java
@@ -4,6 +4,7 @@
import io.github.hylexus.jt.jt808.Jt808ProtocolVersion;
import io.github.hylexus.jt.jt808.spec.impl.msg.builder.ByteBufJt808MsgBuilder;
import io.github.hylexus.jt.jt808.spec.impl.msg.builder.EntityJt808MsgBuilder;
+import io.github.hylexus.jt.jt808.spec.impl.msg.builder.RebuildableByteBufJt808MsgBuilder;
import io.github.hylexus.jt.jt808.spec.session.Jt808FlowIdGenerator;
import io.github.hylexus.jt.jt808.support.codec.Jt808MsgEncoder;
import io.github.hylexus.jt.jt808.support.utils.JtProtocolUtils;
@@ -16,18 +17,353 @@
@UnstableApi
public interface Jt808MsgBuilder> {
+ /**
+ * 支持 重复调用 {@link #build()} 方法
+ *
+ * 推荐在 {@code try-with-resources} 块中使用:
+ *
{@code
+ * final Jt808MsgEncoder encoder = new DefaultJt808MsgEncoder(
+ * ByteBufAllocator.DEFAULT,
+ * new DefaultJt808MsgBytesProcessor(ByteBufAllocator.DEFAULT),
+ * responseSubPackage -> {
+ * },
+ * Jt808ResponseSubPackageStorage.NO_OPS_STORAGE,
+ * Jt808MsgEncryptionHandler.NO_OPS
+ * );
+ * final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer();
+ * try (var builder = Jt808MsgBuilder.newRebuildableByteBufBuilder(flowIdGenerator, encoder, originalBuf)) {
+ * builder.version(...)
+ * .msgId(...)
+ * .terminalId(...)
+ * .body(writer -> writer.writeDWord(1)
+ * .writeWord(2)
+ * .writeString("abc")
+ * );
+ * final ByteBuf result = builder.build();
+ * Assertions.assertEquals(1, result.refCnt());
+ *
+ * doSomeProcess(result);
+ *
+ * result.release();
+ * Assertions.assertEquals(0, result.refCnt());
+ *
+ * final ByteBuf result2 = builder.build();
+ * Assertions.assertEquals(1, result2.refCnt());
+ *
+ * doSomeProcess(result2);
+ *
+ * result2.release();
+ * Assertions.assertEquals(0, result2.refCnt());
+ * }
+ * // try-with-resources 会自动释放 originalBuf
+ * Assertions.assertEquals(0, originalBuf.refCnt());
+ * }
+ *
+ * @return 方法返回的构建结果需要调用方自己 在恰当的时机释放。
+ * @apiNote 这个实现类 需要手动调用 {@link #release()} 方法以释放内部用到的 {@code ByteBuf body} 临时变量。
+ * @see #release()
+ * @see RebuildableByteBufJt808MsgBuilder#close()
+ * @since 2.1.4
+ */
+ static RebuildableByteBufJt808MsgBuilder newRebuildableByteBufBuilder(Jt808FlowIdGenerator flowIdGenerator, Jt808MsgEncoder encoder, ByteBuf body) {
+ return new RebuildableByteBufJt808MsgBuilder(flowIdGenerator, encoder, body);
+ }
+
+ /**
+ * 支持 重复调用 {@link #build()} 方法
+ *
+ * 推荐在 {@code try-with-resources} 块中使用:
+ *
{@code
+ * final Jt808MsgEncoder encoder = new DefaultJt808MsgEncoder(
+ * ByteBufAllocator.DEFAULT,
+ * new DefaultJt808MsgBytesProcessor(ByteBufAllocator.DEFAULT),
+ * responseSubPackage -> {
+ * },
+ * Jt808ResponseSubPackageStorage.NO_OPS_STORAGE,
+ * Jt808MsgEncryptionHandler.NO_OPS
+ * );
+ * try (var builder = Jt808MsgBuilder.newRebuildableByteBufBuilder(flowIdGenerator, encoder)) {
+ * builder.version(...)
+ * .msgId(...)
+ * .terminalId(...)
+ * .body(writer -> writer.writeDWord(1)
+ * .writeWord(2)
+ * .writeString("abc")
+ * );
+ * final ByteBuf result = builder.build();
+ * Assertions.assertEquals(1, result.refCnt());
+ *
+ * doSomeProcess(result);
+ *
+ * result.release();
+ * Assertions.assertEquals(0, result.refCnt());
+ *
+ * final ByteBuf result2 = builder.build();
+ * Assertions.assertEquals(1, result2.refCnt());
+ *
+ * doSomeProcess(result2);
+ *
+ * result2.release();
+ * Assertions.assertEquals(0, result2.refCnt());
+ * }
+ * }
+ *
+ * @return 方法返回的构建结果需要调用方自己 在恰当的时机释放。
+ * @apiNote 这个实现类 需要手动调用 {@link #release()} 方法以释放内部用到的 {@code ByteBuf} 临时变量。
+ * @see #release()
+ * @see RebuildableByteBufJt808MsgBuilder#close()
+ * @since 2.1.4
+ */
+ static RebuildableByteBufJt808MsgBuilder newRebuildableByteBufBuilder(Jt808FlowIdGenerator flowIdGenerator, Jt808MsgEncoder encoder) {
+ return new RebuildableByteBufJt808MsgBuilder(flowIdGenerator, encoder);
+ }
+
+ /**
+ * 支持 重复调用 {@link #build()} 方法
+ *
+ * 推荐在 {@code try-with-resources} 块中使用:
+ *
{@code
+ * final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer();
+ * try (var builder = Jt808MsgBuilder.newRebuildableByteBufBuilder(flowIdGenerator, originalBuf)) {
+ * builder.version(...)
+ * .msgId(...)
+ * .terminalId(...)
+ * .body(writer -> writer.writeDWord(1)
+ * .writeWord(2)
+ * .writeString("abc")
+ * );
+ * final ByteBuf result = builder.build();
+ * Assertions.assertEquals(1, result.refCnt());
+ *
+ * doSomeProcess(result);
+ *
+ * result.release();
+ * Assertions.assertEquals(0, result.refCnt());
+ *
+ * final ByteBuf result2 = builder.build();
+ * Assertions.assertEquals(1, result2.refCnt());
+ *
+ * doSomeProcess(result2);
+ *
+ * result2.release();
+ * Assertions.assertEquals(0, result2.refCnt());
+ * }
+ * // try-with-resources 会自动释放 originalBuf
+ * Assertions.assertEquals(0, originalBuf.refCnt());
+ * }
+ *
+ * @return 方法返回的构建结果需要调用方自己 在恰当的时机释放。
+ * @apiNote 这个实现类 需要手动调用 {@link #release()} 方法以释放内部用到的 {@code ByteBuf body} 临时变量。
+ * @see #release()
+ * @see RebuildableByteBufJt808MsgBuilder#close()
+ * @since 2.1.4
+ */
+ static RebuildableByteBufJt808MsgBuilder newRebuildableByteBufBuilder(Jt808FlowIdGenerator flowIdGenerator, ByteBuf body) {
+ return new RebuildableByteBufJt808MsgBuilder(flowIdGenerator, body);
+ }
+
+ /**
+ * 支持 重复调用 {@link #build()} 方法
+ *
+ * 推荐在 {@code try-with-resources} 块中使用:
+ *
{@code
+ * try (var builder = Jt808MsgBuilder.newRebuildableByteBufBuilder(flowIdGenerator)) {
+ * builder.version(...)
+ * .msgId(...)
+ * .terminalId(...)
+ * .body(writer -> writer.writeDWord(1)
+ * .writeWord(2)
+ * .writeString("abc")
+ * );
+ *
+ * final ByteBuf result = builder.build();
+ * Assertions.assertEquals(1, result.refCnt());
+ * doSomeProcess(result);
+ * result.release();
+ * Assertions.assertEquals(0, result.refCnt());
+ *
+ * final ByteBuf result2 = builder.build();
+ * Assertions.assertEquals(1, result2.refCnt());
+ * doSomeProcess(result2);
+ * result2.release();
+ * Assertions.assertEquals(0, result2.refCnt());
+ * }
+ * }
+ *
+ * @return 方法返回的构建结果需要调用方自己 在恰当的时机释放。
+ * @apiNote 这个实现类 需要手动调用 {@link #release()} 方法以释放内部用到的 {@code ByteBuf} 临时变量。
+ * @see #release()
+ * @see RebuildableByteBufJt808MsgBuilder#close()
+ * @since 2.1.4
+ */
+ static RebuildableByteBufJt808MsgBuilder newRebuildableByteBufBuilder(Jt808FlowIdGenerator flowIdGenerator) {
+ return new RebuildableByteBufJt808MsgBuilder(flowIdGenerator);
+ }
+
+ /**
+ * 不支持 重复调用 {@link #build()} 方法
+ *
+ * 推荐在 {@code try-with-resources} 块中使用:
+ *
{@code
+ * final ByteBuf originalBuf = ...;
+ * try (var builder = Jt808MsgBuilder.newByteBufBuilder(flowIdGenerator, originalBuf)) {
+ * builder.version(...)
+ * .msgId(...)
+ * .terminalId(...)
+ * .body(writer -> writer.writeDWord(1)
+ * .writeWord(2)
+ * .writeString("abc")
+ * );
+ * final ByteBuf result = builder.build();
+ * Assertions.assertEquals(1, result.refCnt());
+ *
+ * doSomeProcess(result);
+ *
+ * result.release();
+ * Assertions.assertEquals(0, result.refCnt());
+ * }
+ * // try-with-resources 会自动释放 originalBuf
+ * Assertions.assertEquals(0, originalBuf.refCnt());
+ * }
+ *
+ * @return 方法返回的构建结果需要调用方自己 在恰当的时机释放。
+ * @apiNote 这个实现类 需要手动调用 {@link #release()} 方法以释放内部用到的 {@code ByteBuf body} 临时变量。
+ * @see #release()
+ * @see ByteBufJt808MsgBuilder#close()
+ */
static ByteBufJt808MsgBuilder newByteBufBuilder(Jt808FlowIdGenerator flowIdGenerator, ByteBuf body) {
return new ByteBufJt808MsgBuilder(flowIdGenerator, body);
}
+ /**
+ * 不支持 重复调用 {@link #build()} 方法
+ *
+ * 推荐在 {@code try-with-resources} 块中使用:
+ *
{@code
+ * try (var builder = Jt808MsgBuilder.newByteBufBuilder(flowIdGenerator)) {
+ * builder.version(...)
+ * .msgId(...)
+ * .terminalId(...)
+ * .body(writer -> writer.writeDWord(1)
+ * .writeWord(2)
+ * .writeString("abc")
+ * );
+ * final ByteBuf result = builder.build();
+ * Assertions.assertEquals(1, result.refCnt());
+ *
+ * doSomeProcess(result);
+ *
+ * result.release();
+ * Assertions.assertEquals(0, result.refCnt());
+ * }
+ * }
+ *
+ * @return 方法返回的构建结果需要调用方自己 在恰当的时机释放。
+ * @apiNote 这个实现类 需要手动调用 {@link #release()} 方法以释放内部用到的 {@code ByteBuf} 临时变量。
+ * @see #release()
+ * @see ByteBufJt808MsgBuilder#close()
+ * @since 2.1.4
+ */
+ static ByteBufJt808MsgBuilder newByteBufBuilder(Jt808FlowIdGenerator flowIdGenerator) {
+ return new ByteBufJt808MsgBuilder(flowIdGenerator);
+ }
+
+ /**
+ * 不支持 重复调用 {@link #build()} 方法
+ *
+ * 推荐在 {@code try-with-resources} 块中使用:
+ *
{@code
+ * final Jt808MsgEncoder encoder = new DefaultJt808MsgEncoder(
+ * ByteBufAllocator.DEFAULT,
+ * new DefaultJt808MsgBytesProcessor(ByteBufAllocator.DEFAULT),
+ * responseSubPackage -> {
+ * },
+ * Jt808ResponseSubPackageStorage.NO_OPS_STORAGE,
+ * Jt808MsgEncryptionHandler.NO_OPS
+ * );
+ * final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer();
+ * try (var builder = Jt808MsgBuilder.newByteBufBuilder(flowIdGenerator, encoder, originalBuf)) {
+ * builder.version(...)
+ * .msgId(...)
+ * .terminalId(...)
+ * .body(writer -> writer.writeDWord(1)
+ * .writeWord(2)
+ * .writeString("abc")
+ * );
+ * final ByteBuf result = builder.build();
+ * Assertions.assertEquals(1, result.refCnt());
+ *
+ * doSomeProcess(result);
+ *
+ * result.release();
+ * Assertions.assertEquals(0, result.refCnt());
+ * }
+ * // try-with-resources 会自动释放 originalBuf
+ * Assertions.assertEquals(0, originalBuf.refCnt());
+ * }
+ *
+ * @return 方法返回的构建结果需要调用方自己 在恰当的时机释放。
+ * @apiNote 这个实现类 需要手动调用 {@link #release()} 方法以释放内部用到的 {@code ByteBuf body} 临时变量。
+ */
static ByteBufJt808MsgBuilder newByteBufBuilder(Jt808FlowIdGenerator flowIdGenerator, Jt808MsgEncoder encoder, ByteBuf body) {
return new ByteBufJt808MsgBuilder(flowIdGenerator, encoder, body);
}
+ /**
+ * 不支持 重复调用 {@link #build()} 方法
+ *
+ * 推荐在 {@code try-with-resources} 块中使用:
+ *
{@code
+ * final Jt808MsgEncoder encoder = new DefaultJt808MsgEncoder(
+ * ByteBufAllocator.DEFAULT,
+ * new DefaultJt808MsgBytesProcessor(ByteBufAllocator.DEFAULT),
+ * responseSubPackage -> {
+ * },
+ * Jt808ResponseSubPackageStorage.NO_OPS_STORAGE,
+ * Jt808MsgEncryptionHandler.NO_OPS
+ * );
+ * try (var builder = Jt808MsgBuilder.newByteBufBuilder(flowIdGenerator, encoder)) {
+ * builder.version(...)
+ * .msgId(...)
+ * .terminalId(...)
+ * .body(writer -> writer.writeDWord(1)
+ * .writeWord(2)
+ * .writeString("abc")
+ * );
+ * final ByteBuf result = builder.build();
+ * Assertions.assertEquals(1, result.refCnt());
+ *
+ * doSomeProcess(result);
+ *
+ * result.release();
+ * Assertions.assertEquals(0, result.refCnt());
+ * }
+ * }
+ *
+ * @return 方法返回的构建结果需要调用方自己 在恰当的时机释放。
+ * @apiNote 这个实现类 需要手动调用 {@link #release()} 方法以释放内部用到的 {@code ByteBuf} 临时变量。
+ * @since 2.1.4
+ */
+ static ByteBufJt808MsgBuilder newByteBufBuilder(Jt808FlowIdGenerator flowIdGenerator, Jt808MsgEncoder encoder) {
+ return new ByteBufJt808MsgBuilder(flowIdGenerator, encoder);
+ }
+
+ /**
+ * 支持 重复调用 {@link #build()} 方法
+ *
+ * @return 方法返回的构建结果需要调用方自己 在恰当的时机释放。
+ * @apiNote 这个实现类 无需调用 {@link #release()} 方法。
+ */
static EntityJt808MsgBuilder newEntityBuilder(Jt808FlowIdGenerator flowIdGenerator) {
return new EntityJt808MsgBuilder(flowIdGenerator);
}
+ /**
+ * 支持 重复调用 {@link #build()} 方法
+ *
+ * @return 方法返回的构建结果需要调用方自己 在恰当的时机释放。
+ * @apiNote 这个实现类 无需调用 {@link #release()} 方法。
+ */
static EntityJt808MsgBuilder newEntityBuilder(Jt808FlowIdGenerator flowIdGenerator, Jt808MsgEncoder encoder) {
return new EntityJt808MsgBuilder(flowIdGenerator, encoder);
}
@@ -76,6 +412,8 @@ default String toHexString() {
JtProtocolUtils.release(byteBuf);
}
throw e;
+ } finally {
+ JtProtocolUtils.release(byteBuf);
}
}
}
diff --git a/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/Jt808MsgEncryptionHandler.java b/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/Jt808MsgEncryptionHandler.java
index a02abe32..c426b8e0 100644
--- a/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/Jt808MsgEncryptionHandler.java
+++ b/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/Jt808MsgEncryptionHandler.java
@@ -1,5 +1,6 @@
package io.github.hylexus.jt.jt808.spec;
+import io.github.hylexus.jt.annotation.BuiltinComponent;
import io.github.hylexus.jt.jt808.support.annotation.msg.resp.Jt808ResponseBody;
import io.netty.buffer.ByteBuf;
@@ -34,6 +35,7 @@ public interface Jt808MsgEncryptionHandler {
Jt808MsgEncryptionHandler NO_OPS = new NoOps();
+ @BuiltinComponent
class NoOps implements Jt808MsgEncryptionHandler {
@Override
diff --git a/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/impl/msg/builder/ByteBufJt808MsgBuilder.java b/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/impl/msg/builder/ByteBufJt808MsgBuilder.java
index 4f15fa17..bfdf3e6e 100644
--- a/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/impl/msg/builder/ByteBufJt808MsgBuilder.java
+++ b/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/impl/msg/builder/ByteBufJt808MsgBuilder.java
@@ -20,13 +20,23 @@
* @author hylexus
*/
@UnstableApi
-public class ByteBufJt808MsgBuilder extends AbstractJt808MsgBuilder {
+public class ByteBufJt808MsgBuilder extends AbstractJt808MsgBuilder implements AutoCloseable {
+
+ private boolean alreadyBuilt = false;
+
+ public ByteBufJt808MsgBuilder(Jt808FlowIdGenerator flowIdGenerator, Jt808MsgEncoder encoder) {
+ this(flowIdGenerator, encoder, ByteBufAllocator.DEFAULT.buffer());
+ }
public ByteBufJt808MsgBuilder(Jt808FlowIdGenerator flowIdGenerator, Jt808MsgEncoder encoder, ByteBuf byteBuf) {
super(flowIdGenerator, encoder);
this.body = byteBuf;
}
+ public ByteBufJt808MsgBuilder(Jt808FlowIdGenerator flowIdGenerator) {
+ this(flowIdGenerator, ByteBufAllocator.DEFAULT.buffer());
+ }
+
public ByteBufJt808MsgBuilder(Jt808FlowIdGenerator flowIdGenerator, ByteBuf byteBuf) {
super(flowIdGenerator);
this.body = byteBuf;
@@ -44,6 +54,7 @@ public ByteBufJt808MsgBuilder(Jt808FlowIdGenerator flowIdGenerator, ByteBufAlloc
@Override
protected Jt808Response toJt808Response() {
+ this.alreadyBuilt = true;
return new DefaultJt808Response(
requireNonNull(version, "version() can not be null"),
assertThat(msgId, id -> id != null && id > 0, "msgId() can not be null"),
@@ -57,9 +68,9 @@ protected Jt808Response toJt808Response() {
}
public ByteBufJt808MsgBuilder body(Consumer consumer) {
- final Jt808ByteWriter writer = Jt808ByteWriter.of(body());
+ final Jt808ByteWriter writer = Jt808ByteWriter.of(this.body);
consumer.accept(writer);
- return super.body(body);
+ return this;
}
@Override
@@ -74,6 +85,10 @@ public ByteBufJt808MsgBuilder body(ByteBuf body) {
@Override
public ByteBuf build() {
+ if (this.alreadyBuilt) {
+ throw new IllegalStateException("ByteBufJt808MsgBuilder can not be built more than once.");
+ }
+
Jt808Response jt808Response = null;
try {
jt808Response = this.toJt808Response();
@@ -92,4 +107,9 @@ public ByteBufJt808MsgBuilder release() {
JtProtocolUtils.release(this.body);
return this;
}
+
+ @Override
+ public void close() {
+ this.release();
+ }
}
diff --git a/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/impl/msg/builder/RebuildableByteBufJt808MsgBuilder.java b/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/impl/msg/builder/RebuildableByteBufJt808MsgBuilder.java
new file mode 100644
index 00000000..3b855dc1
--- /dev/null
+++ b/jt-808-server-support/src/main/java/io/github/hylexus/jt/jt808/spec/impl/msg/builder/RebuildableByteBufJt808MsgBuilder.java
@@ -0,0 +1,104 @@
+package io.github.hylexus.jt.jt808.spec.impl.msg.builder;
+
+import io.github.hylexus.jt.jt808.spec.Jt808Response;
+import io.github.hylexus.jt.jt808.spec.impl.response.DefaultJt808Response;
+import io.github.hylexus.jt.jt808.spec.session.Jt808FlowIdGenerator;
+import io.github.hylexus.jt.jt808.support.codec.Jt808ByteWriter;
+import io.github.hylexus.jt.jt808.support.codec.Jt808MsgBytesProcessor;
+import io.github.hylexus.jt.jt808.support.codec.Jt808MsgEncoder;
+import io.github.hylexus.jt.jt808.support.utils.JtProtocolUtils;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+
+import java.util.function.Consumer;
+
+import static io.github.hylexus.jt.utils.Assertions.assertThat;
+import static io.github.hylexus.jt.utils.Assertions.requireNonNull;
+
+public class RebuildableByteBufJt808MsgBuilder extends AbstractJt808MsgBuilder implements AutoCloseable {
+
+
+ public RebuildableByteBufJt808MsgBuilder(Jt808FlowIdGenerator flowIdGenerator, Jt808MsgEncoder encoder) {
+ this(flowIdGenerator, encoder, ByteBufAllocator.DEFAULT.buffer());
+ }
+
+ public RebuildableByteBufJt808MsgBuilder(Jt808FlowIdGenerator flowIdGenerator, Jt808MsgEncoder encoder, ByteBuf byteBuf) {
+ super(flowIdGenerator, encoder);
+ this.body = byteBuf;
+ }
+
+ public RebuildableByteBufJt808MsgBuilder(Jt808FlowIdGenerator flowIdGenerator) {
+ this(flowIdGenerator, ByteBufAllocator.DEFAULT.buffer());
+ }
+
+ public RebuildableByteBufJt808MsgBuilder(Jt808FlowIdGenerator flowIdGenerator, ByteBuf byteBuf) {
+ super(flowIdGenerator);
+ this.body = byteBuf;
+ }
+
+ public RebuildableByteBufJt808MsgBuilder(Jt808FlowIdGenerator flowIdGenerator, ByteBufAllocator allocator, ByteBuf byteBuf) {
+ super(flowIdGenerator, allocator);
+ this.body = byteBuf;
+ }
+
+ public RebuildableByteBufJt808MsgBuilder(Jt808FlowIdGenerator flowIdGenerator, ByteBufAllocator allocator, Jt808MsgBytesProcessor msgBytesProcessor, ByteBuf byteBuf) {
+ super(flowIdGenerator, allocator, msgBytesProcessor);
+ this.body = byteBuf;
+ }
+
+ @Override
+ protected Jt808Response toJt808Response() {
+ return new DefaultJt808Response(
+ requireNonNull(version, "version() can not be null"),
+ assertThat(msgId, id -> id != null && id > 0, "msgId() can not be null"),
+ encryptionType,
+ maxPackageSize == null ? Jt808Response.DEFAULT_MAX_PACKAGE_SIZE : maxPackageSize,
+ reversedBit15InHeader == null ? 0 : reversedBit15InHeader,
+ requireNonNull(terminalId, "terminalId() can not be null"),
+ -1,
+ requireNonNull(body, "body() can not be null").copy()
+ );
+ }
+
+ public RebuildableByteBufJt808MsgBuilder body(Consumer consumer) {
+ final Jt808ByteWriter writer = Jt808ByteWriter.of(this.body);
+ consumer.accept(writer);
+ return this;
+ }
+
+ @Override
+ public RebuildableByteBufJt808MsgBuilder body(ByteBuf body) {
+ final ByteBuf old = this.body();
+ try {
+ return super.body(body);
+ } finally {
+ JtProtocolUtils.release(old);
+ }
+ }
+
+ @Override
+ public ByteBuf build() {
+ Jt808Response jt808Response = null;
+ try {
+ jt808Response = this.toJt808Response();
+ return this.encoder.encode(jt808Response, this.flowIdGenerator);
+ } catch (Exception e) {
+ if (jt808Response != null) {
+ jt808Response.release();
+ }
+ this.release();
+ throw e;
+ }
+ }
+
+ @Override
+ public RebuildableByteBufJt808MsgBuilder release() {
+ JtProtocolUtils.release(this.body);
+ return this;
+ }
+
+ @Override
+ public void close() {
+ this.release();
+ }
+}
diff --git a/jt-808-server-support/src/test/java/io/github/hylexus/jt/jt808/spec/Jt808MsgBuilderTest.java b/jt-808-server-support/src/test/java/io/github/hylexus/jt/jt808/spec/Jt808MsgBuilderTest.java
index 336b4903..c70dc51d 100644
--- a/jt-808-server-support/src/test/java/io/github/hylexus/jt/jt808/spec/Jt808MsgBuilderTest.java
+++ b/jt-808-server-support/src/test/java/io/github/hylexus/jt/jt808/spec/Jt808MsgBuilderTest.java
@@ -9,7 +9,7 @@
import io.github.hylexus.jt.jt808.support.annotation.msg.resp.ResponseField;
import io.github.hylexus.jt.utils.HexStringUtils;
import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.Unpooled;
import lombok.Data;
import lombok.experimental.Accessors;
import org.junit.jupiter.api.Test;
@@ -73,32 +73,35 @@ void testEntityMsgBuilder() {
@Test
public void testByteBufMsgBuilder() {
- final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer(128);
- final ByteBufJt808MsgBuilder builder = Jt808MsgBuilder.newByteBufBuilder(ALWAYS_RETURN_0, originalBuf)
- .version(Jt808ProtocolVersion.VERSION_2013)
- .msgId(BuiltinJt808MsgType.CLIENT_COMMON_REPLY)
- .terminalId("013912344323")
- // 消息体借助 Jt808ByteWriter 来写入内容
- // 也可以直接提供一个已经写好内容的 ByteBuf 用来充当消息体
- .body(writer -> writer
- // 1. 应答流水号 WORD 对应的平台消息的流水号
- .writeWord(0)
- // 2. 应答id WORD 对应的平台消息的 ID
- .writeWord(0x8103)
- // 3. 结果 byte 0:成功/确认;1:失败;2:消息有误;3:不支持
- .writeByte(0)
- );
-
- final ByteBuf result = builder.build();
- assertEquals("7E0001000501391234432300000000810300F87E", HexStringUtils.byteBufToString(result));
- assertEquals("7E0001000501391234432300000000810300F87E", builder.toHexString());
-
- assertEquals(1, originalBuf.refCnt());
- assertEquals(result.refCnt(), originalBuf.refCnt());
-
- result.release();
-
+ // final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer(128);
+ final ByteBuf originalBuf = Unpooled.buffer(128);
+ try (ByteBufJt808MsgBuilder builder = Jt808MsgBuilder.newByteBufBuilder(ALWAYS_RETURN_0, originalBuf)) {
+ builder.version(Jt808ProtocolVersion.VERSION_2013)
+ .msgId(BuiltinJt808MsgType.CLIENT_COMMON_REPLY)
+ .terminalId("013912344323")
+ // 消息体借助 Jt808ByteWriter 来写入内容
+ // 也可以直接提供一个已经写好内容的 ByteBuf 用来充当消息体
+ .body(writer -> writer
+ // 1. 应答流水号 WORD 对应的平台消息的流水号
+ .writeWord(0)
+ // 2. 应答id WORD 对应的平台消息的 ID
+ .writeWord(0x8103)
+ // 3. 结果 byte 0:成功/确认;1:失败;2:消息有误;3:不支持
+ .writeByte(0)
+ );
+
+ final ByteBuf result = builder.build();
+ assertEquals("7E0001000501391234432300000000810300F87E", HexStringUtils.byteBufToString(result));
+
+ assertEquals(1, result.refCnt());
+ assertEquals(1, originalBuf.refCnt());
+
+ // 在恰当的时机释放构建结果
+ result.release();
+ assertEquals(0, result.refCnt());
+ }
+
+ // try-with-resource 释放了 originalBuf
assertEquals(0, originalBuf.refCnt());
- assertEquals(result.refCnt(), originalBuf.refCnt());
}
-}
\ No newline at end of file
+}
diff --git a/jt-808-server-support/src/test/java/io/github/hylexus/jt/jt808/spec/impl/msg/builder/ByteBufJt808MsgBuilderTest.java b/jt-808-server-support/src/test/java/io/github/hylexus/jt/jt808/spec/impl/msg/builder/ByteBufJt808MsgBuilderTest.java
new file mode 100644
index 00000000..3de21574
--- /dev/null
+++ b/jt-808-server-support/src/test/java/io/github/hylexus/jt/jt808/spec/impl/msg/builder/ByteBufJt808MsgBuilderTest.java
@@ -0,0 +1,124 @@
+package io.github.hylexus.jt.jt808.spec.impl.msg.builder;
+
+import io.github.hylexus.jt.jt808.Jt808ProtocolVersion;
+import io.github.hylexus.jt.jt808.spec.Jt808MsgBuilder;
+import io.github.hylexus.jt.jt808.spec.Jt808MsgEncryptionHandler;
+import io.github.hylexus.jt.jt808.spec.session.Jt808FlowIdGenerator;
+import io.github.hylexus.jt.jt808.support.codec.Jt808MsgEncoder;
+import io.github.hylexus.jt.jt808.support.codec.Jt808ResponseSubPackageStorage;
+import io.github.hylexus.jt.jt808.support.codec.impl.DefaultJt808MsgBytesProcessor;
+import io.github.hylexus.jt.jt808.support.codec.impl.DefaultJt808MsgEncoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class ByteBufJt808MsgBuilderTest {
+
+ Jt808FlowIdGenerator flowIdGenerator = step -> 0;
+
+ @Test
+ void test1() {
+ try (var builder = Jt808MsgBuilder.newByteBufBuilder(flowIdGenerator)) {
+ builder.version(Jt808ProtocolVersion.VERSION_2019)
+ .msgId(0x1111)
+ .terminalId("013912344323")
+ .body(writer -> writer.writeDWord(1)
+ .writeWord(2)
+ .writeString("abc")
+ );
+ final ByteBuf result = builder.build();
+ Assertions.assertEquals(1, result.refCnt());
+
+ doSomeProcess(result);
+
+ result.release();
+ Assertions.assertEquals(0, result.refCnt());
+ }
+ }
+
+ @Test
+ void test2() {
+ final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer();
+ try (var builder = Jt808MsgBuilder.newByteBufBuilder(flowIdGenerator, originalBuf)) {
+ builder.version(Jt808ProtocolVersion.VERSION_2019)
+ .msgId(0x1111)
+ .terminalId("013912344323")
+ .body(writer -> writer.writeDWord(1)
+ .writeWord(2)
+ .writeString("abc")
+ );
+ final ByteBuf result = builder.build();
+ Assertions.assertEquals(1, result.refCnt());
+
+ doSomeProcess(result);
+
+ result.release();
+ Assertions.assertEquals(0, result.refCnt());
+ }
+ // try-with-resources 会自动释放 originalBuf
+ Assertions.assertEquals(0, originalBuf.refCnt());
+ }
+
+ @Test
+ void test3() {
+ final Jt808MsgEncoder encoder = new DefaultJt808MsgEncoder(
+ ByteBufAllocator.DEFAULT,
+ new DefaultJt808MsgBytesProcessor(ByteBufAllocator.DEFAULT),
+ responseSubPackage -> {
+ },
+ Jt808ResponseSubPackageStorage.NO_OPS_STORAGE,
+ Jt808MsgEncryptionHandler.NO_OPS
+ );
+ final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer();
+ try (var builder = Jt808MsgBuilder.newByteBufBuilder(flowIdGenerator, encoder, originalBuf)) {
+ builder.version(Jt808ProtocolVersion.VERSION_2019)
+ .msgId(0x1111)
+ .terminalId("013912344323")
+ .body(writer -> writer.writeDWord(1)
+ .writeWord(2)
+ .writeString("abc")
+ );
+ final ByteBuf result = builder.build();
+ Assertions.assertEquals(1, result.refCnt());
+
+ doSomeProcess(result);
+
+ result.release();
+ Assertions.assertEquals(0, result.refCnt());
+ }
+ // try-with-resources 会自动释放 originalBuf
+ Assertions.assertEquals(0, originalBuf.refCnt());
+ }
+
+ @Test
+ void test4() {
+ final Jt808MsgEncoder encoder = new DefaultJt808MsgEncoder(
+ ByteBufAllocator.DEFAULT,
+ new DefaultJt808MsgBytesProcessor(ByteBufAllocator.DEFAULT),
+ responseSubPackage -> {
+ },
+ Jt808ResponseSubPackageStorage.NO_OPS_STORAGE,
+ Jt808MsgEncryptionHandler.NO_OPS
+ );
+ try (var builder = Jt808MsgBuilder.newByteBufBuilder(flowIdGenerator, encoder)) {
+ builder.version(Jt808ProtocolVersion.VERSION_2019)
+ .msgId(0x1111)
+ .terminalId("013912344323")
+ .body(writer -> writer.writeDWord(1)
+ .writeWord(2)
+ .writeString("abc")
+ );
+ final ByteBuf result = builder.build();
+ Assertions.assertEquals(1, result.refCnt());
+
+ doSomeProcess(result);
+
+ result.release();
+ Assertions.assertEquals(0, result.refCnt());
+ }
+ }
+
+ private void doSomeProcess(ByteBuf ignore) {
+ }
+}
diff --git a/jt-808-server-support/src/test/java/io/github/hylexus/jt/jt808/spec/impl/msg/builder/RebuildableByteBufJt808MsgBuilderTest.java b/jt-808-server-support/src/test/java/io/github/hylexus/jt/jt808/spec/impl/msg/builder/RebuildableByteBufJt808MsgBuilderTest.java
new file mode 100644
index 00000000..258d8959
--- /dev/null
+++ b/jt-808-server-support/src/test/java/io/github/hylexus/jt/jt808/spec/impl/msg/builder/RebuildableByteBufJt808MsgBuilderTest.java
@@ -0,0 +1,165 @@
+package io.github.hylexus.jt.jt808.spec.impl.msg.builder;
+
+import io.github.hylexus.jt.jt808.Jt808ProtocolVersion;
+import io.github.hylexus.jt.jt808.spec.Jt808MsgBuilder;
+import io.github.hylexus.jt.jt808.spec.Jt808MsgEncryptionHandler;
+import io.github.hylexus.jt.jt808.spec.impl.BuiltinJt808MsgType;
+import io.github.hylexus.jt.jt808.spec.session.Jt808FlowIdGenerator;
+import io.github.hylexus.jt.jt808.support.codec.Jt808MsgEncoder;
+import io.github.hylexus.jt.jt808.support.codec.Jt808ResponseSubPackageStorage;
+import io.github.hylexus.jt.jt808.support.codec.impl.DefaultJt808MsgBytesProcessor;
+import io.github.hylexus.jt.jt808.support.codec.impl.DefaultJt808MsgEncoder;
+import io.github.hylexus.jt.utils.FormatUtils;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class RebuildableByteBufJt808MsgBuilderTest {
+ Jt808FlowIdGenerator flowIdGenerator = step -> 0;
+
+
+ @Test
+ void test1() {
+ try (var builder = Jt808MsgBuilder.newRebuildableByteBufBuilder(flowIdGenerator)) {
+ builder.version(Jt808ProtocolVersion.VERSION_2019)
+ .msgId(0x1111)
+ .terminalId("013912344323")
+ .body(writer -> writer.writeDWord(1)
+ .writeWord(2)
+ .writeString("abc")
+ );
+ final ByteBuf result = builder.build();
+ Assertions.assertEquals(1, result.refCnt());
+
+ doSomeProcess(result);
+
+ result.release();
+ Assertions.assertEquals(0, result.refCnt());
+
+ final ByteBuf result2 = builder.build();
+ Assertions.assertEquals(1, result2.refCnt());
+
+ doSomeProcess(result2);
+
+ result2.release();
+ Assertions.assertEquals(0, result2.refCnt());
+ }
+ }
+
+ @Test
+ void test2() {
+ final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer();
+ try (var builder = Jt808MsgBuilder.newRebuildableByteBufBuilder(flowIdGenerator, originalBuf)) {
+ builder.version(Jt808ProtocolVersion.VERSION_2013)
+ .msgId(BuiltinJt808MsgType.CLIENT_COMMON_REPLY)
+ .terminalId("013912344323")
+ // 消息体借助 Jt808ByteWriter 来写入内容
+ // 也可以直接提供一个已经写好内容的 ByteBuf 用来充当消息体
+ .body(writer -> writer
+ // 1. 应答流水号 WORD 对应的平台消息的流水号
+ .writeWord(0)
+ // 2. 应答id WORD 对应的平台消息的 ID
+ .writeWord(0x8103)
+ // 3. 结果 byte 0:成功/确认;1:失败;2:消息有误;3:不支持
+ .writeByte(0)
+ );
+ final ByteBuf result = builder.build();
+ Assertions.assertEquals(1, result.refCnt());
+ Assertions.assertEquals("7E0001000501391234432300000000810300F87E", FormatUtils.toHexString(result));
+
+ doSomeProcess(result);
+
+ result.release();
+ Assertions.assertEquals(0, result.refCnt());
+
+ final ByteBuf result2 = builder.build();
+ Assertions.assertEquals(1, result2.refCnt());
+
+ doSomeProcess(result2);
+
+ result2.release();
+ Assertions.assertEquals(0, result2.refCnt());
+ }
+ // try-with-resources 会自动释放 originalBuf
+ Assertions.assertEquals(0, originalBuf.refCnt());
+ }
+
+ @Test
+ void test3() {
+ final Jt808MsgEncoder encoder = new DefaultJt808MsgEncoder(
+ ByteBufAllocator.DEFAULT,
+ new DefaultJt808MsgBytesProcessor(ByteBufAllocator.DEFAULT),
+ responseSubPackage -> {
+ },
+ Jt808ResponseSubPackageStorage.NO_OPS_STORAGE,
+ Jt808MsgEncryptionHandler.NO_OPS
+ );
+ final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer();
+ try (var builder = Jt808MsgBuilder.newRebuildableByteBufBuilder(flowIdGenerator, encoder, originalBuf)) {
+ builder.version(Jt808ProtocolVersion.VERSION_2019)
+ .msgId(0x1111)
+ .terminalId("013912344323")
+ .body(writer -> writer.writeDWord(1)
+ .writeWord(2)
+ .writeString("abc")
+ );
+ final ByteBuf result = builder.build();
+ Assertions.assertEquals(1, result.refCnt());
+
+ doSomeProcess(result);
+
+ result.release();
+ Assertions.assertEquals(0, result.refCnt());
+
+ final ByteBuf result2 = builder.build();
+ Assertions.assertEquals(1, result2.refCnt());
+
+ doSomeProcess(result2);
+
+ result2.release();
+ Assertions.assertEquals(0, result2.refCnt());
+ }
+ // try-with-resources 会自动释放 originalBuf
+ Assertions.assertEquals(0, originalBuf.refCnt());
+ }
+
+ @Test
+ void test4() {
+ final Jt808MsgEncoder encoder = new DefaultJt808MsgEncoder(
+ ByteBufAllocator.DEFAULT,
+ new DefaultJt808MsgBytesProcessor(ByteBufAllocator.DEFAULT),
+ responseSubPackage -> {
+ },
+ Jt808ResponseSubPackageStorage.NO_OPS_STORAGE,
+ Jt808MsgEncryptionHandler.NO_OPS
+ );
+ try (var builder = Jt808MsgBuilder.newRebuildableByteBufBuilder(flowIdGenerator, encoder)) {
+ builder.version(Jt808ProtocolVersion.VERSION_2019)
+ .msgId(0x1111)
+ .terminalId("013912344323")
+ .body(writer -> writer.writeDWord(1)
+ .writeWord(2)
+ .writeString("abc")
+ );
+ final ByteBuf result = builder.build();
+ Assertions.assertEquals(1, result.refCnt());
+
+ doSomeProcess(result);
+
+ result.release();
+ Assertions.assertEquals(0, result.refCnt());
+
+ final ByteBuf result2 = builder.build();
+ Assertions.assertEquals(1, result2.refCnt());
+
+ doSomeProcess(result2);
+
+ result2.release();
+ Assertions.assertEquals(0, result2.refCnt());
+ }
+ }
+
+ private void doSomeProcess(ByteBuf ignore) {
+ }
+}
diff --git a/jt-core/jt-core.gradle b/jt-core/jt-core.gradle
index cbefdb8d..1d23c240 100644
--- a/jt-core/jt-core.gradle
+++ b/jt-core/jt-core.gradle
@@ -26,4 +26,6 @@ test {
exclude '**/CommandWaitingPoolTest.class'
useJUnitPlatform()
+ // https://github.com/gradle/gradle/issues/7773
+ systemProperties(System.properties)
}
diff --git a/samples/jt-1078-server-sample-webflux-boot3/jt-1078-server-sample-webflux-boot3.gradle b/samples/jt-1078-server-sample-webflux-boot3/jt-1078-server-sample-webflux-boot3.gradle
index c3ee41c5..8c8a560c 100644
--- a/samples/jt-1078-server-sample-webflux-boot3/jt-1078-server-sample-webflux-boot3.gradle
+++ b/samples/jt-1078-server-sample-webflux-boot3/jt-1078-server-sample-webflux-boot3.gradle
@@ -26,4 +26,6 @@ test {
exclude '**/ClientTest.class'
useJUnitPlatform()
+ // https://github.com/gradle/gradle/issues/7773
+ systemProperties(System.properties)
}
diff --git a/samples/jt-1078-server-sample-webmvc-boot3/jt-1078-server-sample-webmvc-boot3.gradle b/samples/jt-1078-server-sample-webmvc-boot3/jt-1078-server-sample-webmvc-boot3.gradle
index b67c27d5..2cd9d3ec 100644
--- a/samples/jt-1078-server-sample-webmvc-boot3/jt-1078-server-sample-webmvc-boot3.gradle
+++ b/samples/jt-1078-server-sample-webmvc-boot3/jt-1078-server-sample-webmvc-boot3.gradle
@@ -26,4 +26,6 @@ dependencies {
test {
useJUnitPlatform()
+ // https://github.com/gradle/gradle/issues/7773
+ systemProperties(System.properties)
}
diff --git a/samples/jt-808-client-sample-debug/jt-808-client-sample-debug.gradle b/samples/jt-808-client-sample-debug/jt-808-client-sample-debug.gradle
index 468e852f..b3bce45e 100644
--- a/samples/jt-808-client-sample-debug/jt-808-client-sample-debug.gradle
+++ b/samples/jt-808-client-sample-debug/jt-808-client-sample-debug.gradle
@@ -23,4 +23,6 @@ dependencies {
test {
useJUnitPlatform()
+ // https://github.com/gradle/gradle/issues/7773
+ systemProperties(System.properties)
}
diff --git a/samples/jt-808-client-sample-debug/src/main/java/io/github/hylexus/jt808/samples/client/debug/client1/MessageGenerator.java b/samples/jt-808-client-sample-debug/src/main/java/io/github/hylexus/jt808/samples/client/debug/client1/MessageGenerator.java
index 3a093f1e..b695fb68 100644
--- a/samples/jt-808-client-sample-debug/src/main/java/io/github/hylexus/jt808/samples/client/debug/client1/MessageGenerator.java
+++ b/samples/jt-808-client-sample-debug/src/main/java/io/github/hylexus/jt808/samples/client/debug/client1/MessageGenerator.java
@@ -3,6 +3,7 @@
import io.github.hylexus.jt.jt808.Jt808ProtocolVersion;
import io.github.hylexus.jt.jt808.spec.Jt808MsgBuilder;
import io.github.hylexus.jt.jt808.spec.impl.BuiltinJt808MsgType;
+import io.github.hylexus.jt.jt808.spec.impl.msg.builder.ByteBufJt808MsgBuilder;
import io.github.hylexus.jt.jt808.spec.session.Jt808FlowIdGenerator;
import io.github.hylexus.jt808.samples.client.debug.client1.entity.ClientLocationUploadMsgV2019;
import io.github.hylexus.jt808.samples.client.debug.client1.entity.ClientRegisterMsgV2013;
@@ -77,25 +78,27 @@ public static String formatTerminalId(String input, Jt808ProtocolVersion version
}
public ByteBuf randomClientRegisterV2019(Jt808FlowIdGenerator flowIdGenerator, String terminalId) {
- return Jt808MsgBuilder.newByteBufBuilder(flowIdGenerator, ByteBufAllocator.DEFAULT.buffer(128))
- .version(VERSION_2019)
- .msgId(BuiltinJt808MsgType.CLIENT_REGISTER)
- .terminalId(terminalId)
- .body(writer -> writer
- // 省域ID WORD
- .writeWord(11)
- // 市县域ID WORD
- .writeWord(2)
- // 制造商ID byte[11]
- .writeString("id987654321")
- // 终端型号 byte[30]
- .writeString("type00123456781234567887654321")
- // 终端ID byte[30]
- .writeString("ID0000123456781234567887654321")
- .writeByte(1)
- .writeString("甘J-123459")
- )
- .build();
+ try (ByteBufJt808MsgBuilder builder = Jt808MsgBuilder.newByteBufBuilder(flowIdGenerator, ByteBufAllocator.DEFAULT.buffer(128))) {
+ return builder
+ .version(VERSION_2019)
+ .msgId(BuiltinJt808MsgType.CLIENT_REGISTER)
+ .terminalId(terminalId)
+ .body(writer -> writer
+ // 省域ID WORD
+ .writeWord(11)
+ // 市县域ID WORD
+ .writeWord(2)
+ // 制造商ID byte[11]
+ .writeString("id987654321")
+ // 终端型号 byte[30]
+ .writeString("type00123456781234567887654321")
+ // 终端ID byte[30]
+ .writeString("ID0000123456781234567887654321")
+ .writeByte(1)
+ .writeString("甘J-123459")
+ )
+ .build();
+ }
}
public ByteBuf randomClientRegisterV2013(Jt808FlowIdGenerator flowIdGenerator, String terminalId) {
@@ -140,32 +143,34 @@ public ByteBuf randomLocationMsgV2013(Jt808FlowIdGenerator flowIdGenerator, Stri
public ByteBuf randomLocationMsgV2019(Jt808FlowIdGenerator flowIdGenerator, String terminalId, int maxPackageSize) {
final int mileage = mileages[new Random().nextInt(3)];
- return Jt808MsgBuilder.newByteBufBuilder(flowIdGenerator, ByteBufAllocator.DEFAULT.buffer(256))
- .version(VERSION_2019)
- .msgId(BuiltinJt808MsgType.CLIENT_LOCATION_INFO_UPLOAD)
- .terminalId(terminalId)
- .maxPackageSize(maxPackageSize)
- .body(writer -> writer
- .writeDWord(0)
- .writeDWord(0)
- .writeDWord(31235930 + ThreadLocalRandom.current().nextInt(100000))
- .writeDWord(121480540 + ThreadLocalRandom.current().nextInt(100000))
- // 16 height
- .writeWord(22)
- .writeWord(100)
- .writeWord(11)
- // 22 time
- .writeBcd("220322001214")
- // item1
- .writeByte(0x01)
- .writeByte(4)
- .writeDWord(ThreadLocalRandom.current().nextInt(mileage))
- //
- // item2
- .writeByte(0x02)
- .writeByte(2)
- .writeWord(99)
- )
- .build();
+ try (ByteBufJt808MsgBuilder builder = Jt808MsgBuilder.newByteBufBuilder(flowIdGenerator, ByteBufAllocator.DEFAULT.buffer(256))) {
+ return builder
+ .version(VERSION_2019)
+ .msgId(BuiltinJt808MsgType.CLIENT_LOCATION_INFO_UPLOAD)
+ .terminalId(terminalId)
+ .maxPackageSize(maxPackageSize)
+ .body(writer -> writer
+ .writeDWord(0)
+ .writeDWord(0)
+ .writeDWord(31235930 + ThreadLocalRandom.current().nextInt(100000))
+ .writeDWord(121480540 + ThreadLocalRandom.current().nextInt(100000))
+ // 16 height
+ .writeWord(22)
+ .writeWord(100)
+ .writeWord(11)
+ // 22 time
+ .writeBcd("220322001214")
+ // item1
+ .writeByte(0x01)
+ .writeByte(4)
+ .writeDWord(ThreadLocalRandom.current().nextInt(mileage))
+ //
+ // item2
+ .writeByte(0x02)
+ .writeByte(2)
+ .writeWord(99)
+ )
+ .build();
+ }
}
-}
\ No newline at end of file
+}
diff --git a/samples/jt-808-server-sample-annotation/jt-808-server-sample-annotation.gradle b/samples/jt-808-server-sample-annotation/jt-808-server-sample-annotation.gradle
index 2ef71d99..0dba3348 100644
--- a/samples/jt-808-server-sample-annotation/jt-808-server-sample-annotation.gradle
+++ b/samples/jt-808-server-sample-annotation/jt-808-server-sample-annotation.gradle
@@ -26,4 +26,6 @@ dependencies {
test {
useJUnitPlatform()
+ // https://github.com/gradle/gradle/issues/7773
+ systemProperties(System.properties)
}
diff --git a/samples/jt-808-server-sample-debug/jt-808-server-sample-debug.gradle b/samples/jt-808-server-sample-debug/jt-808-server-sample-debug.gradle
index c8436c8e..7318d71f 100644
--- a/samples/jt-808-server-sample-debug/jt-808-server-sample-debug.gradle
+++ b/samples/jt-808-server-sample-debug/jt-808-server-sample-debug.gradle
@@ -28,4 +28,6 @@ dependencies {
test {
useJUnitPlatform()
+ // https://github.com/gradle/gradle/issues/7773
+ systemProperties(System.properties)
}
diff --git a/samples/jt-808-server-sample-debug/src/main/java/io/github/hylexus/jt/jt808/samples/debug/Jt808ServerSampleDebugApplication.java b/samples/jt-808-server-sample-debug/src/main/java/io/github/hylexus/jt/jt808/samples/debug/Jt808ServerSampleDebugApplication.java
index b2cdf611..5867c5d3 100644
--- a/samples/jt-808-server-sample-debug/src/main/java/io/github/hylexus/jt/jt808/samples/debug/Jt808ServerSampleDebugApplication.java
+++ b/samples/jt-808-server-sample-debug/src/main/java/io/github/hylexus/jt/jt808/samples/debug/Jt808ServerSampleDebugApplication.java
@@ -11,6 +11,8 @@
public class Jt808ServerSampleDebugApplication {
public static void main(String[] args) {
+ // System.setProperty("io.netty.allocator.type", "unpooled");
+ // System.setProperty("io.netty.allocator.type","pooled");
// FIXME 如果你不了解 ResourceLeakDetector 是做什么的,请务必注释掉下面这行代码!!!
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);
SpringApplication.run(Jt808ServerSampleDebugApplication.class, args);
diff --git a/samples/jt-808-server-sample-debug/src/test/java/io/github/hylexus/jt/jt808/spec/Jt808MsgBuilderEncryptTest.java b/samples/jt-808-server-sample-debug/src/test/java/io/github/hylexus/jt/jt808/spec/Jt808MsgBuilderEncryptTest.java
index 26e6b61f..b0b268c9 100644
--- a/samples/jt-808-server-sample-debug/src/test/java/io/github/hylexus/jt/jt808/spec/Jt808MsgBuilderEncryptTest.java
+++ b/samples/jt-808-server-sample-debug/src/test/java/io/github/hylexus/jt/jt808/spec/Jt808MsgBuilderEncryptTest.java
@@ -4,6 +4,7 @@
import io.github.hylexus.jt.jt808.samples.debug.handler.Jt808MsgEncryptionHandlerDemo01;
import io.github.hylexus.jt.jt808.spec.impl.BuiltinJt808MsgType;
import io.github.hylexus.jt.jt808.spec.impl.msg.builder.EntityJt808MsgBuilder;
+import io.github.hylexus.jt.jt808.spec.impl.msg.builder.RebuildableByteBufJt808MsgBuilder;
import io.github.hylexus.jt.jt808.spec.session.Jt808FlowIdGenerator;
import io.github.hylexus.jt.jt808.support.annotation.msg.resp.Jt808ResponseBody;
import io.github.hylexus.jt.jt808.support.annotation.msg.resp.ResponseFieldAlias;
@@ -13,8 +14,10 @@
import io.github.hylexus.jt.utils.HexStringUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.Unpooled;
import lombok.Data;
import lombok.experimental.Accessors;
+import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -22,6 +25,7 @@
/**
* @author hylexus
*/
+@Slf4j
public class Jt808MsgBuilderEncryptTest {
// 这里使用-1,通过 Jt808MsgBuilder.msgId(int msgId) 来指定了消息ID
@@ -105,35 +109,43 @@ void testEntityMsgBuilder() {
assertEquals(0, result.refCnt());
}
- // @Test
- // public void testByteBufMsgBuilder() {
- // final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer(128);
- // final ByteBufJt808MsgBuilder builder = Jt808MsgBuilder.newByteBufBuilder(ALWAYS_RETURN_1, encoder, originalBuf)
- // .version(Jt808ProtocolVersion.VERSION_2013)
- // .msgId(BuiltinJt808MsgType.CLIENT_COMMON_REPLY)
- // .terminalId("013912344323")
- // .encryptionType(0b010)
- // // 消息体借助 Jt808ByteWriter 来写入内容
- // // 也可以直接提供一个已经写好内容的 ByteBuf 用来充当消息体
- // .body(writer -> writer
- // // 1. 应答流水号 WORD 对应的平台消息的流水号
- // .writeWord(0)
- // // 2. 应答id WORD 对应的平台消息的 ID
- // .writeWord(0x8103)
- // // 3. 结果 byte 0:成功/确认;1:失败;2:消息有误;3:不支持
- // .writeByte(0)
- // );
- //
- // final ByteBuf result = builder.build();
- // assertEquals("7E000108100139123443230001B35513C19F57CC81A1CA86622E4E506C047E", HexStringUtils.byteBufToString(result));
- // assertEquals("7E000108100139123443230001B35513C19F57CC81A1CA86622E4E506C047E", builder.toHexString());
- //
- // assertEquals(1, originalBuf.refCnt());
- // assertEquals(result.refCnt(), originalBuf.refCnt());
- //
- // result.release();
- //
- // assertEquals(0, originalBuf.refCnt());
- // assertEquals(result.refCnt(), originalBuf.refCnt());
- // }
+ @Test
+ public void testByteBufMsgBuilder() {
+ // `./gradlew clean build -Dio.netty.allocator.type=pooled`
+ // `./gradlew clean build -Dio.netty.allocator.type=unpooled`
+ // final ByteBuf originalBuf = ByteBufAllocator.DEFAULT.buffer(128);
+ final ByteBuf originalBuf = Unpooled.buffer(128);
+
+ try (RebuildableByteBufJt808MsgBuilder builder = Jt808MsgBuilder.newRebuildableByteBufBuilder(ALWAYS_RETURN_1, encoder, originalBuf)) {
+
+ builder.version(Jt808ProtocolVersion.VERSION_2013)
+ .msgId(BuiltinJt808MsgType.CLIENT_COMMON_REPLY)
+ .terminalId("013912344323")
+ .encryptionType(0b010)
+ // 消息体借助 Jt808ByteWriter 来写入内容
+ // 也可以直接提供一个已经写好内容的 ByteBuf 用来充当消息体
+ .body(writer -> writer
+ // 1. 应答流水号 WORD 对应的平台消息的流水号
+ .writeWord(0x1111)
+ // 2. 应答id WORD 对应的平台消息的 ID
+ .writeWord(0x8103)
+ // 3. 结果 byte 0:成功/确认;1:失败;2:消息有误;3:不支持
+ .writeByte(0x22)
+ );
+
+ final ByteBuf result = builder.build();
+ assertEquals("7E000108100139123443230001061EE8FEE8B9FA59FAF19642F93BC9E5AB7E", HexStringUtils.byteBufToString(result));
+ assertEquals("7E000108100139123443230001061EE8FEE8B9FA59FAF19642F93BC9E5AB7E", builder.toHexString());
+
+ assertEquals(1, originalBuf.refCnt());
+ assertEquals(1, result.refCnt());
+
+ // 在恰当的时机释放构建结果
+ result.release();
+ assertEquals(0, result.refCnt());
+ }
+
+ // try-with-resource 释放了 originalBuf
+ assertEquals(0, originalBuf.refCnt());
+ }
}