PAYATHON 2026

Java 25 下 Spring Boot 的 IBM ORB 初始化失败如何解决?

数据阿明

当前异常指向客户端的 ORB/RMI-IIOP 依赖与 Java 25 不兼容。要继续使用 Java 25,先确认是否有适配该运行环境且兼容目标服务器的客户端组合。修改 MessageListener 或增加模块开放参数,都无法补回缺失的类。

现有信息不足以确定哪个 IBM 客户端版本一定可用,也不足以认定 IBM 已全面取消 Java 25 上的 IIOP 支持。如果服务器确实只提供 IIOP,且没有兼容 Java 25 的客户端,就需要调整服务器的接入能力,或在允许独立使用旧版运行环境的前提下引入桥接服务。

异常发生在哪一层?

堆栈中的关键路径是:

InitialContext.lookup(...)
  -> com.ibm.ws.naming.util.Helpers.getOrb(...)
  -> IBM ORB 初始化
  -> javax.rmi.CORBA.Util 初始化失败
  -> NoClassDefFoundError: sun/security/action/GetPropertyAction

故障发生在获取 JNDI 对象时,早于 createTopicConnection()、订阅创建和消息回调。因此,调整 onMessage()、重连间隔或消息确认模式,都无法解决这次初始化失败。

Could not initialize class javax.rmi.CORBA.Util 表示该类的初始化已经失败,并不等于缺少 Util.class。应继续追踪首次初始化异常;这里最深层的线索是对 sun.security.action.GetPropertyAction 的依赖。

GetPropertyAction 属于 JDK 内部实现,OpenJDK 已有删除该类及其使用点的变更。依赖它的旧客户端需要适配,不能将它当作稳定的公共 API。OpenJDK 删除变更

Java 17 能运行,为什么 Java 25 会失败?

JDK 自带的 java.corba 模块早在 Java 11 就已移除,Java 17 同样不包含它。因此,问题不能解释为“Java 25 才移除了 CORBA”。Oracle JDK 迁移指南

结合堆栈中的 com.ibm.rmi.corba.*,原来 Java 17 环境中的相关能力应来自外部客户端依赖或运行环境。迁移时需要检查这套外部实现是否兼容 Java 25。Java 17 上曾经运行成功,也不能直接证明该组合获得了厂商支持。

--add-exports 和 --add-opens 用于处理模块访问限制,无法恢复已经不存在的类。如果遇到访问权限异常,可以另行分析;当前不能把开放 sun.security.action 当作修复方案。

先确认 IIOP 用于命名查询,还是消息传输

这份堆栈表明 JNDI lookup 触发了 ORB 初始化,但没有证明 JMS 消息本身也通过 IIOP 传输。

需要向服务器管理员确认:

  • NWConstants.NASDA_INITIAL_CONTEXT_FACTORY 的实际值是什么?
  • IIOP 是否仅用于查询 TopicConnectionFactory 和 Topic?
  • 实际 JMS provider 是 WebSphere Service Integration Bus,还是其他产品?
  • 是否允许直接配置 ConnectionFactory 和 Topic,并连接消息端点?

IBM 文档说明,WebSphere Thin Client for JMS 可以通过编程方式获取连接工厂和目的地,也可以使用 JNDI。如果 IIOP 只用于命名查询,可能存在绕开这一步的接入方式,但仍需核对客户端版本、服务器配置和 Java 支持范围。IBM JMS 资源使用说明

不要仅凭“thin client”的名称就认定它兼容 Java 25。IBM 分别列出了不同客户端的运行环境要求,具体支持范围须按所用产品和版本确认。IBM 客户端类型及支持环境

如何定位需要更新的依赖?

先记录实际启动进程使用的 Java 发行版和版本,以及 IBM naming、ORB、JMS 客户端 JAR 的名称和版本。还需要记录服务器版本、修复级别、Spring Boot 版本和完整启动参数。

可以在应用启动早期临时加入下面的诊断代码,查看关键类资源的来源。它只查找资源,不会主动初始化 javax.rmi.CORBA.Util:

import java.net.URL;
import java.util.Enumeration;

