JAXB 内部实现接口的类加载器问题 com.sun.xml.bind.namespacePrefixMapper

Classloader problem with JAXB internal implementation interface com.sun.xml.bind.namespacePrefixMapper

我需要以下问题的帮助:我将 Websphere Liberty 19.0.0.9 与 Oracle 和 IBM Java 1.8 和 运行 包含 EJB 的旧应用程序 (EAR) 一起使用,它序列化 XML 与 JAXB。应用程序需要控制 XML 命名空间定义和前缀,并通过提供 com.sun.xml.bind.namespacePrefixMapper 到 javax.xml.bind.Marshaller.setProperty 和 属性 "com.sun.xml.bind.namespacePrefixMapper" 的实现来实现。 在 运行 时,错误 java.lang.NoClassDefFoundError: com/sun/xml/bind/marshaller/NamespacePrefixMapper 在加载实现 class 时发生。 server.xml 包含特性 javaee-8.0,以及自由的 JAXB 实现 wlp-19.0.0.9\lib\com.ibm.ws.jaxb.tools.2.2.10_1.0.32.jar 包含 class com.sun..xml.bind.marshaller.NamespacePrefixMapper.

我试图通过将 jaxb-impl-2.2.4.jar 放到 EAR/lib 来解决它(这是错误的方法,因为 JAXB 是由 JEE 提供的)但是随后在com.sun.xml.bind.v2.runtime.MarshallerImpl.setProperty(MarshallerImpl.java:511)因为检查if(!(value instanceof NamespacePrefixMapper))失败了,因为Classloader(AppClassLoader)的该实现为 class NamespacePrefixMapper 提供了另一个 class 对象,而不是 MarshallerImpls 的 classloader (org.eclipse.osgi.internal.loader.EquinoxClassLoader)。但这表明自由可以访问NamespacePrefixMapper。

我多次尝试使用相同的 classloader 来实现,并在加载它们时使用 MarschallerImpl,我试图通过 classloder 设置在 server.xml 中解决它.没有成功。 我知道不建议使用此类特定于 JAXB 实现的 classes,但应用程序是以这种方式开发的,无法轻易更改。

感谢任何帮助,告诉我如何说服 liberty 向应用程序 classloader 提供 NamespacePrefixMapper class,或者使用应用程序 classloaders NamespacePrefixMapper MarschallerImpl。 谢谢。

//The implementation class looks for example like this:
public class MyNamespacePrefixMapperImpl extends com.sun.xml.bind.marshaller.NamespacePrefixMapper {...}
JAXBContext c = JAXBContext.newInstance(some mapped class);
Marshaller m = c.createMarshaller();
com.sun.xml.bind.marshaller.NamespacePrefixMapper mapper = new MyNamespacePrefixMapperImpl();// Here the NoClassDefFoundError occurs.
m.setProperty("com.sun.xml.bind.namespacePrefixMapper", mapper); // Here the instanceof check fails if jaxb-impl.jar is in EAR/lib.

这是一个不稳定的情况,没有简单的解决办法。 Liberty 尝试 "hide" 内部包以避免用户想要与框架提供的实现版本略有不同的情况——这个问题最明显的例子是在传统的 WAS 中,用户想要使用不同版本的 Jakarta与 WAS 一起提供的 Commons Logging——这需要用户提供他们自己的,或者在一个独立的共享库中,或者使用其他父最后 classloading hacks 来使其工作。 Liberty 通过将内部实现与用户应用程序隔离开来避免了这些问题。

因此,当用户想要使用不同于 Liberty 提供的版本的第三方库时,这非常有效,但正如您所发现的,当您的遗留应用程序依赖于这些库时,它就不太适用了 hidden/isolated 第三方库。

最理想的解决方案是重构应用程序代码,以便不依赖于内部 JAXB classes - 具有更多 JAXB 专业知识的人可能能够帮助解决这个问题......但这听起来像是这可能不可行,因此另一种选择是创建用户功能。用户功能本质上是 Liberty 运行时的扩展 - 因此它可以访问用户应用程序无法访问的包。它还允许您为用户应用程序添加包作为 APIs - 因此您可以使用用户功能将 ​​com.sun.xml.bind.marshaller 添加为 public API - 然后您的用户应用程序可以自由扩展它。您还可以将 MyNamespacePrefixMapperImpl class 包含在您的用户功能中并在那里注册,以便它自动应用于您服务器中的所有应用程序。

您可以在此处找到有关用户功能的更多信息: https://www.ibm.com/support/knowledgecenter/en/SSEQTP_liberty/com.ibm.websphere.wlp.doc/ae/twlp_feat_example.html

安迪,希望这对你有所帮助