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()); + } }