public final class ClientResourceDiagnostics {
    public static void printResources() throws Exception {
        System.out.println("java.version="
                + System.getProperty("java.version"));
        System.out.println("java.vendor="
                + System.getProperty("java.vendor"));

        ClassLoader loader = Thread.currentThread()
                .getContextClassLoader();

        if (loader == null) {
            loader = ClassLoader.getSystemClassLoader();
        }

        String[] resources = {
            "javax/rmi/CORBA/Util.class",
            "org/omg/CORBA/ORB.class",
            "com/ibm/rmi/corba/ORB.class",
            "com/ibm/ws/naming/util/WsnInitCtxFactory.class"
        };

        for (String resource : resources) {
            Enumeration<URL> urls = loader.getResources(resource);
            while (urls.hasMoreElements()) {
                System.out.println(resource + " -> "
                        + urls.nextElement());
            }
        }
    }
}

如果同一资源出现在多个位置,应检查是否存在重复依赖或版本混用。仅凭资源列表还不能确定最终加载了哪份类,必要时可结合类加载日志确认:

java -Xlog:class+load=info -jar application.jar

将 application.jar 替换为实际应用包名,并保留原有启动参数。根据加载来源,检查相关 JAR 中哪个实现直接或间接引用了 GetPropertyAction,再按厂商要求更新整套依赖。IBM 要求多个 stand-alone thin client 共用时,使用同一安装或同一 service refresh 的来源。IBM stand-alone thin clients 说明

更新依赖后,应重启 JVM 再验证,避免已失败的类初始化状态干扰判断。验证可以依次覆盖 JNDI lookup、创建连接、订阅 Topic、接收消息和断线恢复。

保留 Java 25 时有哪些可行路径?

使用兼容的客户端组合

如果厂商提供支持 Java 25 且兼容目标服务器的 naming、ORB 和 JMS 客户端,优先按其配置要求整体升级。当前没有这些组件的版本信息,因此不能给出可靠的 Maven 坐标,也不能保证某个修复包能解决问题。

也不要直接将 IBM ORB 替换为任意第三方 ORB。即使都实现 CORBA,命名服务、RMI-IIOP、认证和厂商扩展仍可能存在兼容要求,需要服务器支持依据和端到端验证。

绕开依赖 IIOP 的命名查询

如果服务器允许直接连接 JMS 消息端点,可以使用该 provider 支持的方式创建 ConnectionFactory 和 Topic,再接回现有订阅逻辑。具体类名、地址和参数必须来自实际 provider,不能从当前堆栈推导。

这条路径可以绕开命名查询依赖,但所用 JMS 客户端本身仍须兼容 Java 25。如果服务器确实没有其他接入方式,就不能仅靠客户端配置实现协议切换。

使用独立桥接服务

如果暂时没有兼容的客户端,又允许单独部署旧版 Java 运行环境,可以采用:

原服务器
  -> 兼容的 IIOP/JMS 接入进程
  -> 可持久化的消息通道
  -> Java 25 主应用

主应用可以继续运行在 Java 25,但桥接进程仍依赖旧运行环境。如果约束是所有进程都必须使用 Java 25,这个方案就不符合要求。

桥接还需要明确确认和重试策略。现有代码使用 AUTO_ACKNOWLEDGE;不能把消息放入内存队列后就认为转发已可靠完成,否则进程退出可能造成消息丢失。应根据实际 JMS provider 的事务能力和持久化方案设计,并处理重复投递。

原监听代码还有哪些注意事项?

NoClassDefFoundError 属于 Error,不会被 catch (Exception e) 直接捕获。不过,堆栈外层的 NamingException 可以被捕获。扩大捕获范围只能改变错误处理,不能修复客户端兼容性,也不应把这类错误作为普通网络故障持续重试。

代码直接使用了 JmsObjectMessageImpl,存在额外的厂商实现耦合。它出现在当前失败点之后,不是这次 ORB 初始化失败的直接原因,但后续升级客户端时仍需检查。

当前应先补齐客户端和服务器版本,确认 IIOP 的实际用途,再选择升级客户端或调整接入路径。在没有兼容实现、服务器又不可调整的前提下,不能保证这套原有客户端能够在 Java 25 进程内继续工作。

备注:内容仅供参考。