Skip to content

ProtocolClassLoader: lazy loading inner classes occasionally throws ClassNotFoundException at runtime (encode path) #38

Description

@LXingYun

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

  1. 部署 JetLinks 2.10 + 加载 jetlinks-official-protocol-3.2.0.jar
  2. 注册一个 metering-1ph product + 一个 device(如 meter-0001
  3. 通过 /api/messages/{deviceId}/property/get 下发"读取属性"命令
  4. 观察 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)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions