Summary
JetLinks 2.10 / jetlinks-supports 1.3.1 的 ProtocolClassLoader
在 lazy 加载 protocol JAR 内部类时出现偶发 ClassNotFoundException,
影响命令下发 (encode) 路径。Decode 路径不受影响。
Environment
- JetLinks Community: 2.10.0-SNAPSHOT (image
registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-community:2.10.0-SNAPSHOT)
- jetlinks-supports: 1.3.1
- jetlinks-official-protocol: 3.2.0-SNAPSHOT (commit
4c1a036ee072c2639fd4e21f1b085b7c71de475e)
- JDK: Eclipse Temurin 17
- 复现频率: 服务首次启动后第 1 次下发命令必现;不重启情况下后续偶发
Steps to reproduce
- 部署 JetLinks 2.10 + 加载 jetlinks-official-protocol-3.2.0.jar
- 注册一个 metering-1ph product + 一个 device(如
meter-0001)
- 通过
/api/messages/{deviceId}/property/get 下发"读取属性"命令
- 观察 jetlinks 容器日志
Expected behavior
命令通过 protocol encode 后下发到 broker,设备返回响应或超时(超时是 Modbus 设备不订阅 JetLinks 下行 topic 的预期行为)。
Actual behavior
命令在 encode 阶段抛 NoClassDefFoundError,命令未下发:
java.lang.NoClassDefFoundError: org/jetlinks/protocol/official/TopicPayload
at org.jetlinks.protocol.official.TopicMessageCodec.doEncode(TopicMessageCodec.java:412)
at org.jetlinks.protocol.official.TopicMessageCodec.encode(TopicMessageCodec.java:374)
...
Caused by: java.lang.ClassNotFoundException: org.jetlinks.protocol.official.TopicPayload
at org.jetlinks.supports.protocol.management.jar.ProtocolClassLoader.loadClass(ProtocolClassLoader.java:57)
Verification: class is present in JAR
$ unzip -l jetlinks-official-protocol-3.2.0.jar | grep TopicPayload
4823 2026-05-02 ... org/jetlinks/protocol/official/TopicPayload.class
JAR 包含该类,但 ProtocolClassLoader.loadClass() 在 runtime 加载 JAR 内"类间引用"时找不到。
Root cause analysis (suspected)
ProtocolClassLoader 在初始化时只 register 了主 provider 类 (JetLinksProtocolSupportProvider)。当 codec 类被反射加载并触发 JVM 解析其依赖类时,class loader 的 findClass 路径未能识别 JAR 内未直接 register 的类。
Workaround (current SBOMP patch)
在 JetLinksProtocolSupportProvider 类的 static 初始化块中遍历 JAR 内所有 .class 文件,主动 Class.forName(..., false, classLoader) 预加载到 ProtocolClassLoader 的内部缓存。
详见 attached PR: jetlinks/jetlinks-official-protocol PR (link to be added after PR creation).
Suggested fix (上游)
更优方案应在 ProtocolClassLoader 内部修复:在 register 主 provider 类之后扫描 JAR 内所有同 package 的类并加入 cache(类似 SPI loader 行为)。
或者:把 ProtocolClassLoader 改为 child-first delegation 模型,且 findClass 失败时尝试从同 codeSource 加载。
Severity
High — 影响所有 JetLinks 2.10 部署的命令下发功能(不修则下发全部失败)。
Reproducer
完整复现脚本:github.com/LXingYun/SBOMP/tree/main/deploy/lite/jetlinks(含 patch 全文 + build-protocol.sh + 锁定 commit)
Summary
JetLinks 2.10 / jetlinks-supports 1.3.1 的
ProtocolClassLoader在 lazy 加载 protocol JAR 内部类时出现偶发
ClassNotFoundException,影响命令下发 (encode) 路径。Decode 路径不受影响。
Environment
registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-community:2.10.0-SNAPSHOT)4c1a036ee072c2639fd4e21f1b085b7c71de475e)Steps to reproduce
meter-0001)/api/messages/{deviceId}/property/get下发"读取属性"命令Expected behavior
命令通过 protocol encode 后下发到 broker,设备返回响应或超时(超时是 Modbus 设备不订阅 JetLinks 下行 topic 的预期行为)。
Actual behavior
命令在 encode 阶段抛
NoClassDefFoundError,命令未下发:Verification: class is present in JAR
$ unzip -l jetlinks-official-protocol-3.2.0.jar | grep TopicPayload 4823 2026-05-02 ... org/jetlinks/protocol/official/TopicPayload.classJAR 包含该类,但
ProtocolClassLoader.loadClass()在 runtime 加载 JAR 内"类间引用"时找不到。Root cause analysis (suspected)
ProtocolClassLoader在初始化时只 register 了主 provider 类 (JetLinksProtocolSupportProvider)。当 codec 类被反射加载并触发 JVM 解析其依赖类时,class loader 的 findClass 路径未能识别 JAR 内未直接 register 的类。Workaround (current SBOMP patch)
在
JetLinksProtocolSupportProvider类的 static 初始化块中遍历 JAR 内所有.class文件,主动Class.forName(..., false, classLoader)预加载到 ProtocolClassLoader 的内部缓存。详见 attached PR: jetlinks/jetlinks-official-protocol PR (link to be added after PR creation).
Suggested fix (上游)
更优方案应在
ProtocolClassLoader内部修复:在 register 主 provider 类之后扫描 JAR 内所有同 package 的类并加入 cache(类似 SPI loader 行为)。或者:把 ProtocolClassLoader 改为 child-first delegation 模型,且 findClass 失败时尝试从同 codeSource 加载。
Severity
High — 影响所有 JetLinks 2.10 部署的命令下发功能(不修则下发全部失败)。
Reproducer
完整复现脚本:github.com/LXingYun/SBOMP/tree/main/deploy/lite/jetlinks(含 patch 全文 + build-protocol.sh + 锁定 commit)