首页 > 解决方案 > JAXB内部实现接口com.sun.xml.bind.namespacePrefixMapper的Classloader问题

问题描述

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

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

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

感谢任何帮助告诉我如何说服自由向应用程序类加载器提供 NamespacePrefixMapper 类,或者在 MarschallerImpl 中使用应用程序类加载器 NamespacePrefixMapper。谢谢你。

//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.

标签: javaxmljaxbwebsphere-liberty

解决方案


这是一个不稳定的情况,没有简单的解决办法。Liberty 试图“隐藏”内部包,以避免用户希望实现的版本与框架提供的版本略有不同的情况——这个问题最明显的例子是在传统的 WAS 中,用户希望使用不同版本的 Jakarta Commons Logging比 WAS 附带的 - 这需要用户提供他们自己的,或者在一个隔离的共享库中,或者使用其他 parent-last 类加载黑客来使其工作。Liberty 通过将内部实现与用户应用程序隔离开来避免了这些问题。

因此,当用户想要使用与 Liberty 提供的不同版本的第三方库时,这很有效,但正如您所发现的,当您的旧应用程序依赖于那些隐藏/隔离的第三方库时,它的效果就不那么好了。

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

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

希望这会有所帮助,安迪


推荐阅读